Files
incremental-game-engine/src/types/index.ts
T

126 lines
3.5 KiB
TypeScript

import Decimal, { DecimalSource } from "break_eternity.js";
import { Behaviour } from "../engine/functions";
import { deserializeBehaviour } from "../engine/saveLoad";
export type Resource = 'wood' | 'plank' | 'stone';
export type Resources = Record<Resource, Decimal>;
export type Node = {
id: string;
name: string;
behaviour: Behaviour;
}
/** Orders behaviours so that whatever a behaviour's inputs link to comes before it (a dependency cycle just falls back to encounter order). */
function orderByDependency(behaviours: Behaviour[]): Behaviour[] {
const byId = new Map(behaviours.map((behaviour) => [behaviour.id, behaviour]));
const visited = new Set<string>();
const order: Behaviour[] = [];
function visit(behaviour: Behaviour) {
if (visited.has(behaviour.id))
return;
visited.add(behaviour.id);
for (const input of behaviour.inputs.pins) {
const dependency = byId.get(input.link.target);
if (dependency)
visit(dependency);
}
order.push(behaviour);
}
for (const behaviour of behaviours)
visit(behaviour);
return order;
}
export class Game {
public time: Decimal;
public readonly nodes: Node[];
constructor(source: SerializedGame) {
this.time = new Decimal(source.time);
this.nodes = source.nodes.map((node) => ({
id: node.id,
name: node.name,
behaviour: deserializeBehaviour(node.behaviour),
}));
}
public tick(deltaTime: number) {
this.time = this.time.add(deltaTime);
const behavioursById = new Map(this.nodes.map((node) => [node.behaviour.id, node.behaviour]));
const resolve = (id: string) => behavioursById.get(id);
// Producers must tick (and so fill their storage) before whatever pulls from them,
// so resources can flow all the way down a chain within a single tick.
for (const behaviour of orderByDependency(this.nodes.map((node) => node.behaviour)))
behaviour.tick(deltaTime, resolve);
}
public reset() {
this.time = Decimal.dZero;
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);
}
}
export type SerializedInput = {
resource: Resource;
link: {
target: SerializedNodeBehaviour['id'],
resource: Resource,
}
}
export type SerializedOutput = {
resource: Resource;
}
export type SerializedInputs = {
pins: SerializedInput[];
}
export type SerializedOutputs = {
pins: SerializedOutput[];
}
export type SerializedConverter = {
kind: 'converter';
cycleTime?: DecimalSource;
storage?: Partial<Record<Resource, DecimalSource>>;
rules: {
cycleLength: DecimalSource;
inputRates?: Partial<Record<Resource, DecimalSource>>;
outputRates?: Partial<Record<Resource, DecimalSource>>;
}
}
export type SerializedStorage = {
kind: 'storage',
}
export type SerializedNodeBehaviour = {
id: string;
inputs: SerializedInputs;
outputs: SerializedOutputs;
} & (SerializedConverter | SerializedStorage);
export type SerializedNode = {
id: Node['id'],
name: Node['name'],
behaviour: SerializedNodeBehaviour,
}
export type SerializedGame = {
time: DecimalSource;
nodes: SerializedNode[];
}