@jgengine/core 0.6.0 → 0.8.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 +214 -2
- package/README.md +2 -0
- 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 +61 -0
- package/dist/ai/spawnDirector.js +162 -0
- package/dist/ai/threat.d.ts +25 -0
- package/dist/ai/threat.js +67 -0
- package/dist/anim/easing.d.ts +14 -0
- package/dist/anim/easing.js +55 -0
- package/dist/audio/audioFalloff.d.ts +38 -0
- package/dist/audio/audioFalloff.js +32 -0
- package/dist/behaviour/behaviour.d.ts +136 -0
- package/dist/behaviour/behaviour.js +388 -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 +64 -0
- package/dist/cards/cardPile.js +175 -0
- package/dist/cards/modifierPipeline.d.ts +33 -0
- package/dist/cards/modifierPipeline.js +48 -0
- package/dist/combat/abilityKit.d.ts +47 -0
- package/dist/combat/abilityKit.js +139 -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 +12 -1
- package/dist/combat/effects.js +31 -19
- package/dist/combat/hitReaction.d.ts +23 -0
- package/dist/combat/hitReaction.js +20 -0
- package/dist/combat/projectiles.d.ts +42 -5
- package/dist/combat/projectiles.js +98 -12
- package/dist/combat/resistance.d.ts +13 -0
- package/dist/combat/resistance.js +46 -0
- package/dist/combat/telegraph.d.ts +31 -0
- package/dist/combat/telegraph.js +51 -0
- package/dist/commands/commandRegistry.d.ts +1 -1
- package/dist/commands/commandRegistry.js +2 -1
- 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/data/dataSource.d.ts +26 -0
- package/dist/data/dataSource.js +93 -0
- package/dist/data/devProxy.d.ts +11 -0
- package/dist/data/devProxy.js +38 -0
- package/dist/data/fetchJson.d.ts +20 -0
- package/dist/data/fetchJson.js +43 -0
- package/dist/data/jsonDataSource.d.ts +4 -0
- package/dist/data/jsonDataSource.js +6 -0
- package/dist/devtools/devtools.d.ts +157 -0
- package/dist/devtools/devtools.js +484 -0
- package/dist/devtools/transformTunables.d.ts +14 -0
- package/dist/devtools/transformTunables.js +38 -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/chat.d.ts +66 -0
- package/dist/game/chat.js +165 -0
- package/dist/game/cosmetics.d.ts +25 -0
- package/dist/game/cosmetics.js +51 -0
- package/dist/game/defineGame.d.ts +10 -2
- package/dist/game/defineGame.js +2 -1
- package/dist/game/events.d.ts +87 -0
- package/dist/game/levelSequence.d.ts +44 -0
- package/dist/game/levelSequence.js +78 -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 +516 -8
- package/dist/game/playableGame.js +6 -1
- package/dist/game/progression.d.ts +1 -0
- package/dist/game/progression.js +23 -2
- package/dist/game/race.d.ts +100 -0
- package/dist/game/race.js +236 -0
- package/dist/game/runDraft.d.ts +34 -0
- package/dist/game/runDraft.js +97 -0
- package/dist/game/social.d.ts +69 -0
- package/dist/game/social.js +138 -0
- package/dist/game/spawnPoints.d.ts +19 -0
- package/dist/game/spawnPoints.js +23 -0
- package/dist/game/worldItem.d.ts +95 -0
- package/dist/game/worldItem.js +138 -0
- package/dist/input/actionBindings.d.ts +10 -0
- package/dist/input/actionBindings.js +14 -0
- package/dist/input/axisInput.d.ts +43 -0
- package/dist/input/axisInput.js +70 -0
- package/dist/input/gestureSurface.d.ts +31 -0
- package/dist/input/gestureSurface.js +120 -0
- package/dist/input/pointer.d.ts +69 -0
- package/dist/input/pointer.js +65 -0
- package/dist/input/touchScheme.d.ts +83 -0
- package/dist/input/touchScheme.js +115 -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 +115 -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 +71 -1
- package/dist/movement/dash.d.ts +37 -0
- package/dist/movement/dash.js +65 -0
- package/dist/movement/movementModel.d.ts +36 -3
- package/dist/movement/movementModel.js +81 -5
- package/dist/movement/voxelController.d.ts +61 -0
- package/dist/movement/voxelController.js +229 -0
- package/dist/multiplayer/chatContract.d.ts +42 -0
- package/dist/multiplayer/chatContract.js +57 -0
- package/dist/multiplayer/combatSnapshot.d.ts +45 -0
- package/dist/multiplayer/combatSnapshot.js +89 -0
- package/dist/multiplayer/identity.d.ts +13 -0
- package/dist/multiplayer/identity.js +20 -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/poseSyncGate.d.ts +1 -0
- package/dist/multiplayer/poseSyncGate.js +13 -1
- package/dist/multiplayer/presenceModel.d.ts +3 -0
- package/dist/multiplayer/presenceModel.js +15 -1
- package/dist/multiplayer/simultaneousCommit.d.ts +34 -0
- package/dist/multiplayer/simultaneousCommit.js +53 -0
- package/dist/multiplayer/voiceContract.d.ts +44 -0
- package/dist/multiplayer/voiceContract.js +108 -0
- package/dist/nav/navConstrain.d.ts +14 -0
- package/dist/nav/navConstrain.js +26 -0
- package/dist/nav/navFromEnvironment.d.ts +11 -0
- package/dist/nav/navFromEnvironment.js +16 -0
- package/dist/nav/navGrid.d.ts +56 -0
- package/dist/nav/navGrid.js +265 -0
- package/dist/nav/pathFollow.d.ts +31 -0
- package/dist/nav/pathFollow.js +69 -0
- package/dist/physics/ballisticSweep.d.ts +18 -0
- package/dist/physics/ballisticSweep.js +49 -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 +124 -2
- package/dist/physics/physicsWorld.js +493 -41
- 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 +143 -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 +99 -0
- package/dist/physics/vehicleBody.js +233 -0
- package/dist/puzzle/cellGrid.d.ts +26 -0
- package/dist/puzzle/cellGrid.js +110 -0
- package/dist/puzzle/fallingPiece.d.ts +24 -0
- package/dist/puzzle/fallingPiece.js +51 -0
- package/dist/random/nameGen.d.ts +18 -0
- package/dist/random/nameGen.js +35 -0
- package/dist/random/rng.d.ts +2 -0
- package/dist/random/rng.js +23 -0
- package/dist/runtime/adapter.d.ts +35 -0
- package/dist/runtime/adapter.js +37 -1
- package/dist/runtime/cameraDirector.d.ts +10 -0
- package/dist/runtime/cameraDirector.js +25 -0
- package/dist/runtime/gameContext.d.ts +107 -3
- package/dist/runtime/gameContext.js +253 -7
- package/dist/runtime/hostPersistence.d.ts +39 -0
- package/dist/runtime/hostPersistence.js +54 -0
- package/dist/runtime/inputSnapshot.d.ts +7 -0
- package/dist/runtime/inputSnapshot.js +12 -0
- package/dist/runtime/motionIntents.d.ts +25 -0
- package/dist/runtime/motionIntents.js +25 -0
- package/dist/runtime/persistenceScope.d.ts +32 -0
- package/dist/runtime/persistenceScope.js +64 -0
- package/dist/runtime/transport.d.ts +32 -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 +9 -1
- package/dist/scene/entityStore.js +25 -2
- 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/movementSpeed.d.ts +35 -0
- package/dist/scene/movementSpeed.js +20 -0
- package/dist/scene/objectQuery.d.ts +18 -0
- package/dist/scene/objectQuery.js +108 -0
- package/dist/scene/objectStore.d.ts +11 -0
- package/dist/scene/objectStore.js +109 -4
- package/dist/scene/paintLayer.d.ts +15 -0
- package/dist/scene/paintLayer.js +53 -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/spatial.d.ts +21 -0
- package/dist/scene/spatial.js +63 -1
- package/dist/scene/stationClaim.d.ts +43 -0
- package/dist/scene/stationClaim.js +109 -0
- package/dist/sensor/concealment.d.ts +21 -0
- package/dist/sensor/concealment.js +63 -0
- package/dist/sensor/freezeMonitor.d.ts +17 -0
- package/dist/sensor/freezeMonitor.js +35 -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/roles.d.ts +6 -0
- package/dist/session/roles.js +32 -0
- package/dist/session/roundState.d.ts +79 -0
- package/dist/session/roundState.js +158 -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/fallingGrid.d.ts +62 -0
- package/dist/tactics/fallingGrid.js +158 -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 +68 -0
- package/dist/tactics/tacticalGrid.js +261 -0
- package/dist/time/beatClock.d.ts +40 -0
- package/dist/time/beatClock.js +73 -0
- package/dist/time/simClock.d.ts +25 -1
- package/dist/time/simClock.js +28 -3
- package/dist/turn/commit.d.ts +27 -0
- package/dist/turn/commit.js +72 -0
- package/dist/turn/intent.d.ts +14 -0
- package/dist/turn/intent.js +29 -0
- package/dist/turn/turnLoop.d.ts +60 -0
- package/dist/turn/turnLoop.js +223 -0
- package/dist/world/buildPermissions.d.ts +47 -0
- package/dist/world/buildPermissions.js +134 -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/environmentSummary.d.ts +53 -0
- package/dist/world/environmentSummary.js +129 -0
- package/dist/world/features.d.ts +87 -6
- package/dist/world/features.js +52 -2
- package/dist/world/fog.d.ts +40 -0
- package/dist/world/fog.js +137 -0
- package/dist/world/gridInstances.d.ts +13 -0
- package/dist/world/gridInstances.js +22 -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 +69 -0
- package/dist/world/placementController.js +153 -0
- package/dist/world/realm.d.ts +61 -0
- package/dist/world/realm.js +66 -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 +23 -1
- package/dist/world/terrain.js +64 -1
- package/dist/world/voxelField.d.ts +54 -0
- package/dist/world/voxelField.js +241 -0
- package/dist/world/walls.d.ts +61 -0
- package/dist/world/walls.js +221 -0
- package/dist/world/weather.d.ts +92 -0
- package/dist/world/weather.js +169 -0
- package/llms.txt +4102 -0
- package/package.json +3 -2
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createCardPile } from "../cards/cardPile.js";
|
|
2
|
+
import { createDeathSystem, deathReasonFromEffect, normalizeOnDeath } from "../combat/death.js";
|
|
2
3
|
import { createEffectSystem, } from "../combat/effects.js";
|
|
3
4
|
import { createProjectileSystem } from "../combat/projectiles.js";
|
|
5
|
+
import { resolveHitReaction, } from "../combat/hitReaction.js";
|
|
6
|
+
import { pointInTelegraph, } from "../combat/telegraph.js";
|
|
4
7
|
import { createCommandRegistry, } from "../commands/commandRegistry.js";
|
|
5
8
|
import { balance as walletBalance, canAfford as walletCanAfford, charge as walletCharge, chargeAll as walletChargeAll, createEmptyWallet, grant as walletGrant, } from "../economy/wallet.js";
|
|
9
|
+
import { createCosmetics } from "../game/cosmetics.js";
|
|
10
|
+
import { groundFieldFor } from "../world/terrain.js";
|
|
6
11
|
import { createGameEvents } from "../game/events.js";
|
|
7
12
|
import { createGameFeed } from "../game/feed.js";
|
|
8
13
|
import { createLeaderboard } from "../game/leaderboard.js";
|
|
9
14
|
import { createLoadouts } from "../game/loadout.js";
|
|
10
15
|
import { createLootRegistry, grantDrops } from "../game/lootTable.js";
|
|
11
16
|
import { createQuestJournal } from "../game/quest.js";
|
|
17
|
+
import { createWorldItemStore, resolveDeathDrops, DEFAULT_RARITY, WORLD_ITEM_ENTITY_NAME, } from "../game/worldItem.js";
|
|
18
|
+
import { createChat } from "../game/chat.js";
|
|
12
19
|
import { createSocial } from "../game/social.js";
|
|
13
20
|
import { createTradeSystem } from "../game/trade.js";
|
|
14
21
|
import { createUnlocks } from "../game/unlocks.js";
|
|
@@ -16,18 +23,29 @@ import { createInventorySet, putItem, } from "../inventory/inventoryModel.js";
|
|
|
16
23
|
import { createItemUse, } from "../item/use.js";
|
|
17
24
|
import { createWeaponStats } from "../item/weapon.js";
|
|
18
25
|
import { createPoseState } from "../movement/poseState.js";
|
|
26
|
+
import { createPaintLayer } from "../scene/paintLayer.js";
|
|
19
27
|
import { createEntityStatsApi, seedStatValues, setStatValue, } from "../scene/entityStats.js";
|
|
28
|
+
import { createForms } from "../scene/form.js";
|
|
29
|
+
import { raycastObjects, raycastObjectsAll } from "../scene/objectQuery.js";
|
|
20
30
|
import { createObjectStore } from "../scene/objectStore.js";
|
|
31
|
+
import { createRoster } from "../scene/roster.js";
|
|
32
|
+
import { createPossession } from "../scene/possession.js";
|
|
21
33
|
import { createSpatialApi } from "../scene/spatial.js";
|
|
22
34
|
import { createTargeting } from "../scene/targeting.js";
|
|
23
35
|
import { createStats } from "../stats/statModifiers.js";
|
|
24
36
|
import { createChangeSignal, notifyAfter } from "../store/changeSignal.js";
|
|
37
|
+
import { createObservableKeyedStore } from "../store/observableKeyedStore.js";
|
|
25
38
|
import { createSimClock } from "../time/simClock.js";
|
|
39
|
+
import { createTurnLoop } from "../turn/turnLoop.js";
|
|
40
|
+
import { createCameraDirector } from "./cameraDirector.js";
|
|
41
|
+
import { createInputSnapshot } from "./inputSnapshot.js";
|
|
42
|
+
import { createMotionIntents } from "./motionIntents.js";
|
|
26
43
|
export function createGameContext(options) {
|
|
27
44
|
const { definition, content, player } = options;
|
|
28
45
|
const now = options.now ?? Date.now;
|
|
29
46
|
const signal = createChangeSignal();
|
|
30
47
|
const time = createSimClock({ config: definition.time, onChange: signal.notify });
|
|
48
|
+
const ground = groundFieldFor(definition.world);
|
|
31
49
|
const entities = definition.scene;
|
|
32
50
|
const objects = createObjectStore();
|
|
33
51
|
entities.subscribe(signal.notify);
|
|
@@ -77,20 +95,44 @@ export function createGameContext(options) {
|
|
|
77
95
|
signal.notify();
|
|
78
96
|
},
|
|
79
97
|
};
|
|
80
|
-
const feed = createGameFeed();
|
|
98
|
+
const feed = createGameFeed(definition.feed);
|
|
81
99
|
const lootRegistry = createLootRegistry();
|
|
82
100
|
const unlocks = notifyAfter(createUnlocks(), ["grant", "hydrate"], signal.notify);
|
|
83
|
-
const rawSocial = createSocial({
|
|
101
|
+
const rawSocial = createSocial({
|
|
102
|
+
events,
|
|
103
|
+
now,
|
|
104
|
+
emotes: {
|
|
105
|
+
entities: { get: (id) => entities.get(id) },
|
|
106
|
+
spatial: { inRadius: (center, radius, filter) => spatial.inRadius(center, radius, filter) },
|
|
107
|
+
},
|
|
108
|
+
});
|
|
84
109
|
const social = {
|
|
85
|
-
friends: notifyAfter(rawSocial.friends, ["request", "accept", "remove", "block", "hydrate"], signal.notify),
|
|
86
|
-
party: notifyAfter(rawSocial.party, ["invite", "accept", "kick", "leave", "promote"], signal.notify),
|
|
110
|
+
friends: notifyAfter(rawSocial.friends, ["request", "accept", "decline", "remove", "block", "hydrate"], signal.notify),
|
|
111
|
+
party: notifyAfter(rawSocial.party, ["invite", "accept", "decline", "kick", "leave", "promote"], signal.notify),
|
|
87
112
|
presence: rawSocial.presence,
|
|
113
|
+
emotes: rawSocial.emotes,
|
|
114
|
+
worldInvites: notifyAfter(rawSocial.worldInvites, ["invite", "accept", "decline"], signal.notify),
|
|
88
115
|
};
|
|
116
|
+
const chat = notifyAfter(createChat({
|
|
117
|
+
events,
|
|
118
|
+
now,
|
|
119
|
+
party: rawSocial.party,
|
|
120
|
+
proximity: {
|
|
121
|
+
entities: { get: (id) => entities.get(id) },
|
|
122
|
+
spatial: { inRadius: (center, radius, filter) => spatial.inRadius(center, radius, filter) },
|
|
123
|
+
},
|
|
124
|
+
blockedBy: (userId) => rawSocial.friends.snapshot(userId).blocked,
|
|
125
|
+
}), ["register", "send", "whisper", "hydrate"], signal.notify);
|
|
89
126
|
const leaderboard = notifyAfter(createLeaderboard(), ["increment", "hydrate"], signal.notify);
|
|
127
|
+
const roster = notifyAfter(createRoster({ now }), ["capture", "release", "setEquipped", "hydrate"], signal.notify);
|
|
90
128
|
const playerStats = createStats({});
|
|
91
129
|
const pose = createPoseState((instanceId) => catalogEntry(instanceId)?.movement);
|
|
92
130
|
const commandRegistry = createCommandRegistry();
|
|
93
131
|
const itemUse = createItemUse((itemId) => content.itemById?.(itemId)?.use);
|
|
132
|
+
const possession = notifyAfter(createPossession({ entities, events }), ["possess", "own", "disown"], signal.notify);
|
|
133
|
+
const forms = notifyAfter(createForms({ entities, time, events }), ["shapeshift", "revert"], signal.notify);
|
|
134
|
+
const cosmetics = notifyAfter(createCosmetics({ events }), ["apply", "equip", "hydrate"], signal.notify);
|
|
135
|
+
const paintLayer = notifyAfter(createPaintLayer(), ["paint", "clear"], signal.notify);
|
|
94
136
|
const inventoryDeclarations = definition.inventories ?? {};
|
|
95
137
|
const inventoryIds = Object.keys(inventoryDeclarations);
|
|
96
138
|
const layouts = {};
|
|
@@ -242,6 +284,47 @@ export function createGameContext(options) {
|
|
|
242
284
|
pose.clear(instanceId);
|
|
243
285
|
return existed;
|
|
244
286
|
}
|
|
287
|
+
function resetAllToSpawn(filter) {
|
|
288
|
+
let count = 0;
|
|
289
|
+
for (const entity of entities.list()) {
|
|
290
|
+
if (filter !== undefined && !filter(entity))
|
|
291
|
+
continue;
|
|
292
|
+
if (entities.resetToSpawn(entity.id))
|
|
293
|
+
count += 1;
|
|
294
|
+
}
|
|
295
|
+
return count;
|
|
296
|
+
}
|
|
297
|
+
const worldItems = notifyAfter(createWorldItemStore({
|
|
298
|
+
spawnEntity: (position) => entities.spawn(WORLD_ITEM_ENTITY_NAME, { position, role: "prop" }),
|
|
299
|
+
despawnEntity,
|
|
300
|
+
resolvePosition: (instanceId) => entities.get(instanceId)?.position,
|
|
301
|
+
}), ["spawn", "take", "remove"], signal.notify);
|
|
302
|
+
function spawnWorldItem(input) {
|
|
303
|
+
const record = worldItems.spawn(input);
|
|
304
|
+
events.emit("worldItem.dropped", {
|
|
305
|
+
instanceId: record.instanceId,
|
|
306
|
+
itemId: record.itemId,
|
|
307
|
+
rarity: record.rarity,
|
|
308
|
+
count: record.count,
|
|
309
|
+
position: [input.position[0], input.position[1], input.position[2]],
|
|
310
|
+
...(record.source !== undefined ? { source: record.source } : {}),
|
|
311
|
+
});
|
|
312
|
+
return record;
|
|
313
|
+
}
|
|
314
|
+
function pickupWorldItem(instanceId, userId) {
|
|
315
|
+
const record = worldItems.take(instanceId);
|
|
316
|
+
if (record === null)
|
|
317
|
+
return { status: "rejected", reason: "not-found" };
|
|
318
|
+
loot.grantToPlayer(userId, [{ item: record.itemId, count: record.count }], "worldItem.pickup");
|
|
319
|
+
events.emit("worldItem.picked_up", {
|
|
320
|
+
instanceId,
|
|
321
|
+
userId,
|
|
322
|
+
itemId: record.itemId,
|
|
323
|
+
rarity: record.rarity,
|
|
324
|
+
count: record.count,
|
|
325
|
+
});
|
|
326
|
+
return { status: "ok", record };
|
|
327
|
+
}
|
|
245
328
|
const death = createDeathSystem({
|
|
246
329
|
resolveOnDeath: (instanceId) => catalogEntry(instanceId)?.onDeath,
|
|
247
330
|
resolveIdentity(instanceId) {
|
|
@@ -268,7 +351,10 @@ export function createGameContext(options) {
|
|
|
268
351
|
getStat: weapon.getStat,
|
|
269
352
|
spatial: combatSpatial,
|
|
270
353
|
onLethal(instanceId, lethalCtx) {
|
|
271
|
-
const
|
|
354
|
+
const dyingEntity = entities.get(instanceId);
|
|
355
|
+
const catalogId = dyingEntity?.name;
|
|
356
|
+
const position = dyingEntity?.position;
|
|
357
|
+
const normalizedOnDeath = normalizeOnDeath(catalogEntry(instanceId)?.onDeath);
|
|
272
358
|
const reason = deathReasonFromEffect({
|
|
273
359
|
...lethalCtx,
|
|
274
360
|
userIdOf: (id) => (id === player.userId ? player.userId : undefined),
|
|
@@ -278,7 +364,23 @@ export function createGameContext(options) {
|
|
|
278
364
|
resolution.drops.length > 0 &&
|
|
279
365
|
reason.kind === "player_kill" &&
|
|
280
366
|
reason.killerUserId === player.userId) {
|
|
281
|
-
|
|
367
|
+
if (normalizedOnDeath.dropMode === "world" && position !== undefined) {
|
|
368
|
+
const resolved = resolveDeathDrops(resolution.drops, {
|
|
369
|
+
mode: "world",
|
|
370
|
+
origin: position,
|
|
371
|
+
resolveRarity: (itemId) => content.itemById?.(itemId)?.rarity ?? DEFAULT_RARITY,
|
|
372
|
+
resolveBaseType: (itemId) => content.itemById?.(itemId)?.baseType ?? itemId,
|
|
373
|
+
scatter: normalizedOnDeath.scatter,
|
|
374
|
+
...(catalogId !== undefined ? { source: catalogId } : {}),
|
|
375
|
+
});
|
|
376
|
+
for (const spawn of resolved.worldSpawns)
|
|
377
|
+
spawnWorldItem(spawn);
|
|
378
|
+
if (resolved.grants.length > 0)
|
|
379
|
+
loot.grantToPlayer(player.userId, resolved.grants, catalogId);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
loot.grantToPlayer(player.userId, resolution.drops, catalogId);
|
|
383
|
+
}
|
|
282
384
|
}
|
|
283
385
|
},
|
|
284
386
|
}), ["applyEffect"], signal.notify);
|
|
@@ -297,8 +399,76 @@ export function createGameContext(options) {
|
|
|
297
399
|
event.instanceId = input.instanceId;
|
|
298
400
|
if (input.amount !== undefined)
|
|
299
401
|
event.amount = input.amount;
|
|
402
|
+
if (input.hitType !== undefined)
|
|
403
|
+
event.hitType = input.hitType;
|
|
404
|
+
if (input.element !== undefined)
|
|
405
|
+
event.element = input.element;
|
|
406
|
+
if (input.crit !== undefined)
|
|
407
|
+
event.crit = input.crit;
|
|
408
|
+
if (input.scale !== undefined)
|
|
409
|
+
event.scale = input.scale;
|
|
300
410
|
events.emit("entity.floatText", event);
|
|
301
411
|
}
|
|
412
|
+
let telegraphSeq = 0;
|
|
413
|
+
function fireTelegraph(input) {
|
|
414
|
+
const id = telegraphSeq++;
|
|
415
|
+
const telegraphEvent = {
|
|
416
|
+
id,
|
|
417
|
+
shape: input.shape,
|
|
418
|
+
position: [input.at[0], input.at[1], input.at[2]],
|
|
419
|
+
windupMs: input.windupMs,
|
|
420
|
+
kind: input.kind ?? "danger",
|
|
421
|
+
};
|
|
422
|
+
if (input.dir !== undefined)
|
|
423
|
+
telegraphEvent.dir = input.dir;
|
|
424
|
+
events.emit("combat.telegraph", telegraphEvent);
|
|
425
|
+
const bound = input.effect;
|
|
426
|
+
if (bound === undefined)
|
|
427
|
+
return () => { };
|
|
428
|
+
const config = { shape: input.shape, at: input.at, windupMs: input.windupMs };
|
|
429
|
+
if (input.dir !== undefined)
|
|
430
|
+
config.dir = input.dir;
|
|
431
|
+
return time.after(input.windupMs / 1000, () => {
|
|
432
|
+
const targets = entities.list().filter((entity) => pointInTelegraph(config, entity.position));
|
|
433
|
+
for (const target of targets) {
|
|
434
|
+
applyEffectAndFloat({
|
|
435
|
+
from: input.from,
|
|
436
|
+
to: target.id,
|
|
437
|
+
effect: bound.effect,
|
|
438
|
+
...(bound.via === undefined ? {} : { via: bound.via }),
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
function applyHitReaction(input) {
|
|
444
|
+
const attacker = entities.get(input.from);
|
|
445
|
+
const target = entities.get(input.to);
|
|
446
|
+
if (target === null)
|
|
447
|
+
return null;
|
|
448
|
+
const attackerPos = attacker?.position ?? target.position;
|
|
449
|
+
const reaction = resolveHitReaction(input.config, {
|
|
450
|
+
attackerPos,
|
|
451
|
+
targetPos: target.position,
|
|
452
|
+
...(input.power === undefined ? {} : { power: input.power }),
|
|
453
|
+
});
|
|
454
|
+
entities.setPose(input.to, {
|
|
455
|
+
position: [
|
|
456
|
+
target.position[0] + reaction.impulse[0],
|
|
457
|
+
target.position[1] + reaction.impulse[1],
|
|
458
|
+
target.position[2] + reaction.impulse[2],
|
|
459
|
+
],
|
|
460
|
+
rotationY: target.rotationY,
|
|
461
|
+
});
|
|
462
|
+
const reactionEvent = {
|
|
463
|
+
instanceId: input.to,
|
|
464
|
+
position: [target.position[0], target.position[1], target.position[2]],
|
|
465
|
+
hitstopMs: reaction.hitstopMs,
|
|
466
|
+
};
|
|
467
|
+
if (reaction.shake !== null)
|
|
468
|
+
reactionEvent.shake = reaction.shake;
|
|
469
|
+
events.emit("combat.hitReaction", reactionEvent);
|
|
470
|
+
return reaction;
|
|
471
|
+
}
|
|
302
472
|
function applyEffectAndFloat(input) {
|
|
303
473
|
const positionsBefore = new Map();
|
|
304
474
|
for (const entity of entities.list())
|
|
@@ -347,18 +517,70 @@ export function createGameContext(options) {
|
|
|
347
517
|
const sceneObjects = {
|
|
348
518
|
...objects,
|
|
349
519
|
catalog: (instanceId) => catalogObject(instanceId) ?? null,
|
|
520
|
+
raycast: (input) => raycastObjects(objects.list(), input),
|
|
521
|
+
raycastAll: (input) => raycastObjectsAll(objects.list(), input),
|
|
350
522
|
};
|
|
523
|
+
const store = notifyAfter(createObservableKeyedStore(), ["set", "delete"], signal.notify);
|
|
524
|
+
const cardPiles = new Map();
|
|
525
|
+
function pile(id, config) {
|
|
526
|
+
const existing = cardPiles.get(id);
|
|
527
|
+
if (existing !== undefined)
|
|
528
|
+
return existing;
|
|
529
|
+
if (config === undefined) {
|
|
530
|
+
throw new Error(`cardPile "${id}" has not been created yet; pass a config on first access`);
|
|
531
|
+
}
|
|
532
|
+
const created = notifyAfter(createCardPile(config), ["shuffle", "draw", "discard", "exhaust", "move", "reset"], signal.notify);
|
|
533
|
+
cardPiles.set(id, created);
|
|
534
|
+
return created;
|
|
535
|
+
}
|
|
536
|
+
const turnLoops = new Map();
|
|
537
|
+
function loop(id, config) {
|
|
538
|
+
const existing = turnLoops.get(id);
|
|
539
|
+
if (existing !== undefined)
|
|
540
|
+
return existing;
|
|
541
|
+
if (config === undefined) {
|
|
542
|
+
throw new Error(`turn loop "${id}" has not been created yet; pass a config on first access`);
|
|
543
|
+
}
|
|
544
|
+
const raw = createTurnLoop(config);
|
|
545
|
+
const wrappedCommit = notifyAfter(raw.commit, ["submit", "expected", "commit", "rewind", "clear"], signal.notify);
|
|
546
|
+
const wrapped = {
|
|
547
|
+
...notifyAfter(raw, [
|
|
548
|
+
"setOrder",
|
|
549
|
+
"addParticipant",
|
|
550
|
+
"removeParticipant",
|
|
551
|
+
"advancePhase",
|
|
552
|
+
"advanceTurn",
|
|
553
|
+
"advanceRound",
|
|
554
|
+
"spend",
|
|
555
|
+
"gain",
|
|
556
|
+
"refill",
|
|
557
|
+
"restore",
|
|
558
|
+
], signal.notify),
|
|
559
|
+
commit: wrappedCommit,
|
|
560
|
+
};
|
|
561
|
+
turnLoops.set(id, wrapped);
|
|
562
|
+
return wrapped;
|
|
563
|
+
}
|
|
564
|
+
const camera = notifyAfter(createCameraDirector(), ["follow", "setCinematic"], signal.notify);
|
|
565
|
+
const input = createInputSnapshot();
|
|
566
|
+
const motion = createMotionIntents();
|
|
351
567
|
const ctx = {
|
|
352
568
|
scene: {
|
|
353
569
|
object: sceneObjects,
|
|
354
570
|
entity: {
|
|
355
571
|
spawn: spawnEntity,
|
|
356
572
|
despawn: despawnEntity,
|
|
573
|
+
update: entities.update,
|
|
357
574
|
setPose: entities.setPose,
|
|
358
575
|
get: entities.get,
|
|
359
576
|
list: entities.list,
|
|
577
|
+
spawnPoseOf: entities.spawnPoseOf,
|
|
578
|
+
resetToSpawn: entities.resetToSpawn,
|
|
579
|
+
resetAllToSpawn,
|
|
360
580
|
stats: entityStats,
|
|
361
581
|
floatText: emitFloatText,
|
|
582
|
+
telegraph: fireTelegraph,
|
|
583
|
+
hitReaction: applyHitReaction,
|
|
362
584
|
setTarget: targeting.setTarget,
|
|
363
585
|
getTarget: targeting.getTarget,
|
|
364
586
|
cycleTarget: targeting.cycleTarget,
|
|
@@ -373,8 +595,22 @@ export function createGameContext(options) {
|
|
|
373
595
|
hasLineOfSight: spatial.hasLineOfSight,
|
|
374
596
|
queryArc: spatial.queryArc,
|
|
375
597
|
moveToward: spatial.moveToward,
|
|
598
|
+
form: forms,
|
|
599
|
+
paint: paintLayer,
|
|
600
|
+
},
|
|
601
|
+
worldItem: {
|
|
602
|
+
spawn: spawnWorldItem,
|
|
603
|
+
get: worldItems.get,
|
|
604
|
+
list: worldItems.list,
|
|
605
|
+
nearestInRadius: worldItems.nearestInRadius,
|
|
606
|
+
pickup: pickupWorldItem,
|
|
607
|
+
consume: worldItems.remove,
|
|
376
608
|
},
|
|
377
609
|
},
|
|
610
|
+
world: {
|
|
611
|
+
ground,
|
|
612
|
+
groundHeightAt: ground.sampleHeight,
|
|
613
|
+
},
|
|
378
614
|
game: {
|
|
379
615
|
commands: {
|
|
380
616
|
define: commandRegistry.define,
|
|
@@ -405,9 +641,14 @@ export function createGameContext(options) {
|
|
|
405
641
|
trade,
|
|
406
642
|
quest,
|
|
407
643
|
social,
|
|
644
|
+
chat,
|
|
408
645
|
unlocks,
|
|
409
646
|
economy,
|
|
410
647
|
leaderboard,
|
|
648
|
+
roster,
|
|
649
|
+
store,
|
|
650
|
+
cards: { pile },
|
|
651
|
+
turn: { loop },
|
|
411
652
|
},
|
|
412
653
|
player: {
|
|
413
654
|
userId: player.userId,
|
|
@@ -417,6 +658,9 @@ export function createGameContext(options) {
|
|
|
417
658
|
loadout: loadouts,
|
|
418
659
|
applyLoadout: loadouts.applyLoadout,
|
|
419
660
|
movement: pose,
|
|
661
|
+
possession,
|
|
662
|
+
cosmetics,
|
|
663
|
+
motion,
|
|
420
664
|
},
|
|
421
665
|
item: {
|
|
422
666
|
use: {
|
|
@@ -432,6 +676,8 @@ export function createGameContext(options) {
|
|
|
432
676
|
weapon,
|
|
433
677
|
},
|
|
434
678
|
time,
|
|
679
|
+
camera,
|
|
680
|
+
input,
|
|
435
681
|
subscribe: signal.subscribe,
|
|
436
682
|
version: signal.version,
|
|
437
683
|
};
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import type { LeaderboardScope } from "../game/leaderboard.js";
|
|
2
|
+
import type { NormalizedScenarioReset } from "./persistenceScope.js";
|
|
2
3
|
import type { SaveConfig } from "./save.js";
|
|
3
4
|
import type { GameRuntimeSnapshot, RuntimeChunkRow, RuntimePlayerRow, RuntimeServerRow } from "./snapshot.js";
|
|
4
5
|
export type GameServerStatus = "open" | "running" | "closed";
|
|
6
|
+
export type SessionVisibility = "public" | "private";
|
|
7
|
+
export type SessionAttributes = {
|
|
8
|
+
label?: string;
|
|
9
|
+
mode?: string;
|
|
10
|
+
visibility?: SessionVisibility;
|
|
11
|
+
joinCode?: string;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
};
|
|
5
14
|
export type GameServerRecord = {
|
|
6
15
|
serverId: string;
|
|
7
16
|
gameId: string;
|
|
8
17
|
status: GameServerStatus;
|
|
9
18
|
mode?: string;
|
|
10
19
|
modeConfig?: unknown;
|
|
20
|
+
label?: string;
|
|
21
|
+
visibility?: SessionVisibility;
|
|
22
|
+
joinCode?: string;
|
|
23
|
+
tags?: string[];
|
|
11
24
|
memberUserIds: string[];
|
|
12
25
|
slotsPerServer: number;
|
|
13
26
|
save: SaveConfig;
|
|
@@ -49,6 +62,26 @@ export declare function drainPendingLeaderboardIncrements(session: Record<string
|
|
|
49
62
|
increments: LeaderboardIncrement[];
|
|
50
63
|
session: Record<string, unknown>;
|
|
51
64
|
};
|
|
65
|
+
export declare const LEADERBOARD_TOP_LIMIT = 100;
|
|
66
|
+
export type LeaderboardRow = {
|
|
67
|
+
gameId: string;
|
|
68
|
+
stat: string;
|
|
69
|
+
scope: LeaderboardScope;
|
|
70
|
+
serverId?: string;
|
|
71
|
+
userId: string;
|
|
72
|
+
value: number;
|
|
73
|
+
updatedAt: number;
|
|
74
|
+
};
|
|
75
|
+
export declare function leaderboardRowKey(row: Omit<LeaderboardRow, "value" | "updatedAt">): string;
|
|
76
|
+
export declare function applyLeaderboardRows(rows: Map<string, LeaderboardRow>, gameId: string, entries: LeaderboardIncrement[], now: number): void;
|
|
77
|
+
export declare function topLeaderboardRows(rows: Iterable<LeaderboardRow>, args: {
|
|
78
|
+
gameId: string;
|
|
79
|
+
stat: string;
|
|
80
|
+
scope: LeaderboardScope;
|
|
81
|
+
serverId?: string;
|
|
82
|
+
limit?: number;
|
|
83
|
+
}): LeaderboardEntry[];
|
|
84
|
+
export declare function profileLeaderboardStats(rows: Iterable<LeaderboardRow>, gameId: string, userId: string): Record<string, number>;
|
|
52
85
|
export declare const FEED_RING_LIMIT = 20;
|
|
53
86
|
export declare function trimFeedEntries<T>(entries: T[], limit?: number): T[];
|
|
54
87
|
export declare function shouldAutoSave(save: SaveConfig, dirtyAt: number | undefined, lastSavedAt: number | undefined, now: number): boolean;
|
|
@@ -58,11 +91,17 @@ export type ServerListing = {
|
|
|
58
91
|
memberCount: number;
|
|
59
92
|
slotsPerServer: number;
|
|
60
93
|
mode?: string;
|
|
94
|
+
label?: string;
|
|
95
|
+
visibility?: SessionVisibility;
|
|
96
|
+
joinCode?: string;
|
|
97
|
+
tags?: string[];
|
|
61
98
|
updatedAt: number;
|
|
62
99
|
};
|
|
63
100
|
export declare function toServerListing(record: GameServerRecord): ServerListing;
|
|
101
|
+
export declare function toOpenServerListings(listings: Iterable<ServerListing>, limit?: number): ServerListing[];
|
|
64
102
|
export type HostPersistence = {
|
|
65
103
|
savePlan?: (plan: ServerPersistPlan) => Promise<void>;
|
|
104
|
+
resetScenario?: (reset: NormalizedScenarioReset) => Promise<void>;
|
|
66
105
|
loadServer: (serverId: string) => Promise<GameServerRecord | null>;
|
|
67
106
|
saveServer: (record: GameServerRecord) => Promise<void>;
|
|
68
107
|
listServers: (gameId: string) => Promise<GameServerRecord[]>;
|
|
@@ -20,6 +20,50 @@ export function drainPendingLeaderboardIncrements(session) {
|
|
|
20
20
|
delete rest[LEADERBOARD_PENDING_KEY];
|
|
21
21
|
return { increments: raw.filter(isLeaderboardIncrement), session: rest };
|
|
22
22
|
}
|
|
23
|
+
export const LEADERBOARD_TOP_LIMIT = 100;
|
|
24
|
+
export function leaderboardRowKey(row) {
|
|
25
|
+
return [row.gameId, row.scope, row.stat, row.serverId ?? "", row.userId].join("|");
|
|
26
|
+
}
|
|
27
|
+
export function applyLeaderboardRows(rows, gameId, entries, now) {
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
const key = leaderboardRowKey({ ...entry, gameId });
|
|
30
|
+
const existing = rows.get(key);
|
|
31
|
+
if (existing) {
|
|
32
|
+
rows.set(key, { ...existing, value: existing.value + entry.by, updatedAt: now });
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
rows.set(key, {
|
|
36
|
+
gameId,
|
|
37
|
+
stat: entry.stat,
|
|
38
|
+
scope: entry.scope,
|
|
39
|
+
serverId: entry.serverId,
|
|
40
|
+
userId: entry.userId,
|
|
41
|
+
value: entry.by,
|
|
42
|
+
updatedAt: now,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function topLeaderboardRows(rows, args) {
|
|
48
|
+
const limit = Math.min(Math.max(args.limit ?? LEADERBOARD_TOP_LIMIT, 1), LEADERBOARD_TOP_LIMIT);
|
|
49
|
+
return [...rows]
|
|
50
|
+
.filter((row) => row.gameId === args.gameId &&
|
|
51
|
+
row.stat === args.stat &&
|
|
52
|
+
row.scope === args.scope &&
|
|
53
|
+
(args.serverId === undefined || row.serverId === args.serverId))
|
|
54
|
+
.sort((a, b) => b.value - a.value)
|
|
55
|
+
.slice(0, limit)
|
|
56
|
+
.map((row) => ({ userId: row.userId, value: row.value }));
|
|
57
|
+
}
|
|
58
|
+
export function profileLeaderboardStats(rows, gameId, userId) {
|
|
59
|
+
const profile = {};
|
|
60
|
+
for (const row of rows) {
|
|
61
|
+
if (row.gameId === gameId && row.userId === userId && row.scope === "profile") {
|
|
62
|
+
profile[row.stat] = row.value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return profile;
|
|
66
|
+
}
|
|
23
67
|
export const FEED_RING_LIMIT = 20;
|
|
24
68
|
export function trimFeedEntries(entries, limit = FEED_RING_LIMIT) {
|
|
25
69
|
if (entries.length <= limit)
|
|
@@ -42,9 +86,19 @@ export function toServerListing(record) {
|
|
|
42
86
|
memberCount: record.memberUserIds.length,
|
|
43
87
|
slotsPerServer: record.slotsPerServer,
|
|
44
88
|
mode: record.mode,
|
|
89
|
+
label: record.label,
|
|
90
|
+
visibility: record.visibility,
|
|
91
|
+
joinCode: record.joinCode,
|
|
92
|
+
tags: record.tags,
|
|
45
93
|
updatedAt: record.updatedAt,
|
|
46
94
|
};
|
|
47
95
|
}
|
|
96
|
+
export function toOpenServerListings(listings, limit = 20) {
|
|
97
|
+
return [...listings]
|
|
98
|
+
.filter((listing) => listing.status === "running")
|
|
99
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
100
|
+
.slice(0, limit);
|
|
101
|
+
}
|
|
48
102
|
export function buildHydratePlayers(server, profiles) {
|
|
49
103
|
const players = {};
|
|
50
104
|
for (const userId of server.memberUserIds) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface InputSnapshot {
|
|
2
|
+
/** Replaces the held-action set for this frame. Called by the shell before `onTick` each frame; does not bump `ctx.version()` or notify `ctx.subscribe` listeners — per-frame publishes would storm subscribers. */
|
|
3
|
+
publish(held: readonly string[]): void;
|
|
4
|
+
isDown(action: string): boolean;
|
|
5
|
+
held(): readonly string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function createInputSnapshot(): InputSnapshot;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seam for game code to reach the vertical motion the shell's FrameDriver
|
|
3
|
+
* otherwise owns privately (#162.4). Game code calls `impulse`,
|
|
4
|
+
* `setVerticalVelocity`, and/or `setY` from `onTick` or commands; the shell
|
|
5
|
+
* calls `takePending()` once per frame, before integrating gravity, to drain
|
|
6
|
+
* what accumulated. `setY` wins over physics for that frame; impulses add to
|
|
7
|
+
* the vertical velocity the driver is about to integrate; a later
|
|
8
|
+
* `setVerticalVelocity` replaces that velocity outright.
|
|
9
|
+
*
|
|
10
|
+
* Intentionally not `ctx.version()`/`ctx.subscribe` wired: these fire at
|
|
11
|
+
* most once per frame and would otherwise storm subscribers with per-frame
|
|
12
|
+
* churn (same rationale as `InputSnapshot.publish`).
|
|
13
|
+
*/
|
|
14
|
+
export interface MotionIntents {
|
|
15
|
+
impulse(velocityY: number): void;
|
|
16
|
+
setVerticalVelocity(velocityY: number): void;
|
|
17
|
+
setY(y: number): void;
|
|
18
|
+
takePending(): MotionIntentBatch | null;
|
|
19
|
+
}
|
|
20
|
+
export interface MotionIntentBatch {
|
|
21
|
+
impulses: readonly number[];
|
|
22
|
+
verticalVelocity: number | null;
|
|
23
|
+
y: number | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function createMotionIntents(): MotionIntents;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function createMotionIntents() {
|
|
2
|
+
let impulses = [];
|
|
3
|
+
let verticalVelocity = null;
|
|
4
|
+
let y = null;
|
|
5
|
+
return {
|
|
6
|
+
impulse(velocityY) {
|
|
7
|
+
impulses.push(velocityY);
|
|
8
|
+
},
|
|
9
|
+
setVerticalVelocity(velocityY) {
|
|
10
|
+
verticalVelocity = velocityY;
|
|
11
|
+
},
|
|
12
|
+
setY(nextY) {
|
|
13
|
+
y = nextY;
|
|
14
|
+
},
|
|
15
|
+
takePending() {
|
|
16
|
+
if (impulses.length === 0 && verticalVelocity === null && y === null)
|
|
17
|
+
return null;
|
|
18
|
+
const batch = { impulses, verticalVelocity, y };
|
|
19
|
+
impulses = [];
|
|
20
|
+
verticalVelocity = null;
|
|
21
|
+
y = null;
|
|
22
|
+
return batch;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PlayerProfileRecord } from "./hostPersistence.js";
|
|
2
|
+
import type { RuntimePlayerRow } from "./snapshot.js";
|
|
3
|
+
export type PersistenceScope = "run" | "meta";
|
|
4
|
+
export interface ScopeSchema {
|
|
5
|
+
run: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
export interface ScopedState<T = Record<string, unknown>> {
|
|
8
|
+
meta: Partial<T>;
|
|
9
|
+
run: Partial<T>;
|
|
10
|
+
}
|
|
11
|
+
export declare function partitionScopes<T extends Record<string, unknown>>(state: T, schema: ScopeSchema): ScopedState<T>;
|
|
12
|
+
export declare function resetRun<T extends Record<string, unknown>>(scoped: ScopedState<T>): ScopedState<T>;
|
|
13
|
+
export declare function mergeScopes<T extends Record<string, unknown>>(scoped: ScopedState<T>): Partial<T>;
|
|
14
|
+
export declare function clearRunFields(player: RuntimePlayerRow, runFields: readonly string[]): RuntimePlayerRow;
|
|
15
|
+
export declare function applyRunReset(profile: PlayerProfileRecord, runFields: readonly string[], now: number): PlayerProfileRecord;
|
|
16
|
+
export interface ScenarioReset {
|
|
17
|
+
gameId: string;
|
|
18
|
+
serverId?: string;
|
|
19
|
+
wipeChunks?: boolean;
|
|
20
|
+
wipeServerSession?: boolean;
|
|
21
|
+
resetPlayers?: PersistenceScope | "none";
|
|
22
|
+
runFields?: readonly string[];
|
|
23
|
+
}
|
|
24
|
+
export interface NormalizedScenarioReset {
|
|
25
|
+
gameId: string;
|
|
26
|
+
serverId: string | null;
|
|
27
|
+
wipeChunks: boolean;
|
|
28
|
+
wipeServerSession: boolean;
|
|
29
|
+
resetPlayers: "run" | "none";
|
|
30
|
+
runFields: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
export declare function planScenarioReset(reset: ScenarioReset): NormalizedScenarioReset;
|