full generation and upgrade rules revamp

This commit is contained in:
azykov@mail.ru
2026-02-14 13:37:17 +03:00
parent c9c9f03526
commit 52f75c74c0
19 changed files with 374 additions and 204 deletions
-42
View File
@@ -1,42 +0,0 @@
// #root {
// max-width: 1280px;
// margin: 0 auto;
// padding: 2rem;
// text-align: center;
// }
// .logo {
// height: 6em;
// padding: 1.5em;
// will-change: filter;
// transition: filter 300ms;
// }
// .logo:hover {
// filter: drop-shadow(0 0 2em #646cffaa);
// }
// .logo.react:hover {
// filter: drop-shadow(0 0 2em #61dafbaa);
// }
// @keyframes logo-spin {
// from {
// transform: rotate(0deg);
// }
// to {
// transform: rotate(360deg);
// }
// }
// @media (prefers-reduced-motion: no-preference) {
// a:nth-of-type(2) .logo {
// animation: logo-spin infinite 20s linear;
// }
// }
// .card {
// padding: 2em;
// }
// .read-the-docs {
// color: #888;
// }
+4 -12
View File
@@ -3,21 +3,16 @@ import { observer } from 'mobx-react-lite';
import { root } from './state/Root';
import { Page } from './Page';
import './App.scss'
import type { ChangeEvent } from 'react';
import { now } from './tools/time';
function now(): number {
return (performance.now() + performance.timeOrigin) / 1000;
}
const gameLoop = function () {
let lastTime = now();
function loop() {
const nowTime = now();
const deltaTime = nowTime - lastTime;
lastTime = now();
const deltaTime = nowTime - root.lastGameTime;
root.setLastGameTime(now());
// console.log('loop tick ' + deltaTime);
root.tick(deltaTime);
@@ -33,10 +28,6 @@ gameLoop();
export const App = observer(function () {
function handleTickClick(): void {
root.tick(1 / 60);
}
function handleNumberNotationChange(event: ChangeEvent<HTMLSelectElement, HTMLSelectElement>): void {
root.setNumberNotationName(event.target.value);
}
@@ -47,6 +38,7 @@ export const App = observer(function () {
return (
<>
<div>{root.lastGameTime}</div>
<div>Notation:&nbsp;
<select onChange={handleNumberNotationChange} value={root.numberNotationName}>
{root.availableNotationNames.map((n) => <option key={n} value={n}>{n}</option>)}
+12 -20
View File
@@ -1,35 +1,27 @@
import { type ResourceGenerator } from "@idle-economy/engine";
import { observer } from "mobx-react-lite";
import { root } from "./state/Root";
import { ResourcesView } from "./ResourcesView";
import { ResourceGeneratorView } from "./ResourceGeneratorView";
import { ProgressView } from "./ProgressView";
export const Page = observer(function () {
function handleGeneratorUpgradeClick(generator: ResourceGenerator): void {
generator.upgrade();
}
return (<>
<div>Resources: <ResourcesView resources={root.resources.toObject()} /></div>
<div>
<div className="generators">
{
root.generators.filter((gen) => gen.isVisible).map((gen) => (<button className={`generator ${gen.rule.name}`} key={gen.rule.name} disabled={!gen.isOpen} onClick={() => handleGeneratorUpgradeClick(gen)}>
<div className="name">{gen.rule.name}</div>
<div>LEVEL {gen.level}</div>
<div>+<ResourcesView resources={gen.generation.toObject()} /></div>
{/* <div>+<ResourcesView resources={gen.generation.toObject()} /> / {gen.period} sec</div> */}
<div><ProgressView period={gen.period} progress={gen.progress} /></div>
{
true
? <div>{gen.level} ⇒ {gen.level + 1} for <ResourcesView resources={gen.openPrice.toObject()} /></div>
: <></>
}
</button>))
root.generators
// .filter((gen) => gen.isVisible)
.map((gen) => <ResourceGeneratorView generator={gen} key={gen.rule.name} />)
}
</div>
<div className="progresses">
{
root.generators
// .filter((gen) => gen.isVisible)
.map((gen) => <ProgressView period={gen.getPeriod()} progress={gen.progress} name={gen.rule.name} />)
}
</div>
</>)
});
+3 -7
View File
@@ -1,6 +1,7 @@
import { observer } from "mobx-react-lite";
type Props = {
name?: string,
progress: number,
period: number,
}
@@ -10,15 +11,10 @@ export const ProgressView = observer(function (props: Props) {
<>
{/* <div>{formatNumber(props.progress)} / {formatNumber(props.period)}</div> */}
<div
className="progressBar"
className={`progressBar`}
style={{
backgroundColor: '#ffffff',
backgroundColor: props.name ? props.name : '#ffffff',
width: `${props.period === 0 ? 0 : props.progress / props.period * 100}%`,
fontSize: '0.1px',
margin: '8px 0px',
minHeight: '0.25rem',
borderRadius: '4px',
boxSizing: 'border-box',
}}
>&nbsp;</div>
</>
+52
View File
@@ -0,0 +1,52 @@
import { type ResourceGenerator } from "@idle-economy/engine";
import { observer } from "mobx-react-lite";
import { ResourcesView } from "./ResourcesView";
import { root } from "./state/Root";
type Props = {
generator: ResourceGenerator,
}
export const ResourceGeneratorView = observer(function (props: Props) {
let gen = props.generator;
let showName = gen.rule.name === 'prestige';
function handleGeneratorClick(): void {
if (props.generator.rule.manualGeneration)
props.generator.manualGenerate();
}
function handleGeneratorUpgradeClick(): void {
while (props.generator.isUpgradable)
props.generator.upgrade();
}
const upgradeLevelAvailable = gen.upgradableToLevel();
return (<>
<div
className={['generator', gen.rule.name].join(' ')}
data-lasttime={root.lastGameTime}
>
<div
className={['body', gen.rule.manualGeneration ? 'interactive' : ''].join(' ')}
onClick={handleGeneratorClick}
>
<div className={`name ${showName ? '' : 'hidden'}`}>{gen.rule.name}</div>
<div>LEVEL {gen.level}</div>
<div>+<ResourcesView resources={gen.getGain().toObject()} /> / {gen.getPeriod()} sec</div>
{/* <div><ProgressView period={gen.getPeriod()} progress={gen.progress} /></div> */}
{/* <div>{gen.isVisible ? 'visible' : 'not visible'}</div>
<div>{gen.isUpgradable ? 'upgradable' : 'not upgradable'}</div>
<div>{gen.isFull ? 'full' : 'not full'}</div> */}
</div>
<button onClick={handleGeneratorUpgradeClick} disabled={!gen.isUpgradable} className="upgrade">
<div>UPGRADE {gen.level} ⇒ {upgradeLevelAvailable}</div>
<div>at: <ResourcesView resources={gen.getUpgradePrice(upgradeLevelAvailable).toObject()} /></div>
<div>cost: </div>
<div>+<ResourcesView resources={gen.getGain(upgradeLevelAvailable).toObject()} /> / {gen.getPeriod(upgradeLevelAvailable)} sec</div>
</button>
</div>
</>)
});
+11 -11
View File
@@ -6,15 +6,15 @@ type Props = {
}
export const ResourcesView = function (props: Props) {
return (
<>
<span className="resources">
{
Object.entries(props.resources).map(
([res, value]) => <span key={res} style={{ color: res }}>{formatNumber(value)}</span>
)
}
</span>
</>
);
const list = Object.entries(props.resources);
return (<>
<span className="resources">
{
list.length
? list.map(([res, value]) => <span key={res} style={{ color: res }}>{res} {formatNumber(value)}</span>)
: 'none'
}
</span>
</>);
};
+76 -9
View File
@@ -14,6 +14,14 @@
-moz-osx-font-smoothing: grayscale;
}
* {
-webkit-user-select: none;
-webkit-touch-callout: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/*
a {
font-weight: 500;
@@ -69,24 +77,68 @@ button:focus-visible {
}
} */
.generator {
margin: 1rem;
padding: 0.5rem 1rem;
background-color: #1a1a1a;
border: 1px solid transparent;
button,
.interactive {
cursor: pointer;
}
.progresses {
& .progressBar {
min-height: 2rem;
}
}
.progressBar {
background-color: white;
font-size: 0.1px;
margin: 8px 0px;
min-height: 0.25rem;
border-radius: 4px;
box-sizing: border-box;
}
.generator {
margin: 1em;
padding: 0.5em;
background-color: #ffffff40;
border: 1px solid #ffffff60;
color: white;
border-radius: 4px;
text-align: center;
&.interactive {
&:hover {
box-shadow: 0px 0px 8px #ffffff40;
}
}
& .name {
display: none;
font-size: bold;
&.hidden {
display: none;
}
}
&:disabled {
opacity: 0.5;
}
&:hover {
box-shadow: 0px 0px 8px #ffffff40;
// border-width: 1px;
& .upgrade {
padding: 0.5em;
background: transparent;
border: 1px solid white;
border-radius: 4px;
color: white;
&:disabled {
opacity: 0.5;
}
&:hover {
box-shadow: 0px 0px 8px #ffffff40;
}
}
&.red {
@@ -123,4 +175,19 @@ button:focus-visible {
background-color: #ff00ff30;
border-color: #ff00ff80;
}
}
.generators {
display: flex;
flex-wrap: wrap;
}
.resources>span {
// min-width: 6em;
display: inline-block;
// border: 1px solid red;
&+span {
margin-left: 1em;
}
}
+7
View File
@@ -1,6 +1,7 @@
import { makeGame, ResourceGenerator, ResourceSet } from "@idle-economy/engine";
import { makeAutoObservable } from "mobx";
import { Notation, Presets } from 'eternal_notations';
import { now } from "../tools/time";
export class Root {
@@ -9,6 +10,8 @@ export class Root {
public resources: ResourceSet = new ResourceSet();
public generators: ResourceGenerator[] = [];
public lastGameTime = now();
public readonly availableNotationNames: string[] = Object.keys(Presets).filter((n: any) => !!(Presets as any)[n].name).sort();
public numberNotationName: keyof typeof Presets = 'Standard';
public numberNotation: Notation = Presets.Standard;
@@ -19,6 +22,10 @@ export class Root {
makeAutoObservable(this);
}
public setLastGameTime(value: number) {
this.lastGameTime = value;
}
public tick(deltaTime: number): void {
Array.from(this.game.tick(deltaTime));
+2 -2
View File
@@ -1,6 +1,6 @@
import type Decimal from 'break_eternity.js';
import { root } from '../state/Root';
export function formatNumber(value: Decimal): string {
return root.numberNotation.format(value);
export function formatNumber(value: Decimal | undefined): string {
return root.numberNotation.format(value ?? 0);
}
+3
View File
@@ -0,0 +1,3 @@
export function now(): number {
return (performance.now() + performance.timeOrigin) / 1000;
}