@jgengine/core 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +191 -2
- package/dist/ai/crowd.d.ts +36 -0
- package/dist/ai/crowd.js +213 -0
- package/dist/ai/jobBoard.d.ts +49 -0
- package/dist/ai/jobBoard.js +155 -0
- package/dist/ai/spawnDirector.d.ts +56 -0
- package/dist/ai/spawnDirector.js +150 -0
- package/dist/ai/threat.d.ts +25 -0
- package/dist/ai/threat.js +67 -0
- package/dist/audio/audioFalloff.d.ts +38 -0
- package/dist/audio/audioFalloff.js +32 -0
- package/dist/board/laneBoard.d.ts +63 -0
- package/dist/board/laneBoard.js +114 -0
- package/dist/board/timelineBoard.d.ts +36 -0
- package/dist/board/timelineBoard.js +70 -0
- package/dist/cards/cardPile.d.ts +63 -0
- package/dist/cards/cardPile.js +166 -0
- package/dist/cards/modifierPipeline.d.ts +33 -0
- package/dist/cards/modifierPipeline.js +48 -0
- package/dist/combat/abilityKit.d.ts +42 -0
- package/dist/combat/abilityKit.js +129 -0
- package/dist/combat/animationState.d.ts +47 -0
- package/dist/combat/animationState.js +101 -0
- package/dist/combat/attackTags.d.ts +14 -0
- package/dist/combat/attackTags.js +26 -0
- package/dist/combat/breakMeters.d.ts +32 -0
- package/dist/combat/breakMeters.js +51 -0
- package/dist/combat/comboString.d.ts +35 -0
- package/dist/combat/comboString.js +59 -0
- package/dist/combat/death.d.ts +8 -0
- package/dist/combat/death.js +5 -2
- package/dist/combat/defensiveWindow.d.ts +34 -0
- package/dist/combat/defensiveWindow.js +66 -0
- package/dist/combat/downed.d.ts +35 -0
- package/dist/combat/downed.js +104 -0
- package/dist/combat/effects.d.ts +11 -0
- package/dist/combat/effects.js +22 -13
- package/dist/combat/hitReaction.d.ts +23 -0
- package/dist/combat/hitReaction.js +20 -0
- package/dist/combat/resistance.d.ts +13 -0
- package/dist/combat/resistance.js +46 -0
- package/dist/combat/telegraph.d.ts +27 -0
- package/dist/combat/telegraph.js +37 -0
- package/dist/crafting/crop.d.ts +59 -0
- package/dist/crafting/crop.js +181 -0
- package/dist/crafting/production.d.ts +64 -0
- package/dist/crafting/production.js +130 -0
- package/dist/crafting/recipe.d.ts +62 -0
- package/dist/crafting/recipe.js +101 -0
- package/dist/economy/sharedWallet.d.ts +28 -0
- package/dist/economy/sharedWallet.js +62 -0
- package/dist/economy/techTree.d.ts +40 -0
- package/dist/economy/techTree.js +66 -0
- package/dist/game/cosmetics.d.ts +25 -0
- package/dist/game/cosmetics.js +51 -0
- package/dist/game/defineGame.d.ts +5 -0
- package/dist/game/events.d.ts +62 -0
- package/dist/game/lootFilter.d.ts +33 -0
- package/dist/game/lootFilter.js +48 -0
- package/dist/game/ping.d.ts +67 -0
- package/dist/game/ping.js +86 -0
- package/dist/game/playableGame.d.ts +291 -5
- package/dist/game/race.d.ts +95 -0
- package/dist/game/race.js +216 -0
- package/dist/game/runDraft.d.ts +34 -0
- package/dist/game/runDraft.js +97 -0
- package/dist/game/social.d.ts +27 -0
- package/dist/game/social.js +16 -0
- package/dist/game/worldItem.d.ts +92 -0
- package/dist/game/worldItem.js +132 -0
- package/dist/input/axisInput.d.ts +43 -0
- package/dist/input/axisInput.js +70 -0
- package/dist/input/pointer.d.ts +24 -0
- package/dist/input/pointer.js +17 -0
- package/dist/interaction/contextMenu.d.ts +30 -0
- package/dist/interaction/contextMenu.js +22 -0
- package/dist/interaction/qte.d.ts +20 -0
- package/dist/interaction/qte.js +24 -0
- package/dist/interaction/skillCheck.d.ts +20 -0
- package/dist/interaction/skillCheck.js +26 -0
- package/dist/inventory/shapedGrid.d.ts +56 -0
- package/dist/inventory/shapedGrid.js +176 -0
- package/dist/inventory/storageTier.d.ts +52 -0
- package/dist/inventory/storageTier.js +77 -0
- package/dist/item/affix.d.ts +54 -0
- package/dist/item/affix.js +133 -0
- package/dist/item/durability.d.ts +51 -0
- package/dist/item/durability.js +78 -0
- package/dist/item/modularItem.d.ts +46 -0
- package/dist/item/modularItem.js +75 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +53 -1
- package/dist/movement/dash.d.ts +37 -0
- package/dist/movement/dash.js +65 -0
- package/dist/multiplayer/combatSnapshot.d.ts +45 -0
- package/dist/multiplayer/combatSnapshot.js +89 -0
- package/dist/multiplayer/lagCompensation.d.ts +43 -0
- package/dist/multiplayer/lagCompensation.js +154 -0
- package/dist/multiplayer/matchmaking.d.ts +33 -0
- package/dist/multiplayer/matchmaking.js +60 -0
- package/dist/multiplayer/simultaneousCommit.d.ts +34 -0
- package/dist/multiplayer/simultaneousCommit.js +53 -0
- package/dist/nav/navGrid.d.ts +46 -0
- package/dist/nav/navGrid.js +248 -0
- package/dist/nav/pathFollow.d.ts +29 -0
- package/dist/nav/pathFollow.js +67 -0
- package/dist/physics/buoyancy.d.ts +49 -0
- package/dist/physics/buoyancy.js +112 -0
- package/dist/physics/carryable.d.ts +46 -0
- package/dist/physics/carryable.js +110 -0
- package/dist/physics/damageZones.d.ts +56 -0
- package/dist/physics/damageZones.js +107 -0
- package/dist/physics/forceVolume.d.ts +48 -0
- package/dist/physics/forceVolume.js +119 -0
- package/dist/physics/physicsWorld.d.ts +219 -0
- package/dist/physics/physicsWorld.js +749 -0
- package/dist/physics/ragdoll.d.ts +49 -0
- package/dist/physics/ragdoll.js +103 -0
- package/dist/physics/spatialGrid.d.ts +41 -0
- package/dist/physics/spatialGrid.js +138 -0
- package/dist/physics/structure.d.ts +83 -0
- package/dist/physics/structure.js +0 -0
- package/dist/physics/traversal.d.ts +75 -0
- package/dist/physics/traversal.js +138 -0
- package/dist/physics/vehicleBody.d.ts +93 -0
- package/dist/physics/vehicleBody.js +234 -0
- package/dist/runtime/gameContext.d.ts +66 -2
- package/dist/runtime/gameContext.js +157 -4
- package/dist/runtime/hostPersistence.d.ts +18 -0
- package/dist/runtime/hostPersistence.js +4 -0
- package/dist/runtime/persistenceScope.d.ts +32 -0
- package/dist/runtime/persistenceScope.js +64 -0
- package/dist/scene/assetCatalog.d.ts +13 -0
- package/dist/scene/autoTarget.d.ts +15 -0
- package/dist/scene/autoTarget.js +54 -0
- package/dist/scene/behaviors.d.ts +13 -1
- package/dist/scene/behaviors.js +3 -0
- package/dist/scene/captureCheck.d.ts +7 -0
- package/dist/scene/captureCheck.js +11 -0
- package/dist/scene/entityStore.d.ts +7 -1
- package/dist/scene/entityStore.js +15 -1
- package/dist/scene/form.d.ts +40 -0
- package/dist/scene/form.js +56 -0
- package/dist/scene/mount.d.ts +62 -0
- package/dist/scene/mount.js +107 -0
- package/dist/scene/possession.d.ts +30 -0
- package/dist/scene/possession.js +51 -0
- package/dist/scene/roster.d.ts +25 -0
- package/dist/scene/roster.js +57 -0
- package/dist/scene/selection.d.ts +30 -0
- package/dist/scene/selection.js +38 -0
- package/dist/scene/stationClaim.d.ts +43 -0
- package/dist/scene/stationClaim.js +109 -0
- package/dist/sensor/frustumSensor.d.ts +60 -0
- package/dist/sensor/frustumSensor.js +104 -0
- package/dist/sensor/hiddenStateProbe.d.ts +31 -0
- package/dist/sensor/hiddenStateProbe.js +40 -0
- package/dist/sensor/recordingBuffer.d.ts +29 -0
- package/dist/sensor/recordingBuffer.js +61 -0
- package/dist/sensor/revealQuery.d.ts +22 -0
- package/dist/sensor/revealQuery.js +36 -0
- package/dist/session/contestedChannel.d.ts +36 -0
- package/dist/session/contestedChannel.js +100 -0
- package/dist/session/extraction.d.ts +54 -0
- package/dist/session/extraction.js +108 -0
- package/dist/session/ring.d.ts +36 -0
- package/dist/session/ring.js +88 -0
- package/dist/session/roundState.d.ts +55 -0
- package/dist/session/roundState.js +129 -0
- package/dist/stats/accumulatorMeter.d.ts +31 -0
- package/dist/stats/accumulatorMeter.js +81 -0
- package/dist/stats/eventMeter.d.ts +34 -0
- package/dist/stats/eventMeter.js +76 -0
- package/dist/stats/rollCheck.d.ts +15 -0
- package/dist/stats/rollCheck.js +15 -0
- package/dist/survival/decayMeter.d.ts +59 -0
- package/dist/survival/decayMeter.js +95 -0
- package/dist/survival/moodle.d.ts +48 -0
- package/dist/survival/moodle.js +91 -0
- package/dist/survival/regionHealth.d.ts +72 -0
- package/dist/survival/regionHealth.js +129 -0
- package/dist/tactics/predictiveQuery.d.ts +34 -0
- package/dist/tactics/predictiveQuery.js +44 -0
- package/dist/tactics/snapshot.d.ts +18 -0
- package/dist/tactics/snapshot.js +65 -0
- package/dist/tactics/surface.d.ts +52 -0
- package/dist/tactics/surface.js +135 -0
- package/dist/tactics/tacticalGrid.d.ts +57 -0
- package/dist/tactics/tacticalGrid.js +244 -0
- package/dist/time/beatClock.d.ts +40 -0
- package/dist/time/beatClock.js +73 -0
- package/dist/time/simClock.d.ts +62 -0
- package/dist/time/simClock.js +132 -0
- package/dist/turn/commit.d.ts +27 -0
- package/dist/turn/commit.js +72 -0
- package/dist/turn/turnLoop.d.ts +58 -0
- package/dist/turn/turnLoop.js +216 -0
- package/dist/world/buildPermissions.d.ts +47 -0
- package/dist/world/buildPermissions.js +134 -0
- package/dist/world/buildingIndex.d.ts +16 -0
- package/dist/world/buildingIndex.js +63 -0
- package/dist/world/buildings.d.ts +98 -0
- package/dist/world/buildings.js +320 -0
- package/dist/world/carve.d.ts +105 -0
- package/dist/world/carve.js +175 -0
- package/dist/world/connectors.d.ts +40 -0
- package/dist/world/connectors.js +67 -0
- package/dist/world/envField.d.ts +71 -0
- package/dist/world/envField.js +96 -0
- package/dist/world/features.d.ts +105 -1
- package/dist/world/features.js +95 -0
- package/dist/world/fog.d.ts +40 -0
- package/dist/world/fog.js +137 -0
- package/dist/world/markers.d.ts +52 -0
- package/dist/world/markers.js +96 -0
- package/dist/world/minimap.d.ts +42 -0
- package/dist/world/minimap.js +72 -0
- package/dist/world/placedStructureStore.d.ts +39 -0
- package/dist/world/placedStructureStore.js +95 -0
- package/dist/world/placementController.d.ts +48 -0
- package/dist/world/placementController.js +97 -0
- package/dist/world/realm.d.ts +61 -0
- package/dist/world/realm.js +66 -0
- package/dist/world/regions.d.ts +66 -0
- package/dist/world/regions.js +158 -0
- package/dist/world/scatter.d.ts +24 -0
- package/dist/world/scatter.js +126 -0
- package/dist/world/scatterItems.d.ts +38 -0
- package/dist/world/scatterItems.js +85 -0
- package/dist/world/support.d.ts +27 -0
- package/dist/world/support.js +75 -0
- package/dist/world/terraform.d.ts +54 -0
- package/dist/world/terraform.js +186 -0
- package/dist/world/terrain.d.ts +54 -0
- package/dist/world/terrain.js +181 -0
- package/dist/world/walls.d.ts +61 -0
- package/dist/world/walls.js +221 -0
- package/dist/world/water.d.ts +27 -0
- package/dist/world/water.js +125 -0
- package/dist/world/weather.d.ts +92 -0
- package/dist/world/weather.js +169 -0
- package/dist/world/wind.d.ts +17 -0
- package/dist/world/wind.js +40 -0
- package/package.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export function createDownedState(config) {
|
|
2
|
+
const bleedoutSeconds = Math.max(0, config.bleedoutSeconds);
|
|
3
|
+
const reviveSeconds = config.reviveSeconds !== undefined ? Math.max(0, config.reviveSeconds) : 0;
|
|
4
|
+
const reviveHealthFraction = config.reviveHealthFraction ?? 1;
|
|
5
|
+
const bannerSeconds = config.banner?.expireSeconds;
|
|
6
|
+
const entries = new Map();
|
|
7
|
+
function toDead(instanceId, entry, reason, events) {
|
|
8
|
+
entry.phase = "dead";
|
|
9
|
+
entry.bleedoutRemaining = 0;
|
|
10
|
+
entry.reviveProgress = 0;
|
|
11
|
+
events.push({ kind: "died", instanceId, reason });
|
|
12
|
+
if (bannerSeconds !== undefined) {
|
|
13
|
+
entry.bannerRemaining = bannerSeconds;
|
|
14
|
+
events.push({ kind: "banner.created", instanceId });
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
entry.bannerRemaining = null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
down(instanceId) {
|
|
22
|
+
const existing = entries.get(instanceId);
|
|
23
|
+
if (existing !== undefined && existing.phase === "downed")
|
|
24
|
+
return null;
|
|
25
|
+
entries.set(instanceId, {
|
|
26
|
+
phase: "downed",
|
|
27
|
+
bleedoutRemaining: bleedoutSeconds,
|
|
28
|
+
reviveProgress: 0,
|
|
29
|
+
bannerRemaining: null,
|
|
30
|
+
});
|
|
31
|
+
return { kind: "downed", instanceId };
|
|
32
|
+
},
|
|
33
|
+
revive(instanceId, dt) {
|
|
34
|
+
const entry = entries.get(instanceId);
|
|
35
|
+
if (entry === undefined || entry.phase !== "downed" || dt <= 0)
|
|
36
|
+
return null;
|
|
37
|
+
entry.reviveProgress += dt;
|
|
38
|
+
if (entry.reviveProgress >= reviveSeconds) {
|
|
39
|
+
entry.phase = "alive";
|
|
40
|
+
entry.reviveProgress = 0;
|
|
41
|
+
entry.bleedoutRemaining = 0;
|
|
42
|
+
return { kind: "revived", instanceId, reviveHealthFraction };
|
|
43
|
+
}
|
|
44
|
+
return { kind: "reviving", instanceId };
|
|
45
|
+
},
|
|
46
|
+
interruptRevive(instanceId) {
|
|
47
|
+
const entry = entries.get(instanceId);
|
|
48
|
+
if (entry !== undefined && entry.phase === "downed")
|
|
49
|
+
entry.reviveProgress = 0;
|
|
50
|
+
},
|
|
51
|
+
finish(instanceId, reason) {
|
|
52
|
+
const entry = entries.get(instanceId);
|
|
53
|
+
if (entry === undefined || entry.phase !== "downed")
|
|
54
|
+
return null;
|
|
55
|
+
const events = [];
|
|
56
|
+
toDead(instanceId, entry, reason ?? "finished", events);
|
|
57
|
+
return events[0] ?? null;
|
|
58
|
+
},
|
|
59
|
+
tick(dt) {
|
|
60
|
+
if (dt <= 0)
|
|
61
|
+
return [];
|
|
62
|
+
const events = [];
|
|
63
|
+
for (const [instanceId, entry] of entries) {
|
|
64
|
+
if (entry.phase === "downed") {
|
|
65
|
+
entry.bleedoutRemaining -= dt;
|
|
66
|
+
if (entry.bleedoutRemaining <= 0)
|
|
67
|
+
toDead(instanceId, entry, "bleedout", events);
|
|
68
|
+
}
|
|
69
|
+
else if (entry.phase === "dead" && entry.bannerRemaining !== null) {
|
|
70
|
+
entry.bannerRemaining -= dt;
|
|
71
|
+
if (entry.bannerRemaining <= 0) {
|
|
72
|
+
entry.bannerRemaining = null;
|
|
73
|
+
events.push({ kind: "banner.expired", instanceId });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return events;
|
|
78
|
+
},
|
|
79
|
+
respawnFromBanner(instanceId) {
|
|
80
|
+
const entry = entries.get(instanceId);
|
|
81
|
+
if (entry === undefined || entry.phase !== "dead" || entry.bannerRemaining === null)
|
|
82
|
+
return null;
|
|
83
|
+
entry.phase = "alive";
|
|
84
|
+
entry.bannerRemaining = null;
|
|
85
|
+
entry.bleedoutRemaining = 0;
|
|
86
|
+
entry.reviveProgress = 0;
|
|
87
|
+
return { kind: "respawned", instanceId, reviveHealthFraction };
|
|
88
|
+
},
|
|
89
|
+
phase: (instanceId) => entries.get(instanceId)?.phase ?? "alive",
|
|
90
|
+
get: (instanceId) => {
|
|
91
|
+
const entry = entries.get(instanceId);
|
|
92
|
+
return entry === undefined ? null : { ...entry };
|
|
93
|
+
},
|
|
94
|
+
clear: (instanceId) => {
|
|
95
|
+
entries.delete(instanceId);
|
|
96
|
+
},
|
|
97
|
+
snapshot: () => {
|
|
98
|
+
const out = {};
|
|
99
|
+
for (const [instanceId, entry] of entries)
|
|
100
|
+
out[instanceId] = { ...entry };
|
|
101
|
+
return out;
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
package/dist/combat/effects.d.ts
CHANGED
|
@@ -45,6 +45,17 @@ export interface CombatSpatialDeps {
|
|
|
45
45
|
hasLineOfSight(from: EntityPosition | string, to: string): boolean;
|
|
46
46
|
positionOf(instanceId: string): EntityPosition | undefined;
|
|
47
47
|
}
|
|
48
|
+
export interface AreaTargetInput {
|
|
49
|
+
at: EntityPosition;
|
|
50
|
+
radius: number;
|
|
51
|
+
falloff?: "linear" | "none";
|
|
52
|
+
los?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface AreaTarget {
|
|
55
|
+
instanceId: string;
|
|
56
|
+
scale: number;
|
|
57
|
+
}
|
|
58
|
+
export declare function resolveAreaTargets(spatial: CombatSpatialDeps, input: AreaTargetInput, accept?: (instanceId: string) => boolean): AreaTarget[];
|
|
48
59
|
export interface EffectSystemDeps {
|
|
49
60
|
resolveReceive(instanceId: string): ReceiveMap | null | undefined;
|
|
50
61
|
resolveStats(instanceId: string): StatValueMap | undefined;
|
package/dist/combat/effects.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import { applyPoolDelta } from "../scene/entityStats.js";
|
|
2
2
|
import { distanceBetween } from "../scene/spatial.js";
|
|
3
|
+
export function resolveAreaTargets(spatial, input, accept) {
|
|
4
|
+
const falloff = input.falloff ?? "none";
|
|
5
|
+
const los = input.los ?? true;
|
|
6
|
+
const targets = [];
|
|
7
|
+
for (const instanceId of spatial.inRadius(input.at, input.radius)) {
|
|
8
|
+
if (los && !spatial.hasLineOfSight(input.at, instanceId))
|
|
9
|
+
continue;
|
|
10
|
+
let scale = 1;
|
|
11
|
+
if (falloff === "linear") {
|
|
12
|
+
const position = spatial.positionOf(instanceId);
|
|
13
|
+
if (position === undefined)
|
|
14
|
+
continue;
|
|
15
|
+
scale = Math.max(0, 1 - distanceBetween(input.at, position) / input.radius);
|
|
16
|
+
}
|
|
17
|
+
if (accept !== undefined && !accept(instanceId))
|
|
18
|
+
continue;
|
|
19
|
+
targets.push({ instanceId, scale });
|
|
20
|
+
}
|
|
21
|
+
return targets;
|
|
22
|
+
}
|
|
3
23
|
const DEFAULT_DRAIN_STAT = "damage";
|
|
4
24
|
export function createEffectSystem(deps) {
|
|
5
25
|
function resolveRule(instanceId, effect) {
|
|
@@ -85,20 +105,9 @@ export function createEffectSystem(deps) {
|
|
|
85
105
|
const result = applyTo(input.to, input.effect, input.via, input.from, 1);
|
|
86
106
|
return result === null ? [] : [result];
|
|
87
107
|
}
|
|
88
|
-
const falloff = input.falloff ?? "none";
|
|
89
|
-
const los = input.los ?? true;
|
|
90
108
|
const results = [];
|
|
91
|
-
for (const
|
|
92
|
-
|
|
93
|
-
continue;
|
|
94
|
-
let scale = 1;
|
|
95
|
-
if (falloff === "linear") {
|
|
96
|
-
const position = deps.spatial.positionOf(instanceId);
|
|
97
|
-
if (position === undefined)
|
|
98
|
-
continue;
|
|
99
|
-
scale = Math.max(0, 1 - distanceBetween(input.at, position) / input.radius);
|
|
100
|
-
}
|
|
101
|
-
const result = applyTo(instanceId, input.effect, input.via, input.from, scale);
|
|
109
|
+
for (const target of resolveAreaTargets(deps.spatial, input)) {
|
|
110
|
+
const result = applyTo(target.instanceId, input.effect, input.via, input.from, target.scale);
|
|
102
111
|
if (result !== null)
|
|
103
112
|
results.push(result);
|
|
104
113
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { EntityPosition } from "../scene/entityStore.js";
|
|
2
|
+
export interface CameraShake {
|
|
3
|
+
amplitude: number;
|
|
4
|
+
decay: number;
|
|
5
|
+
}
|
|
6
|
+
export interface HitReactionConfig {
|
|
7
|
+
hitstopMs: number;
|
|
8
|
+
knockback: number;
|
|
9
|
+
vertical?: number;
|
|
10
|
+
shake?: CameraShake;
|
|
11
|
+
}
|
|
12
|
+
export interface HitReactionInput {
|
|
13
|
+
attackerPos: EntityPosition;
|
|
14
|
+
targetPos: EntityPosition;
|
|
15
|
+
power?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface HitReaction {
|
|
18
|
+
hitstopMs: number;
|
|
19
|
+
impulse: [number, number, number];
|
|
20
|
+
shake: CameraShake | null;
|
|
21
|
+
}
|
|
22
|
+
export declare function resolveHitReaction(config: HitReactionConfig, input: HitReactionInput): HitReaction;
|
|
23
|
+
export declare function applyImpulse(position: EntityPosition, impulse: [number, number, number]): EntityPosition;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function resolveHitReaction(config, input) {
|
|
2
|
+
const power = input.power ?? 1;
|
|
3
|
+
const dx = input.targetPos[0] - input.attackerPos[0];
|
|
4
|
+
const dz = input.targetPos[2] - input.attackerPos[2];
|
|
5
|
+
const planar = Math.hypot(dx, dz);
|
|
6
|
+
const nx = planar === 0 ? 0 : dx / planar;
|
|
7
|
+
const nz = planar === 0 ? 0 : dz / planar;
|
|
8
|
+
const magnitude = config.knockback * power;
|
|
9
|
+
const shake = config.shake === undefined
|
|
10
|
+
? null
|
|
11
|
+
: { amplitude: config.shake.amplitude * power, decay: config.shake.decay };
|
|
12
|
+
return {
|
|
13
|
+
hitstopMs: config.hitstopMs,
|
|
14
|
+
impulse: [nx * magnitude, (config.vertical ?? 0) * power, nz * magnitude],
|
|
15
|
+
shake,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function applyImpulse(position, impulse) {
|
|
19
|
+
return [position[0] + impulse[0], position[1] + impulse[1], position[2] + impulse[2]];
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ResistVerdict = "immune" | "resist" | "normal" | "vulnerable";
|
|
2
|
+
export interface ResistanceMatrix {
|
|
3
|
+
categories: Record<string, Record<string, ResistVerdict>>;
|
|
4
|
+
multipliers?: Partial<Record<ResistVerdict, number>>;
|
|
5
|
+
default?: ResistVerdict;
|
|
6
|
+
}
|
|
7
|
+
export interface ResistanceResult {
|
|
8
|
+
verdict: ResistVerdict;
|
|
9
|
+
multiplier: number;
|
|
10
|
+
immune: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveResistance(matrix: ResistanceMatrix, category: string, targetProperties: readonly string[]): ResistanceResult;
|
|
13
|
+
export declare function resistanceScale(matrix: ResistanceMatrix, category: string, targetProperties: readonly string[]): number;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const DEFAULT_MULTIPLIERS = {
|
|
2
|
+
immune: 0,
|
|
3
|
+
resist: 0.5,
|
|
4
|
+
normal: 1,
|
|
5
|
+
vulnerable: 2,
|
|
6
|
+
};
|
|
7
|
+
function multiplierFor(matrix, verdict) {
|
|
8
|
+
return matrix.multipliers?.[verdict] ?? DEFAULT_MULTIPLIERS[verdict];
|
|
9
|
+
}
|
|
10
|
+
function summaryVerdict(matrix, immune, net) {
|
|
11
|
+
if (immune)
|
|
12
|
+
return "immune";
|
|
13
|
+
const normal = multiplierFor(matrix, "normal");
|
|
14
|
+
if (net > normal)
|
|
15
|
+
return "vulnerable";
|
|
16
|
+
if (net < normal)
|
|
17
|
+
return "resist";
|
|
18
|
+
return "normal";
|
|
19
|
+
}
|
|
20
|
+
export function resolveResistance(matrix, category, targetProperties) {
|
|
21
|
+
const byProperty = matrix.categories[category] ?? {};
|
|
22
|
+
const fallback = matrix.default ?? "normal";
|
|
23
|
+
let net = 1;
|
|
24
|
+
let matched = false;
|
|
25
|
+
let immune = false;
|
|
26
|
+
for (const property of targetProperties) {
|
|
27
|
+
const verdict = byProperty[property];
|
|
28
|
+
if (verdict === undefined)
|
|
29
|
+
continue;
|
|
30
|
+
matched = true;
|
|
31
|
+
if (verdict === "immune")
|
|
32
|
+
immune = true;
|
|
33
|
+
net *= multiplierFor(matrix, verdict);
|
|
34
|
+
}
|
|
35
|
+
if (!matched) {
|
|
36
|
+
if (fallback === "immune")
|
|
37
|
+
return { verdict: "immune", multiplier: 0, immune: true };
|
|
38
|
+
const multiplier = multiplierFor(matrix, fallback);
|
|
39
|
+
return { verdict: fallback, multiplier, immune: false };
|
|
40
|
+
}
|
|
41
|
+
const multiplier = immune ? 0 : net;
|
|
42
|
+
return { verdict: summaryVerdict(matrix, immune, multiplier), multiplier, immune };
|
|
43
|
+
}
|
|
44
|
+
export function resistanceScale(matrix, category, targetProperties) {
|
|
45
|
+
return resolveResistance(matrix, category, targetProperties).multiplier;
|
|
46
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { EntityPosition } from "../scene/entityStore.js";
|
|
2
|
+
export type TelegraphShape = {
|
|
3
|
+
kind: "circle";
|
|
4
|
+
radius: number;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "ring";
|
|
7
|
+
radius: number;
|
|
8
|
+
innerRadius: number;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "cone";
|
|
11
|
+
radius: number;
|
|
12
|
+
angle: number;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "line";
|
|
15
|
+
length: number;
|
|
16
|
+
width: number;
|
|
17
|
+
};
|
|
18
|
+
export interface TelegraphConfig {
|
|
19
|
+
shape: TelegraphShape;
|
|
20
|
+
at: EntityPosition;
|
|
21
|
+
dir?: number;
|
|
22
|
+
windupMs: number;
|
|
23
|
+
kind?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function telegraphProgress(windupMs: number, startedAtMs: number, nowMs: number): number;
|
|
26
|
+
export declare function telegraphFired(windupMs: number, startedAtMs: number, nowMs: number): boolean;
|
|
27
|
+
export declare function pointInTelegraph(config: TelegraphConfig, point: EntityPosition): boolean;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function telegraphProgress(windupMs, startedAtMs, nowMs) {
|
|
2
|
+
if (windupMs <= 0)
|
|
3
|
+
return 1;
|
|
4
|
+
return Math.max(0, Math.min(1, (nowMs - startedAtMs) / windupMs));
|
|
5
|
+
}
|
|
6
|
+
export function telegraphFired(windupMs, startedAtMs, nowMs) {
|
|
7
|
+
return nowMs - startedAtMs >= windupMs;
|
|
8
|
+
}
|
|
9
|
+
function planarDelta(config, point) {
|
|
10
|
+
return { dx: point[0] - config.at[0], dz: point[2] - config.at[2] };
|
|
11
|
+
}
|
|
12
|
+
export function pointInTelegraph(config, point) {
|
|
13
|
+
const { shape } = config;
|
|
14
|
+
const { dx, dz } = planarDelta(config, point);
|
|
15
|
+
const dist = Math.hypot(dx, dz);
|
|
16
|
+
const dir = config.dir ?? 0;
|
|
17
|
+
if (shape.kind === "circle")
|
|
18
|
+
return dist <= shape.radius;
|
|
19
|
+
if (shape.kind === "ring")
|
|
20
|
+
return dist >= shape.innerRadius && dist <= shape.radius;
|
|
21
|
+
if (shape.kind === "cone") {
|
|
22
|
+
if (dist > shape.radius || dist === 0)
|
|
23
|
+
return dist === 0;
|
|
24
|
+
const angleTo = Math.atan2(dx, dz);
|
|
25
|
+
let delta = angleTo - dir;
|
|
26
|
+
while (delta > Math.PI)
|
|
27
|
+
delta -= Math.PI * 2;
|
|
28
|
+
while (delta < -Math.PI)
|
|
29
|
+
delta += Math.PI * 2;
|
|
30
|
+
return Math.abs(delta) <= shape.angle / 2;
|
|
31
|
+
}
|
|
32
|
+
const forwardX = Math.sin(dir);
|
|
33
|
+
const forwardZ = Math.cos(dir);
|
|
34
|
+
const along = dx * forwardX + dz * forwardZ;
|
|
35
|
+
const lateral = Math.abs(dx * forwardZ - dz * forwardX);
|
|
36
|
+
return along >= 0 && along <= shape.length && lateral <= shape.width / 2;
|
|
37
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { RecipeItem } from "./recipe.js";
|
|
2
|
+
export type SoilState = "untilled" | "tilled";
|
|
3
|
+
export interface CropDef {
|
|
4
|
+
id: string;
|
|
5
|
+
stages: readonly number[];
|
|
6
|
+
regrowDays?: number;
|
|
7
|
+
needsDailyWater?: boolean;
|
|
8
|
+
harvest?: RecipeItem;
|
|
9
|
+
}
|
|
10
|
+
export interface CropTileState {
|
|
11
|
+
soil: SoilState;
|
|
12
|
+
watered: boolean;
|
|
13
|
+
cropId: string | null;
|
|
14
|
+
stage: number;
|
|
15
|
+
stageProgress: number;
|
|
16
|
+
harvestable: boolean;
|
|
17
|
+
}
|
|
18
|
+
export type CropCatalog = (cropId: string) => CropDef | null;
|
|
19
|
+
export declare function emptyTile(): CropTileState;
|
|
20
|
+
export declare function canTill(state: CropTileState): boolean;
|
|
21
|
+
export declare function tillTile(state: CropTileState): CropTileState;
|
|
22
|
+
export declare function canPlant(state: CropTileState): boolean;
|
|
23
|
+
export declare function plantCrop(state: CropTileState, cropId: string): CropTileState;
|
|
24
|
+
export declare function waterTile(state: CropTileState): CropTileState;
|
|
25
|
+
export declare function maturityDays(def: CropDef): number;
|
|
26
|
+
export declare function advanceCropDay(def: CropDef, state: CropTileState): CropTileState;
|
|
27
|
+
export interface HarvestResult {
|
|
28
|
+
state: CropTileState;
|
|
29
|
+
yield: RecipeItem | null;
|
|
30
|
+
}
|
|
31
|
+
export declare function harvestCrop(def: CropDef, state: CropTileState): HarvestResult;
|
|
32
|
+
export type TileCoord = readonly [number, number];
|
|
33
|
+
export type TilePattern = readonly TileCoord[];
|
|
34
|
+
export declare function tileKey(coord: TileCoord): string;
|
|
35
|
+
export declare function singleTile(): TilePattern;
|
|
36
|
+
export declare function squarePattern(radius: number): TilePattern;
|
|
37
|
+
export declare function diamondPattern(radius: number): TilePattern;
|
|
38
|
+
export declare function rectPattern(width: number, depth: number): TilePattern;
|
|
39
|
+
export declare function patternAt(center: TileCoord, pattern: TilePattern): TileCoord[];
|
|
40
|
+
export interface ApplyToolResult {
|
|
41
|
+
tiles: Map<string, CropTileState>;
|
|
42
|
+
changed: TileCoord[];
|
|
43
|
+
}
|
|
44
|
+
export declare function applyToolToTiles(tiles: ReadonlyMap<string, CropTileState>, center: TileCoord, pattern: TilePattern, apply: (state: CropTileState, coord: TileCoord) => CropTileState, fill?: () => CropTileState): ApplyToolResult;
|
|
45
|
+
export interface DayTicker {
|
|
46
|
+
tick(currentDay: number): number;
|
|
47
|
+
day(): number;
|
|
48
|
+
}
|
|
49
|
+
export declare function createDayTicker(startDay?: number): DayTicker;
|
|
50
|
+
export interface CropField {
|
|
51
|
+
get(coord: TileCoord): CropTileState;
|
|
52
|
+
till(coord: TileCoord, pattern?: TilePattern): TileCoord[];
|
|
53
|
+
plant(coord: TileCoord, cropId: string): boolean;
|
|
54
|
+
water(coord: TileCoord, pattern?: TilePattern): TileCoord[];
|
|
55
|
+
harvest(coord: TileCoord): RecipeItem | null;
|
|
56
|
+
advanceDay(days?: number): void;
|
|
57
|
+
tiles(): ReadonlyMap<string, CropTileState>;
|
|
58
|
+
}
|
|
59
|
+
export declare function createCropField(catalog?: CropCatalog): CropField;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
export function emptyTile() {
|
|
2
|
+
return { soil: "untilled", watered: false, cropId: null, stage: 0, stageProgress: 0, harvestable: false };
|
|
3
|
+
}
|
|
4
|
+
export function canTill(state) {
|
|
5
|
+
return state.soil === "untilled" && state.cropId === null;
|
|
6
|
+
}
|
|
7
|
+
export function tillTile(state) {
|
|
8
|
+
if (!canTill(state))
|
|
9
|
+
return state;
|
|
10
|
+
return { ...state, soil: "tilled" };
|
|
11
|
+
}
|
|
12
|
+
export function canPlant(state) {
|
|
13
|
+
return state.soil === "tilled" && state.cropId === null;
|
|
14
|
+
}
|
|
15
|
+
export function plantCrop(state, cropId) {
|
|
16
|
+
if (!canPlant(state))
|
|
17
|
+
return state;
|
|
18
|
+
return { ...state, cropId, stage: 0, stageProgress: 0, harvestable: false };
|
|
19
|
+
}
|
|
20
|
+
export function waterTile(state) {
|
|
21
|
+
if (state.soil !== "tilled" || state.watered)
|
|
22
|
+
return state;
|
|
23
|
+
return { ...state, watered: true };
|
|
24
|
+
}
|
|
25
|
+
export function maturityDays(def) {
|
|
26
|
+
return def.stages.reduce((sum, days) => sum + Math.max(0, days), 0);
|
|
27
|
+
}
|
|
28
|
+
export function advanceCropDay(def, state) {
|
|
29
|
+
const rested = state.watered ? { ...state, watered: false } : state;
|
|
30
|
+
if (state.cropId === null)
|
|
31
|
+
return rested;
|
|
32
|
+
const needsWater = def.needsDailyWater !== false;
|
|
33
|
+
if (needsWater && !state.watered)
|
|
34
|
+
return rested;
|
|
35
|
+
if (state.stage < def.stages.length) {
|
|
36
|
+
const threshold = Math.max(1, def.stages[state.stage] ?? 1);
|
|
37
|
+
const stageProgress = state.stageProgress + 1;
|
|
38
|
+
if (stageProgress < threshold)
|
|
39
|
+
return { ...rested, stageProgress };
|
|
40
|
+
const stage = state.stage + 1;
|
|
41
|
+
const harvestable = stage >= def.stages.length;
|
|
42
|
+
return { ...rested, stage, stageProgress: 0, harvestable };
|
|
43
|
+
}
|
|
44
|
+
if (!state.harvestable && def.regrowDays !== undefined && def.regrowDays > 0) {
|
|
45
|
+
const stageProgress = state.stageProgress + 1;
|
|
46
|
+
if (stageProgress < def.regrowDays)
|
|
47
|
+
return { ...rested, stageProgress };
|
|
48
|
+
return { ...rested, stageProgress: 0, harvestable: true };
|
|
49
|
+
}
|
|
50
|
+
return rested;
|
|
51
|
+
}
|
|
52
|
+
export function harvestCrop(def, state) {
|
|
53
|
+
if (!state.harvestable || state.cropId === null)
|
|
54
|
+
return { state, yield: null };
|
|
55
|
+
const produce = def.harvest ?? { itemId: state.cropId, count: 1 };
|
|
56
|
+
if (def.regrowDays !== undefined && def.regrowDays > 0) {
|
|
57
|
+
return {
|
|
58
|
+
state: { ...state, harvestable: false, stageProgress: 0, stage: def.stages.length },
|
|
59
|
+
yield: produce,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
state: { ...state, soil: "tilled", cropId: null, stage: 0, stageProgress: 0, harvestable: false },
|
|
64
|
+
yield: produce,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function tileKey(coord) {
|
|
68
|
+
return `${coord[0]},${coord[1]}`;
|
|
69
|
+
}
|
|
70
|
+
export function singleTile() {
|
|
71
|
+
return [[0, 0]];
|
|
72
|
+
}
|
|
73
|
+
export function squarePattern(radius) {
|
|
74
|
+
const r = Math.max(0, Math.floor(radius));
|
|
75
|
+
const tiles = [];
|
|
76
|
+
for (let dx = -r; dx <= r; dx++)
|
|
77
|
+
for (let dz = -r; dz <= r; dz++)
|
|
78
|
+
tiles.push([dx, dz]);
|
|
79
|
+
return tiles;
|
|
80
|
+
}
|
|
81
|
+
export function diamondPattern(radius) {
|
|
82
|
+
const r = Math.max(0, Math.floor(radius));
|
|
83
|
+
const tiles = [];
|
|
84
|
+
for (let dx = -r; dx <= r; dx++)
|
|
85
|
+
for (let dz = -r; dz <= r; dz++)
|
|
86
|
+
if (Math.abs(dx) + Math.abs(dz) <= r)
|
|
87
|
+
tiles.push([dx, dz]);
|
|
88
|
+
return tiles;
|
|
89
|
+
}
|
|
90
|
+
export function rectPattern(width, depth) {
|
|
91
|
+
const w = Math.max(1, Math.floor(width));
|
|
92
|
+
const d = Math.max(1, Math.floor(depth));
|
|
93
|
+
const x0 = -Math.floor((w - 1) / 2);
|
|
94
|
+
const z0 = -Math.floor((d - 1) / 2);
|
|
95
|
+
const tiles = [];
|
|
96
|
+
for (let dx = 0; dx < w; dx++)
|
|
97
|
+
for (let dz = 0; dz < d; dz++)
|
|
98
|
+
tiles.push([x0 + dx, z0 + dz]);
|
|
99
|
+
return tiles;
|
|
100
|
+
}
|
|
101
|
+
export function patternAt(center, pattern) {
|
|
102
|
+
return pattern.map(([dx, dz]) => [center[0] + dx, center[1] + dz]);
|
|
103
|
+
}
|
|
104
|
+
export function applyToolToTiles(tiles, center, pattern, apply, fill = emptyTile) {
|
|
105
|
+
const next = new Map(tiles);
|
|
106
|
+
const changed = [];
|
|
107
|
+
for (const coord of patternAt(center, pattern)) {
|
|
108
|
+
const key = tileKey(coord);
|
|
109
|
+
const before = next.get(key) ?? fill();
|
|
110
|
+
const after = apply(before, coord);
|
|
111
|
+
if (after !== before) {
|
|
112
|
+
next.set(key, after);
|
|
113
|
+
changed.push(coord);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return { tiles: next, changed };
|
|
117
|
+
}
|
|
118
|
+
export function createDayTicker(startDay = 0) {
|
|
119
|
+
let last = Math.floor(startDay);
|
|
120
|
+
return {
|
|
121
|
+
tick(currentDay) {
|
|
122
|
+
const now = Math.floor(currentDay);
|
|
123
|
+
const crossed = Math.max(0, now - last);
|
|
124
|
+
last = now;
|
|
125
|
+
return crossed;
|
|
126
|
+
},
|
|
127
|
+
day: () => last,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function createCropField(catalog = () => null) {
|
|
131
|
+
let grid = new Map();
|
|
132
|
+
function read(coord) {
|
|
133
|
+
return grid.get(tileKey(coord)) ?? emptyTile();
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
get: read,
|
|
137
|
+
till(coord, pattern = singleTile()) {
|
|
138
|
+
const result = applyToolToTiles(grid, coord, pattern, tillTile);
|
|
139
|
+
grid = result.tiles;
|
|
140
|
+
return result.changed;
|
|
141
|
+
},
|
|
142
|
+
plant(coord, cropId) {
|
|
143
|
+
const before = read(coord);
|
|
144
|
+
const after = plantCrop(before, cropId);
|
|
145
|
+
if (after === before)
|
|
146
|
+
return false;
|
|
147
|
+
grid.set(tileKey(coord), after);
|
|
148
|
+
return true;
|
|
149
|
+
},
|
|
150
|
+
water(coord, pattern = singleTile()) {
|
|
151
|
+
const result = applyToolToTiles(grid, coord, pattern, waterTile);
|
|
152
|
+
grid = result.tiles;
|
|
153
|
+
return result.changed;
|
|
154
|
+
},
|
|
155
|
+
harvest(coord) {
|
|
156
|
+
const state = read(coord);
|
|
157
|
+
if (state.cropId === null)
|
|
158
|
+
return null;
|
|
159
|
+
const def = catalog(state.cropId);
|
|
160
|
+
if (def === null)
|
|
161
|
+
return null;
|
|
162
|
+
const result = harvestCrop(def, state);
|
|
163
|
+
grid.set(tileKey(coord), result.state);
|
|
164
|
+
return result.yield;
|
|
165
|
+
},
|
|
166
|
+
advanceDay(days = 1) {
|
|
167
|
+
for (let i = 0; i < days; i++) {
|
|
168
|
+
for (const [key, state] of grid) {
|
|
169
|
+
if (state.cropId === null) {
|
|
170
|
+
if (state.watered)
|
|
171
|
+
grid.set(key, { ...state, watered: false });
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const def = catalog(state.cropId);
|
|
175
|
+
grid.set(key, def === null ? state : advanceCropDay(def, state));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
tiles: () => grid,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { RecipeItem } from "./recipe.js";
|
|
2
|
+
export interface ProductionBuildingDef {
|
|
3
|
+
id: string;
|
|
4
|
+
inputs: readonly RecipeItem[];
|
|
5
|
+
outputs: readonly RecipeItem[];
|
|
6
|
+
rate: number;
|
|
7
|
+
power?: number;
|
|
8
|
+
bufferMultiplier?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ProductionBuildingConfig {
|
|
11
|
+
id: string;
|
|
12
|
+
inputs?: readonly RecipeItem[];
|
|
13
|
+
outputs: readonly RecipeItem[];
|
|
14
|
+
rate: number;
|
|
15
|
+
power?: number;
|
|
16
|
+
bufferMultiplier?: number;
|
|
17
|
+
}
|
|
18
|
+
export type ItemCounts = Readonly<Record<string, number>>;
|
|
19
|
+
export interface ProductionState {
|
|
20
|
+
buffer: ItemCounts;
|
|
21
|
+
output: ItemCounts;
|
|
22
|
+
progress: number;
|
|
23
|
+
active: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface ProductionTickInput {
|
|
26
|
+
dt: number;
|
|
27
|
+
powered?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function productionBuilding(config: ProductionBuildingConfig): ProductionBuildingDef;
|
|
30
|
+
export declare function createProductionState(): ProductionState;
|
|
31
|
+
export declare function acceptsInput(def: ProductionBuildingDef, state: ProductionState, itemId: string): boolean;
|
|
32
|
+
export declare function feedProduction(def: ProductionBuildingDef, state: ProductionState, itemId: string, count: number): {
|
|
33
|
+
state: ProductionState;
|
|
34
|
+
accepted: number;
|
|
35
|
+
};
|
|
36
|
+
export declare function drainOutput(state: ProductionState, itemId: string, count?: number): {
|
|
37
|
+
state: ProductionState;
|
|
38
|
+
taken: number;
|
|
39
|
+
};
|
|
40
|
+
export declare function tickProduction(def: ProductionBuildingDef, state: ProductionState, input: ProductionTickInput): ProductionState;
|
|
41
|
+
export interface TransportItem {
|
|
42
|
+
itemId: string;
|
|
43
|
+
count: number;
|
|
44
|
+
position: number;
|
|
45
|
+
}
|
|
46
|
+
export interface TransportPath {
|
|
47
|
+
length: number;
|
|
48
|
+
speed: number;
|
|
49
|
+
}
|
|
50
|
+
export declare function advanceTransport(path: TransportPath, items: readonly TransportItem[], dt: number): {
|
|
51
|
+
items: TransportItem[];
|
|
52
|
+
delivered: TransportItem[];
|
|
53
|
+
};
|
|
54
|
+
export interface PowerConsumer {
|
|
55
|
+
id: string;
|
|
56
|
+
demand: number;
|
|
57
|
+
}
|
|
58
|
+
export interface PowerGridResult {
|
|
59
|
+
powered: ReadonlySet<string>;
|
|
60
|
+
supply: number;
|
|
61
|
+
demand: number;
|
|
62
|
+
deficit: number;
|
|
63
|
+
}
|
|
64
|
+
export declare function resolvePowerGrid(supply: number, consumers: readonly PowerConsumer[]): PowerGridResult;
|