From d96a6e2e9238f46c589f3b37fb2b1bb2c3b2406e Mon Sep 17 00:00:00 2001 From: "azykov@mail.ru" Date: Mon, 31 Aug 2026 22:01:35 +0300 Subject: [PATCH] basic logic --- src/App.tsx | 14 +++----- src/components/NodeCard.tsx | 3 +- src/engine/functions/index.ts | 62 +++++++++++++++++++++++------------ src/engine/saveLoad.ts | 10 ++---- src/storage.ts | 47 -------------------------- src/types/index.ts | 18 +++++++++- 6 files changed, 66 insertions(+), 88 deletions(-) delete mode 100644 src/storage.ts diff --git a/src/App.tsx b/src/App.tsx index 80a4f28..5c35709 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,13 +15,8 @@ export default function App() { // useEffect(() => saveTickCount(tickCount), [tickCount]); function handleTick() { - game.tick(1.0 / 60); - // const nextTopology = cloneJSON(game); - // const nextBuffers = cloneJSON(buffers); - // tick(nextTopology, nextBuffers); - // setGame(nextTopology); - // setBuffers(nextBuffers); - // setTickCount((n) => n + 1); + game.tick(1.0 / 10); + setGame(game.clone()); } function handleReset() { @@ -31,8 +26,8 @@ export default function App() { if (!ok) return; - game.time = 0; - save(game); + game.reset(); + setGame(game.clone()); } function handleInitialize() { @@ -43,7 +38,6 @@ export default function App() { return; setGame(deserialize(defaultGame())); - save(game); } // function handleAddNode(kind: NodeKind) { diff --git a/src/components/NodeCard.tsx b/src/components/NodeCard.tsx index 351064e..481e3ac 100644 --- a/src/components/NodeCard.tsx +++ b/src/components/NodeCard.tsx @@ -59,7 +59,7 @@ const NodeCard = forwardRef(function NodeCard({ node2d, s
{node.behaviour.kind}
{ Object.entries(info.storages).map(([name, storage]) => ( -
+
{name}
{ Object.entries(storage) @@ -73,6 +73,7 @@ const NodeCard = forwardRef(function NodeCard({ node2d, s info.links.map((link) =>
→{link.target}/{link.resource}
) }
+
{JSON.stringify(info, undefined, 4)}
); }); diff --git a/src/engine/functions/index.ts b/src/engine/functions/index.ts index 61ce3de..88923f6 100644 --- a/src/engine/functions/index.ts +++ b/src/engine/functions/index.ts @@ -1,4 +1,4 @@ -import { Resource, Resources, SerializedConverter, SerializedNodeBehaviour as SerializedBehaviour, SerializedNodeBehaviour, SerializedConverterOutput, SerializedConverterInput } from "../../types"; +import { Resource, Resources, SerializedConverter, SerializedNodeBehaviour as SerializedBehaviour, SerializedNodeBehaviour } from "../../types"; export type BehaviourResourcePin = { resource: Resource; @@ -16,6 +16,7 @@ export type BehaviourLink = { } export type BehaviourInput = BehaviourResourcePin & { + rate: number; // per second of tick deltaTime link: BehaviourLink; } @@ -24,6 +25,7 @@ export type BehaviourInputs = { } export type BehaviourOutput = BehaviourResourcePin & { + rate: number; // per second of tick deltaTime } export type BehaviourOutputs = { @@ -41,51 +43,73 @@ export abstract class Behaviour { public readonly kind: string; public readonly inputs: BehaviourInputs; public readonly outputs: BehaviourOutputs; + protected readonly storage: Partial = {}; constructor(source: SerializedBehaviour) { this.id = source.id; this.kind = source.kind; this.outputs = { - pins: source.outputs.pins.map((input) => ({ - resource: input.resource, + pins: source.outputs.pins.map((output) => ({ + resource: output.resource, + rate: output.rate, })), } this.inputs = { pins: source.inputs.pins.map((input) => ({ resource: input.resource, + rate: input.rate, link: input.link, })), } } + public reset() { + for (const res of Object.keys(this.storage)) + delete (this.storage[res as Resource]); + } + abstract getDisplayInfo(): BehaviourDisplayInfo; - abstract tick(deltaTime: number): void; -} + /** Withdraws up to `amount` of `resource` from this behaviour's storage, returning how much was actually taken. */ + protected take(resource: Resource, amount: number): number { + const available = this.storage[resource] ?? 0; + const taken = Math.min(available, amount); + this.storage[resource] = available - taken; + return taken; + } -export type ResourceUsageRule = { - resource: Resource; - speed: number; // per second + protected give(resource: Resource, amount: number): void { + this.storage[resource] = (this.storage[resource] ?? 0) + amount; + } + + /** Fills this behaviour's own storage from its outputs' production rates. */ + public produce(deltaTime: number): void { + for (const output of this.outputs.pins) + this.give(output.resource, output.rate * deltaTime); + } + + /** Pulls resources into this behaviour's storage from whatever each input pin is linked to. */ + public pull(deltaTime: number, resolve: (id: string) => Behaviour | undefined): void { + for (const input of this.inputs.pins) { + const source = resolve(input.link.target); + if (!source) + continue; + const taken = source.take(input.link.resource, input.rate * deltaTime); + this.give(input.resource, taken); + } + } } export class Converter extends Behaviour { - public outputRules: ResourceUsageRule[] = []; - rules: { inputs: SerializedConverterInput[]; outputs: SerializedConverterOutput[]; }; - constructor(source: SerializedNodeBehaviour & SerializedConverter) { if (source.kind !== 'converter') throw new Error('Invalid source'); super(source); - - this.rules = { - inputs: source.inputs.pins, - outputs: source.outputs.pins, - }; } public getDisplayInfo(): BehaviourDisplayInfo { return { - storages: {}, + storages: { 'Буфер': { ...this.storage } as Resources }, links: this.inputs.pins.map((input) => input.link), sockets: [ ...this.inputs.pins.map((input, index) => ({ @@ -101,10 +125,6 @@ export class Converter extends Behaviour { ], } } - - tick(deltaTime: number): void { - - } } // export class Storage extends Behaviour { diff --git a/src/engine/saveLoad.ts b/src/engine/saveLoad.ts index 9f320b2..9a0639d 100644 --- a/src/engine/saveLoad.ts +++ b/src/engine/saveLoad.ts @@ -9,16 +9,10 @@ export function serializeBehaviour(behaviour: Behaviour): SerializedNodeBehaviou id: behaviour.id, kind: 'converter', inputs: { - pins: behaviour.inputs.pins.map((pin) => ({ - ...pin, - rate: 11, - })), + pins: behaviour.inputs.pins.map((pin) => ({ ...pin })), }, outputs: { - pins: behaviour.outputs.pins.map((pin) => ({ - ...pin, - rate: 11, - })), + pins: behaviour.outputs.pins.map((pin) => ({ ...pin })), }, }; default: diff --git a/src/storage.ts b/src/storage.ts deleted file mode 100644 index eb7129c..0000000 --- a/src/storage.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { Topology, Buffers } from './engine'; -import { defaultTopology, resetBuffers } from './engine'; - -const KEY_TOPOLOGY = 'incsim.topology.v1'; -const KEY_BUFFERS = 'incsim.buffers.v1'; -const KEY_TICKS = 'incsim.tickcount.v1'; - -export function loadTopology(): Topology { - try { - const raw = localStorage.getItem(KEY_TOPOLOGY); - if (raw) return JSON.parse(raw) as Topology; - } catch (e) { - console.warn('Не удалось прочитать игру из localStorage', e); - } - const fresh = defaultTopology(); - saveTopology(fresh); - return fresh; -} - -export function saveTopology(topology: Topology): void { - localStorage.setItem(KEY_TOPOLOGY, JSON.stringify(topology)); -} - -export function loadBuffers(topology: Topology): Buffers { - try { - const raw = localStorage.getItem(KEY_BUFFERS); - if (raw) return JSON.parse(raw) as Buffers; - } catch (e) { - console.warn('Не удалось прочитать буферы из localStorage', e); - } - const fresh = resetBuffers(topology); - saveBuffers(fresh); - return fresh; -} - -export function saveBuffers(buffers: Buffers): void { - localStorage.setItem(KEY_BUFFERS, JSON.stringify(buffers)); -} - -export function loadTickCount(): number { - const raw = localStorage.getItem(KEY_TICKS); - return raw ? Number(raw) || 0 : 0; -} - -export function saveTickCount(n: number): void { - localStorage.setItem(KEY_TICKS, String(n)); -} diff --git a/src/types/index.ts b/src/types/index.ts index 15f49f2..77a20eb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -26,8 +26,24 @@ export class Game { public tick(deltaTime: number) { this.time += deltaTime; + + const behavioursById = new Map(this.nodes.map((node) => [node.behaviour.id, node.behaviour])); + for (const node of this.nodes) - node.behaviour.tick(deltaTime); + node.behaviour.produce(deltaTime); + for (const node of this.nodes) + node.behaviour.pull(deltaTime, (id) => behavioursById.get(id)); + } + + public reset() { + this.time = 0; + for (const node of this.nodes) + node.behaviour.reset(); + } + + /** Shallow copy with a new identity, so React sees a change after in-place mutations like `tick`. */ + public clone(): Game { + return Object.assign(Object.create(Game.prototype), this); } }