@jgengine/core 0.7.0 → 0.9.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 +63 -0
- package/README.md +2 -0
- package/dist/ai/flock.d.ts +36 -0
- package/dist/ai/flock.js +182 -0
- package/dist/ai/groupAssist.d.ts +19 -0
- package/dist/ai/groupAssist.js +59 -0
- package/dist/ai/laneSelect.d.ts +21 -0
- package/dist/ai/laneSelect.js +37 -0
- package/dist/ai/mobBrain.d.ts +68 -0
- package/dist/ai/mobBrain.js +115 -0
- package/dist/ai/spawnDirector.d.ts +5 -0
- package/dist/ai/spawnDirector.js +14 -2
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -0
- package/dist/anim/easing.d.ts +14 -0
- package/dist/anim/easing.js +55 -0
- package/dist/behaviour/behaviour.d.ts +136 -0
- package/dist/behaviour/behaviour.js +388 -0
- package/dist/cards/cardPile.d.ts +1 -0
- package/dist/cards/cardPile.js +12 -3
- package/dist/cartridge/runtime.d.ts +15 -0
- package/dist/cartridge/runtime.js +556 -0
- package/dist/cartridge/spec.d.ts +188 -0
- package/dist/cartridge/spec.js +19 -0
- package/dist/cartridge/validate.d.ts +2 -0
- package/dist/cartridge/validate.js +78 -0
- package/dist/combat/abilityKit.d.ts +22 -2
- package/dist/combat/abilityKit.js +72 -13
- package/dist/combat/castRunner.d.ts +40 -0
- package/dist/combat/castRunner.js +56 -0
- package/dist/combat/comboPoints.d.ts +14 -0
- package/dist/combat/comboPoints.js +52 -0
- package/dist/combat/defensiveWindow.d.ts +1 -1
- package/dist/combat/defensiveWindow.js +5 -3
- package/dist/combat/effects.d.ts +1 -1
- package/dist/combat/effects.js +9 -6
- package/dist/combat/projectiles.d.ts +64 -6
- package/dist/combat/projectiles.js +232 -74
- package/dist/combat/resistance.d.ts +16 -4
- package/dist/combat/resistance.js +43 -7
- package/dist/combat/resourcePool.d.ts +20 -0
- package/dist/combat/resourcePool.js +55 -0
- package/dist/combat/shotOrigin.d.ts +40 -0
- package/dist/combat/shotOrigin.js +57 -0
- package/dist/combat/telegraph.d.ts +30 -1
- package/dist/combat/telegraph.js +61 -0
- package/dist/commands/commandRegistry.d.ts +1 -1
- package/dist/commands/commandRegistry.js +2 -1
- package/dist/crafting/recipe.js +4 -1
- 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 +205 -0
- package/dist/devtools/devtools.js +832 -0
- package/dist/devtools/transformTunables.d.ts +14 -0
- package/dist/devtools/transformTunables.js +38 -0
- package/dist/devtools/tunableSchema.d.ts +140 -0
- package/dist/devtools/tunableSchema.js +476 -0
- package/dist/faction/factions.d.ts +33 -0
- package/dist/faction/factions.js +76 -0
- package/dist/faction/reputation.d.ts +32 -0
- package/dist/faction/reputation.js +82 -0
- package/dist/game/chat.d.ts +68 -0
- package/dist/game/chat.js +173 -0
- package/dist/game/chatFilter.d.ts +15 -0
- package/dist/game/chatFilter.js +55 -0
- package/dist/game/controlGate.d.ts +4 -0
- package/dist/game/controlGate.js +7 -0
- package/dist/game/defineGame.d.ts +10 -2
- package/dist/game/defineGame.js +2 -1
- package/dist/game/events.d.ts +35 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/levelSequence.d.ts +44 -0
- package/dist/game/levelSequence.js +78 -0
- package/dist/game/lootTable.d.ts +10 -1
- package/dist/game/lootTable.js +27 -7
- package/dist/game/playableGame.d.ts +286 -6
- 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/quest.d.ts +7 -3
- package/dist/game/quest.js +17 -8
- package/dist/game/race.d.ts +44 -1
- package/dist/game/race.js +116 -11
- package/dist/game/recordBook.d.ts +29 -0
- package/dist/game/recordBook.js +86 -0
- package/dist/game/social.d.ts +42 -0
- package/dist/game/social.js +122 -0
- package/dist/game/spawnPoints.d.ts +19 -0
- package/dist/game/spawnPoints.js +23 -0
- package/dist/game/talents.d.ts +47 -0
- package/dist/game/talents.js +140 -0
- package/dist/game/worldItem.d.ts +3 -0
- package/dist/game/worldItem.js +12 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/input/actionBindings.d.ts +10 -0
- package/dist/input/actionBindings.js +14 -0
- package/dist/input/axisInput.d.ts +26 -3
- package/dist/input/axisInput.js +44 -4
- package/dist/input/bindingOverrides.d.ts +25 -0
- package/dist/input/bindingOverrides.js +95 -0
- package/dist/input/gestureSurface.d.ts +31 -0
- package/dist/input/gestureSurface.js +120 -0
- package/dist/input/pointer.d.ts +45 -0
- package/dist/input/pointer.js +48 -0
- package/dist/input/pointerAxis.d.ts +30 -0
- package/dist/input/pointerAxis.js +31 -0
- package/dist/input/touchScheme.d.ts +89 -0
- package/dist/input/touchScheme.js +153 -0
- package/dist/item/affix.d.ts +1 -1
- package/dist/item/affix.js +1 -19
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +59 -1
- package/dist/movement/dash.d.ts +3 -0
- package/dist/movement/dash.js +19 -5
- package/dist/movement/glideModel.d.ts +44 -0
- package/dist/movement/glideModel.js +54 -0
- package/dist/movement/grappleSwing.d.ts +34 -0
- package/dist/movement/grappleSwing.js +56 -0
- package/dist/movement/leaderTrail.d.ts +29 -0
- package/dist/movement/leaderTrail.js +67 -0
- package/dist/movement/movementModel.d.ts +36 -3
- package/dist/movement/movementModel.js +81 -5
- package/dist/movement/steering.d.ts +27 -0
- package/dist/movement/steering.js +40 -0
- 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/feedWriteGate.d.ts +10 -0
- package/dist/multiplayer/feedWriteGate.js +12 -0
- package/dist/multiplayer/identity.d.ts +13 -0
- package/dist/multiplayer/identity.js +20 -0
- package/dist/multiplayer/poseSyncGate.d.ts +1 -0
- package/dist/multiplayer/poseSyncGate.js +13 -1
- package/dist/multiplayer/presenceModel.d.ts +17 -2
- package/dist/multiplayer/presenceModel.js +30 -4
- package/dist/multiplayer/voiceContract.d.ts +44 -0
- package/dist/multiplayer/voiceContract.js +108 -0
- package/dist/nav/corridors.d.ts +30 -0
- package/dist/nav/corridors.js +69 -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 +10 -0
- package/dist/nav/navGrid.js +48 -5
- package/dist/nav/pathFollow.d.ts +4 -2
- package/dist/nav/pathFollow.js +5 -3
- package/dist/nav/railGraph.d.ts +66 -0
- package/dist/nav/railGraph.js +129 -0
- package/dist/nav/timetable.d.ts +41 -0
- package/dist/nav/timetable.js +94 -0
- package/dist/physics/ballisticSweep.d.ts +18 -0
- package/dist/physics/ballisticSweep.js +49 -0
- package/dist/physics/buoyancy.d.ts +6 -0
- package/dist/physics/buoyancy.js +14 -0
- package/dist/physics/flowTube.d.ts +32 -0
- package/dist/physics/flowTube.js +64 -0
- package/dist/physics/forceVolume.d.ts +27 -0
- package/dist/physics/forceVolume.js +43 -5
- package/dist/physics/kinematicVehicle.d.ts +52 -0
- package/dist/physics/kinematicVehicle.js +81 -0
- package/dist/physics/physicsWorld.d.ts +54 -8
- package/dist/physics/physicsWorld.js +400 -79
- package/dist/physics/ragdoll.js +1 -1
- package/dist/physics/spatialGrid.d.ts +1 -1
- package/dist/physics/spatialGrid.js +5 -0
- package/dist/physics/vehicleBody.d.ts +6 -0
- package/dist/physics/vehicleBody.js +18 -18
- 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/random/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- package/dist/runtime/adapter.d.ts +35 -0
- package/dist/runtime/adapter.js +37 -1
- package/dist/runtime/cameraDirector.d.ts +15 -0
- package/dist/runtime/cameraDirector.js +31 -0
- package/dist/runtime/commandRunner.d.ts +2 -2
- package/dist/runtime/commandRunner.js +2 -2
- package/dist/runtime/gameContext.d.ts +65 -2
- package/dist/runtime/gameContext.js +280 -9
- package/dist/runtime/gameRuntime.d.ts +5 -1
- package/dist/runtime/gameRuntime.js +13 -12
- package/dist/runtime/hostPersistence.d.ts +30 -1
- package/dist/runtime/hostPersistence.js +70 -5
- package/dist/runtime/inputSnapshot.d.ts +12 -0
- package/dist/runtime/inputSnapshot.js +17 -0
- package/dist/runtime/motionIntents.d.ts +33 -0
- package/dist/runtime/motionIntents.js +43 -0
- package/dist/runtime/snapshot.d.ts +1 -0
- package/dist/runtime/snapshot.js +1 -1
- package/dist/runtime/transport.d.ts +32 -0
- package/dist/runtime/visibility.d.ts +74 -0
- package/dist/runtime/visibility.js +127 -0
- package/dist/scene/colliders.d.ts +44 -0
- package/dist/scene/colliders.js +77 -0
- package/dist/scene/entityStore.d.ts +30 -5
- package/dist/scene/entityStore.js +83 -13
- package/dist/scene/movementSpeed.d.ts +35 -0
- package/dist/scene/movementSpeed.js +20 -0
- package/dist/scene/objectQuery.d.ts +29 -0
- package/dist/scene/objectQuery.js +140 -0
- package/dist/scene/objectStore.d.ts +13 -0
- package/dist/scene/objectStore.js +130 -6
- package/dist/scene/paintLayer.d.ts +15 -0
- package/dist/scene/paintLayer.js +53 -0
- package/dist/scene/sceneRaycast.d.ts +80 -0
- package/dist/scene/sceneRaycast.js +284 -0
- package/dist/scene/spatial.d.ts +17 -1
- package/dist/scene/spatial.js +140 -17
- 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/recordingBuffer.d.ts +6 -0
- package/dist/sensor/recordingBuffer.js +7 -0
- package/dist/sensor/replayLoop.d.ts +39 -0
- package/dist/sensor/replayLoop.js +64 -0
- package/dist/sensor/visionCone.d.ts +31 -0
- package/dist/sensor/visionCone.js +72 -0
- package/dist/session/extraction.d.ts +2 -2
- package/dist/session/extraction.js +13 -2
- package/dist/session/roles.d.ts +6 -0
- package/dist/session/roles.js +32 -0
- package/dist/session/roundState.d.ts +41 -17
- package/dist/session/roundState.js +67 -38
- package/dist/settings/settingsModel.d.ts +97 -0
- package/dist/settings/settingsModel.js +100 -0
- package/dist/stats/statModifiers.d.ts +5 -2
- package/dist/stats/statModifiers.js +32 -14
- package/dist/store/observableKeyedStore.js +8 -2
- package/dist/survival/decayMeter.d.ts +1 -1
- package/dist/survival/decayMeter.js +0 -1
- package/dist/tactics/fallingGrid.d.ts +62 -0
- package/dist/tactics/fallingGrid.js +158 -0
- package/dist/tactics/tacticalGrid.d.ts +11 -0
- package/dist/tactics/tacticalGrid.js +17 -0
- package/dist/time/beatClock.d.ts +18 -0
- package/dist/time/beatClock.js +24 -0
- package/dist/time/idleProgress.d.ts +29 -0
- package/dist/time/idleProgress.js +31 -0
- package/dist/time/serverTick.d.ts +7 -1
- package/dist/time/serverTick.js +27 -4
- package/dist/time/simClock.d.ts +25 -1
- package/dist/time/simClock.js +28 -3
- package/dist/time/stateSchedule.d.ts +57 -0
- package/dist/time/stateSchedule.js +121 -0
- package/dist/turn/commit.d.ts +2 -2
- package/dist/turn/commit.js +2 -2
- package/dist/turn/intent.d.ts +14 -0
- package/dist/turn/intent.js +29 -0
- package/dist/turn/turnLoop.d.ts +2 -0
- package/dist/turn/turnLoop.js +8 -1
- package/dist/ui/hudLayout.d.ts +58 -0
- package/dist/ui/hudLayout.js +207 -0
- package/dist/ui/hudScale.d.ts +44 -0
- package/dist/ui/hudScale.js +55 -0
- package/dist/visibility/assetStreaming.d.ts +66 -0
- package/dist/visibility/assetStreaming.js +201 -0
- package/dist/visibility/bounds.d.ts +66 -0
- package/dist/visibility/bounds.js +113 -0
- package/dist/visibility/camera.d.ts +21 -0
- package/dist/visibility/camera.js +3 -0
- package/dist/visibility/config.d.ts +21 -0
- package/dist/visibility/config.js +1 -0
- package/dist/visibility/diagnostics.d.ts +26 -0
- package/dist/visibility/diagnostics.js +36 -0
- package/dist/visibility/distance.d.ts +11 -0
- package/dist/visibility/distance.js +30 -0
- package/dist/visibility/frustum.d.ts +47 -0
- package/dist/visibility/frustum.js +186 -0
- package/dist/visibility/occlusion.d.ts +36 -0
- package/dist/visibility/occlusion.js +45 -0
- package/dist/visibility/settings.d.ts +75 -0
- package/dist/visibility/settings.js +49 -0
- package/dist/visibility/simulationCulling.d.ts +34 -0
- package/dist/visibility/simulationCulling.js +36 -0
- package/dist/visibility/spatialIndex.d.ts +45 -0
- package/dist/visibility/spatialIndex.js +198 -0
- package/dist/visibility/visibilitySystem.d.ts +90 -0
- package/dist/visibility/visibilitySystem.js +257 -0
- package/dist/world/buildings.d.ts +6 -0
- package/dist/world/buildings.js +152 -0
- package/dist/world/carve.d.ts +7 -2
- package/dist/world/carve.js +17 -7
- package/dist/world/cellStates.d.ts +45 -0
- package/dist/world/cellStates.js +103 -0
- package/dist/world/environmentSummary.d.ts +61 -0
- package/dist/world/environmentSummary.js +138 -0
- package/dist/world/features.d.ts +138 -9
- package/dist/world/features.js +73 -2
- package/dist/world/gridInstances.d.ts +12 -0
- package/dist/world/gridInstances.js +160 -0
- package/dist/world/lod.d.ts +17 -0
- package/dist/world/lod.js +66 -0
- package/dist/world/mapLayers.d.ts +60 -0
- package/dist/world/mapLayers.js +44 -0
- package/dist/world/massing.d.ts +45 -0
- package/dist/world/massing.js +581 -0
- package/dist/world/minimap.d.ts +13 -1
- package/dist/world/minimap.js +21 -2
- package/dist/world/placementController.d.ts +22 -1
- package/dist/world/placementController.js +56 -0
- package/dist/world/terrain.d.ts +62 -1
- package/dist/world/terrain.js +253 -9
- package/dist/world/voxelField.d.ts +54 -0
- package/dist/world/voxelField.js +241 -0
- package/dist/world/water.d.ts +7 -0
- package/dist/world/water.js +19 -4
- package/dist/world/weather.d.ts +3 -18
- package/dist/world/weather.js +4 -17
- package/dist/world/windZones.d.ts +47 -0
- package/dist/world/windZones.js +59 -0
- package/llms.txt +4983 -0
- package/package.json +14 -3
package/dist/game/progression.js
CHANGED
|
@@ -60,8 +60,13 @@ export function leveling(config) {
|
|
|
60
60
|
const startLevel = config.startLevel ?? 1;
|
|
61
61
|
const xpStat = config.xpStat ?? "xp";
|
|
62
62
|
const levelStat = config.levelStat ?? "level";
|
|
63
|
-
const
|
|
64
|
-
const
|
|
63
|
+
const thresholdMode = config.thresholdMode ?? "perLevel";
|
|
64
|
+
const xpForLevel = (level) => {
|
|
65
|
+
if (thresholdMode === "cumulative" && level <= startLevel)
|
|
66
|
+
return 0;
|
|
67
|
+
return evalCurve(config.xpForLevel, level);
|
|
68
|
+
};
|
|
69
|
+
const resolvePerLevel = (level, xp) => {
|
|
65
70
|
let currentLevel = level;
|
|
66
71
|
let currentXp = xp;
|
|
67
72
|
let levelsGained = 0;
|
|
@@ -79,6 +84,22 @@ export function leveling(config) {
|
|
|
79
84
|
levelsGained,
|
|
80
85
|
};
|
|
81
86
|
};
|
|
87
|
+
const resolveCumulative = (level, xp) => {
|
|
88
|
+
let currentLevel = level;
|
|
89
|
+
let levelsGained = 0;
|
|
90
|
+
while (currentLevel < maxLevel && xp >= xpForLevel(currentLevel + 1)) {
|
|
91
|
+
currentLevel += 1;
|
|
92
|
+
levelsGained += 1;
|
|
93
|
+
}
|
|
94
|
+
const capped = currentLevel >= maxLevel;
|
|
95
|
+
return {
|
|
96
|
+
level: currentLevel,
|
|
97
|
+
xp: capped ? xpForLevel(maxLevel) : xp,
|
|
98
|
+
xpMax: capped ? xpForLevel(maxLevel) : xpForLevel(currentLevel + 1),
|
|
99
|
+
levelsGained,
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
const resolve = (level, xp) => thresholdMode === "cumulative" ? resolveCumulative(level, xp) : resolvePerLevel(level, xp);
|
|
82
103
|
const grantXp = (access, userId, amount, onLevelUp) => {
|
|
83
104
|
const xp = access.get(userId, xpStat);
|
|
84
105
|
const level = access.get(userId, levelStat);
|
package/dist/game/quest.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export interface QuestJournalDeps {
|
|
|
65
65
|
partyMembersNear?(userId: string, radius: number): string[];
|
|
66
66
|
}
|
|
67
67
|
export interface QuestJournal {
|
|
68
|
-
register(catalog: QuestDef[] | Record<string, QuestDef>): void;
|
|
68
|
+
register(catalog: readonly QuestDef[] | Record<string, QuestDef>): void;
|
|
69
69
|
has(questId: string): boolean;
|
|
70
70
|
canAccept(userId: string, questId: string): {
|
|
71
71
|
reason: string;
|
|
@@ -126,7 +126,11 @@ export interface QuestEvaluator {
|
|
|
126
126
|
export declare function applyQuestRewards(rewards: QuestRewards, appliers: {
|
|
127
127
|
grantXp?(amount: number): void;
|
|
128
128
|
grantEconomy?(currencyId: string, amount: number): void;
|
|
129
|
-
grantItem?(inventoryId: string, itemId: string, count: number):
|
|
129
|
+
grantItem?(inventoryId: string, itemId: string, count: number): {
|
|
130
|
+
reason: string;
|
|
131
|
+
} | null | void;
|
|
130
132
|
grantUnlock?(unlockId: string): void;
|
|
131
|
-
}):
|
|
133
|
+
}): {
|
|
134
|
+
reason: string;
|
|
135
|
+
} | null;
|
|
132
136
|
export declare function createQuestEvaluator(defs: QuestDef[] | Record<string, QuestDef>): QuestEvaluator;
|
package/dist/game/quest.js
CHANGED
|
@@ -72,17 +72,20 @@ export function createQuestJournal(deps) {
|
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
74
|
function applyRewards(userId, rewards) {
|
|
75
|
+
for (const entry of rewards.items ?? []) {
|
|
76
|
+
const fail = deps.rewards.grantItem(userId, entry.inventory, entry.item, entry.count);
|
|
77
|
+
if (fail !== null)
|
|
78
|
+
return fail;
|
|
79
|
+
}
|
|
75
80
|
if (rewards.xp)
|
|
76
81
|
deps.rewards.grantXp(userId, rewards.xp.amount);
|
|
77
82
|
for (const [currencyId, amount] of Object.entries(rewards.economy ?? {})) {
|
|
78
83
|
deps.rewards.grantEconomy(userId, currencyId, amount);
|
|
79
84
|
}
|
|
80
|
-
for (const entry of rewards.items ?? []) {
|
|
81
|
-
deps.rewards.grantItem(userId, entry.inventory, entry.item, entry.count);
|
|
82
|
-
}
|
|
83
85
|
for (const unlockId of rewards.unlocks ?? []) {
|
|
84
86
|
deps.rewards.grantUnlock(userId, unlockId);
|
|
85
87
|
}
|
|
88
|
+
return null;
|
|
86
89
|
}
|
|
87
90
|
function turnIn(userId, questId) {
|
|
88
91
|
const denied = canTurnIn(userId, questId);
|
|
@@ -90,9 +93,12 @@ export function createQuestJournal(deps) {
|
|
|
90
93
|
return denied;
|
|
91
94
|
const def = catalog.get(questId);
|
|
92
95
|
const state = users.get(userId).get(questId);
|
|
96
|
+
if (def.rewards) {
|
|
97
|
+
const fail = applyRewards(userId, def.rewards);
|
|
98
|
+
if (fail !== null)
|
|
99
|
+
return fail;
|
|
100
|
+
}
|
|
93
101
|
state.status = "completed";
|
|
94
|
-
if (def.rewards)
|
|
95
|
-
applyRewards(userId, def.rewards);
|
|
96
102
|
deps.events.emit("quest.completed", { userId, questId });
|
|
97
103
|
for (const nextQuestId of def.rewards?.quests ?? []) {
|
|
98
104
|
if (canAccept(userId, nextQuestId) === null)
|
|
@@ -227,17 +233,20 @@ export function createQuestJournal(deps) {
|
|
|
227
233
|
};
|
|
228
234
|
}
|
|
229
235
|
export function applyQuestRewards(rewards, appliers) {
|
|
236
|
+
for (const entry of rewards.items ?? []) {
|
|
237
|
+
const fail = appliers.grantItem?.(entry.inventory, entry.item, entry.count);
|
|
238
|
+
if (fail != null && typeof fail === "object" && "reason" in fail)
|
|
239
|
+
return fail;
|
|
240
|
+
}
|
|
230
241
|
if (rewards.xp)
|
|
231
242
|
appliers.grantXp?.(rewards.xp.amount);
|
|
232
243
|
for (const [currencyId, amount] of Object.entries(rewards.economy ?? {})) {
|
|
233
244
|
appliers.grantEconomy?.(currencyId, amount);
|
|
234
245
|
}
|
|
235
|
-
for (const entry of rewards.items ?? []) {
|
|
236
|
-
appliers.grantItem?.(entry.inventory, entry.item, entry.count);
|
|
237
|
-
}
|
|
238
246
|
for (const unlockId of rewards.unlocks ?? []) {
|
|
239
247
|
appliers.grantUnlock?.(unlockId);
|
|
240
248
|
}
|
|
249
|
+
return null;
|
|
241
250
|
}
|
|
242
251
|
function toWorking(state) {
|
|
243
252
|
const working = new Map();
|
package/dist/game/race.d.ts
CHANGED
|
@@ -3,18 +3,36 @@ export interface Checkpoint {
|
|
|
3
3
|
center: readonly [number, number, number];
|
|
4
4
|
half: readonly [number, number, number];
|
|
5
5
|
}
|
|
6
|
+
export interface RaceForkRoute {
|
|
7
|
+
id: string;
|
|
8
|
+
checkpoints: readonly Checkpoint[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* An alternate-route section (#286.3): after passing mainline checkpoint `afterIndex`, a racer commits
|
|
12
|
+
* to whichever route's first checkpoint they hit, runs its checkpoints in order, and rejoins at
|
|
13
|
+
* mainline `afterIndex + 1`. Every completed route contributes exactly one checkpoint of `progress`
|
|
14
|
+
* regardless of length, and each hit records a split — so route time accounting comes for free.
|
|
15
|
+
*/
|
|
16
|
+
export interface RaceFork {
|
|
17
|
+
id: string;
|
|
18
|
+
/** Mainline checkpoint index the fork opens after; must be before the finish line. */
|
|
19
|
+
afterIndex: number;
|
|
20
|
+
routes: readonly RaceForkRoute[];
|
|
21
|
+
}
|
|
6
22
|
export interface RaceTrackConfig {
|
|
7
23
|
checkpoints: readonly Checkpoint[];
|
|
8
24
|
laps?: number;
|
|
25
|
+
forks?: readonly RaceFork[];
|
|
9
26
|
}
|
|
10
27
|
export interface RaceTrack {
|
|
11
28
|
readonly checkpoints: readonly Checkpoint[];
|
|
12
29
|
readonly laps: number;
|
|
30
|
+
readonly forks: readonly RaceFork[];
|
|
13
31
|
}
|
|
14
32
|
/**
|
|
15
33
|
* A race track is an ordered ring of checkpoint trigger volumes plus a lap count. The final checkpoint
|
|
16
34
|
* is the lap/finish line: a racer completes a lap by passing all checkpoints in order and hitting the
|
|
17
|
-
* last one.
|
|
35
|
+
* last one. `forks` splice alternate route segments between mainline checkpoints.
|
|
18
36
|
*/
|
|
19
37
|
export declare function raceTrack(config: RaceTrackConfig): RaceTrack;
|
|
20
38
|
export interface RacerProgress {
|
|
@@ -28,6 +46,13 @@ export interface RacerProgress {
|
|
|
28
46
|
finishTime: number | null;
|
|
29
47
|
eliminated: boolean;
|
|
30
48
|
splits: readonly number[];
|
|
49
|
+
/** Fork id → route id last taken through it. */
|
|
50
|
+
routesTaken: Readonly<Record<string, string>>;
|
|
51
|
+
/** Route the racer is currently inside, or `null` on the mainline. */
|
|
52
|
+
activeRoute: {
|
|
53
|
+
forkId: string;
|
|
54
|
+
routeId: string;
|
|
55
|
+
} | null;
|
|
31
56
|
}
|
|
32
57
|
export type RaceEvent = {
|
|
33
58
|
type: "checkpoint.hit";
|
|
@@ -35,6 +60,18 @@ export type RaceEvent = {
|
|
|
35
60
|
checkpoint: number;
|
|
36
61
|
lap: number;
|
|
37
62
|
time: number;
|
|
63
|
+
/** Set when the hit is a fork-route checkpoint (`checkpoint` is then the fork's `afterIndex`). */
|
|
64
|
+
fork?: {
|
|
65
|
+
forkId: string;
|
|
66
|
+
routeId: string;
|
|
67
|
+
index: number;
|
|
68
|
+
};
|
|
69
|
+
} | {
|
|
70
|
+
type: "fork.taken";
|
|
71
|
+
racerId: string;
|
|
72
|
+
forkId: string;
|
|
73
|
+
routeId: string;
|
|
74
|
+
time: number;
|
|
38
75
|
} | {
|
|
39
76
|
type: "lap.completed";
|
|
40
77
|
racerId: string;
|
|
@@ -69,6 +106,7 @@ export interface RaceStateConfig {
|
|
|
69
106
|
* volumes. It emits `checkpoint.hit` / `lap.completed` / `position.changed` / `race.finished`, keeps
|
|
70
107
|
* cumulative split times for PB deltas, resolves a pluggable win condition (first-past-post, round-cut,
|
|
71
108
|
* derby last-standing), and `resetToCheckpoint` hands back a respawn pose at the racer's last checkpoint.
|
|
109
|
+
* `removeRacer` drops a racer mid-race and `reset` returns the whole instance to its pre-race state for reuse.
|
|
72
110
|
*/
|
|
73
111
|
export declare class RaceState {
|
|
74
112
|
private readonly track;
|
|
@@ -79,12 +117,17 @@ export declare class RaceState {
|
|
|
79
117
|
private finalRanking;
|
|
80
118
|
constructor(config: RaceStateConfig);
|
|
81
119
|
addRacer(racerId: string, startTime?: number): void;
|
|
120
|
+
/** Removes a racer's progress/standing and renumbers the remaining field's positions; a no-op for an unknown id. */
|
|
121
|
+
removeRacer(racerId: string): void;
|
|
122
|
+
/** Clears all racer progress and finish state back to construction time; `track` and `win` are untouched, so the same instance can run another race. */
|
|
123
|
+
reset(): void;
|
|
82
124
|
eliminate(racerId: string): void;
|
|
83
125
|
get finished(): boolean;
|
|
84
126
|
get ranking(): readonly string[];
|
|
85
127
|
progressOf(racerId: string): RacerProgress | null;
|
|
86
128
|
private snapshot;
|
|
87
129
|
standings(): readonly RacerProgress[];
|
|
130
|
+
private expectedCheckpoint;
|
|
88
131
|
resetToCheckpoint(racerId: string): {
|
|
89
132
|
position: [number, number, number];
|
|
90
133
|
heading: number;
|
package/dist/game/race.js
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A race track is an ordered ring of checkpoint trigger volumes plus a lap count. The final checkpoint
|
|
3
3
|
* is the lap/finish line: a racer completes a lap by passing all checkpoints in order and hitting the
|
|
4
|
-
* last one.
|
|
4
|
+
* last one. `forks` splice alternate route segments between mainline checkpoints.
|
|
5
5
|
*/
|
|
6
6
|
export function raceTrack(config) {
|
|
7
|
-
|
|
7
|
+
const checkpoints = config.checkpoints;
|
|
8
|
+
const forks = config.forks ?? [];
|
|
9
|
+
for (const fork of forks) {
|
|
10
|
+
if (fork.afterIndex < 0 || fork.afterIndex >= checkpoints.length - 1) {
|
|
11
|
+
throw new Error(`race fork "${fork.id}" afterIndex ${fork.afterIndex} must sit before the finish line`);
|
|
12
|
+
}
|
|
13
|
+
if (fork.routes.length < 2)
|
|
14
|
+
throw new Error(`race fork "${fork.id}" needs at least two routes`);
|
|
15
|
+
for (const route of fork.routes) {
|
|
16
|
+
if (route.checkpoints.length === 0) {
|
|
17
|
+
throw new Error(`race fork "${fork.id}" route "${route.id}" needs at least one checkpoint`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { checkpoints, laps: Math.max(1, Math.floor(config.laps ?? 1)), forks };
|
|
8
22
|
}
|
|
9
23
|
function rankIds(standings) {
|
|
10
24
|
return standings.map((s) => s.racerId);
|
|
@@ -50,6 +64,7 @@ function distanceToNext(point, cp) {
|
|
|
50
64
|
* volumes. It emits `checkpoint.hit` / `lap.completed` / `position.changed` / `race.finished`, keeps
|
|
51
65
|
* cumulative split times for PB deltas, resolves a pluggable win condition (first-past-post, round-cut,
|
|
52
66
|
* derby last-standing), and `resetToCheckpoint` hands back a respawn pose at the racer's last checkpoint.
|
|
67
|
+
* `removeRacer` drops a racer mid-race and `reset` returns the whole instance to its pre-race state for reuse.
|
|
53
68
|
*/
|
|
54
69
|
export class RaceState {
|
|
55
70
|
track;
|
|
@@ -77,9 +92,32 @@ export class RaceState {
|
|
|
77
92
|
eliminated: false,
|
|
78
93
|
startTime,
|
|
79
94
|
splits: [],
|
|
95
|
+
fork: null,
|
|
96
|
+
routesTaken: {},
|
|
97
|
+
forksDone: {},
|
|
98
|
+
lastHit: null,
|
|
80
99
|
});
|
|
81
100
|
this.order.push(racerId);
|
|
82
101
|
}
|
|
102
|
+
/** Removes a racer's progress/standing and renumbers the remaining field's positions; a no-op for an unknown id. */
|
|
103
|
+
removeRacer(racerId) {
|
|
104
|
+
if (!this.racers.delete(racerId))
|
|
105
|
+
return;
|
|
106
|
+
const idx = this.order.indexOf(racerId);
|
|
107
|
+
if (idx >= 0)
|
|
108
|
+
this.order.splice(idx, 1);
|
|
109
|
+
const remaining = this.order.map((id) => this.racers.get(id)).sort((a, b) => this.compare(a, b, null));
|
|
110
|
+
remaining.forEach((r, i) => {
|
|
111
|
+
r.position = i + 1;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** Clears all racer progress and finish state back to construction time; `track` and `win` are untouched, so the same instance can run another race. */
|
|
115
|
+
reset() {
|
|
116
|
+
this.racers.clear();
|
|
117
|
+
this.order.length = 0;
|
|
118
|
+
this.raceFinished = false;
|
|
119
|
+
this.finalRanking = [];
|
|
120
|
+
}
|
|
83
121
|
eliminate(racerId) {
|
|
84
122
|
const r = this.racers.get(racerId);
|
|
85
123
|
if (r !== undefined)
|
|
@@ -96,6 +134,7 @@ export class RaceState {
|
|
|
96
134
|
return r === null || r === undefined ? null : this.snapshot(r);
|
|
97
135
|
}
|
|
98
136
|
snapshot(r) {
|
|
137
|
+
const activeFork = r.fork === null ? null : this.track.forks[r.fork.forkIndex];
|
|
99
138
|
return {
|
|
100
139
|
racerId: r.racerId,
|
|
101
140
|
lap: r.lap,
|
|
@@ -107,6 +146,10 @@ export class RaceState {
|
|
|
107
146
|
finishTime: r.finishTime,
|
|
108
147
|
eliminated: r.eliminated,
|
|
109
148
|
splits: [...r.splits],
|
|
149
|
+
routesTaken: { ...r.routesTaken },
|
|
150
|
+
activeRoute: activeFork === null || r.fork === null
|
|
151
|
+
? null
|
|
152
|
+
: { forkId: activeFork.id, routeId: activeFork.routes[r.fork.routeIndex].id },
|
|
110
153
|
};
|
|
111
154
|
}
|
|
112
155
|
standings() {
|
|
@@ -116,16 +159,24 @@ export class RaceState {
|
|
|
116
159
|
.sort((a, b) => this.compare(a, b, null))
|
|
117
160
|
.map((r) => this.snapshot(r));
|
|
118
161
|
}
|
|
162
|
+
expectedCheckpoint(r) {
|
|
163
|
+
if (r.fork !== null) {
|
|
164
|
+
const fork = this.track.forks[r.fork.forkIndex];
|
|
165
|
+
const route = fork.routes[r.fork.routeIndex];
|
|
166
|
+
return route.checkpoints[r.fork.nextRouteCheckpoint] ?? this.track.checkpoints[r.nextCheckpoint] ?? null;
|
|
167
|
+
}
|
|
168
|
+
return this.track.checkpoints[r.nextCheckpoint] ?? null;
|
|
169
|
+
}
|
|
119
170
|
resetToCheckpoint(racerId) {
|
|
120
171
|
const r = this.racers.get(racerId);
|
|
121
172
|
if (r === undefined)
|
|
122
173
|
return null;
|
|
123
|
-
const
|
|
124
|
-
const idx = r.lastCheckpoint >= 0 ? r.lastCheckpoint : 0;
|
|
125
|
-
const cp = cps[idx];
|
|
174
|
+
const cp = r.lastHit ?? this.track.checkpoints[0];
|
|
126
175
|
if (cp === undefined)
|
|
127
176
|
return null;
|
|
128
|
-
|
|
177
|
+
let next = this.expectedCheckpoint(r) ?? cp;
|
|
178
|
+
if (next === cp)
|
|
179
|
+
next = this.track.checkpoints[1 % this.track.checkpoints.length] ?? cp;
|
|
129
180
|
const heading = Math.atan2(next.center[0] - cp.center[0], next.center[2] - cp.center[2]);
|
|
130
181
|
return { position: [cp.center[0], cp.center[1], cp.center[2]], heading };
|
|
131
182
|
}
|
|
@@ -141,10 +192,11 @@ export class RaceState {
|
|
|
141
192
|
if (positions !== null) {
|
|
142
193
|
const pa = positions.get(a.racerId);
|
|
143
194
|
const pb = positions.get(b.racerId);
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
195
|
+
const ca = this.expectedCheckpoint(a);
|
|
196
|
+
const cb = this.expectedCheckpoint(b);
|
|
197
|
+
if (pa !== undefined && pb !== undefined && ca !== null && cb !== null) {
|
|
198
|
+
const da = distanceToNext(pa, ca);
|
|
199
|
+
const db = distanceToNext(pb, cb);
|
|
148
200
|
if (da !== db)
|
|
149
201
|
return da - db;
|
|
150
202
|
}
|
|
@@ -157,6 +209,8 @@ export class RaceState {
|
|
|
157
209
|
const cps = this.track.checkpoints;
|
|
158
210
|
const count = cps.length;
|
|
159
211
|
if (count > 0) {
|
|
212
|
+
const forks = this.track.forks;
|
|
213
|
+
const maxSteps = count + forks.reduce((sum, fork) => sum + Math.max(...fork.routes.map((route) => route.checkpoints.length)), 0) + 2;
|
|
160
214
|
for (const id of this.order) {
|
|
161
215
|
const r = this.racers.get(id);
|
|
162
216
|
if (r.finished || r.eliminated)
|
|
@@ -165,10 +219,61 @@ export class RaceState {
|
|
|
165
219
|
if (pos === undefined)
|
|
166
220
|
continue;
|
|
167
221
|
let guard = 0;
|
|
168
|
-
while (guard <
|
|
222
|
+
while (guard < maxSteps) {
|
|
169
223
|
guard += 1;
|
|
224
|
+
if (r.fork !== null) {
|
|
225
|
+
const fork = forks[r.fork.forkIndex];
|
|
226
|
+
const route = fork.routes[r.fork.routeIndex];
|
|
227
|
+
const cp = route.checkpoints[r.fork.nextRouteCheckpoint];
|
|
228
|
+
if (!insideAabb(pos, cp))
|
|
229
|
+
break;
|
|
230
|
+
r.lastHit = cp;
|
|
231
|
+
r.progress += 1 / route.checkpoints.length;
|
|
232
|
+
r.splits.push(now - r.startTime);
|
|
233
|
+
events.push({
|
|
234
|
+
type: "checkpoint.hit",
|
|
235
|
+
racerId: id,
|
|
236
|
+
checkpoint: fork.afterIndex,
|
|
237
|
+
lap: r.lap,
|
|
238
|
+
time: now,
|
|
239
|
+
fork: { forkId: fork.id, routeId: route.id, index: r.fork.nextRouteCheckpoint },
|
|
240
|
+
});
|
|
241
|
+
r.fork.nextRouteCheckpoint += 1;
|
|
242
|
+
if (r.fork.nextRouteCheckpoint >= route.checkpoints.length) {
|
|
243
|
+
r.forksDone[fork.id] = true;
|
|
244
|
+
r.fork = null;
|
|
245
|
+
}
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
let tookRoute = false;
|
|
249
|
+
for (let forkIndex = 0; forkIndex < forks.length; forkIndex += 1) {
|
|
250
|
+
const fork = forks[forkIndex];
|
|
251
|
+
if (fork.afterIndex !== r.lastCheckpoint || r.nextCheckpoint !== fork.afterIndex + 1)
|
|
252
|
+
continue;
|
|
253
|
+
if (r.forksDone[fork.id] === true)
|
|
254
|
+
continue;
|
|
255
|
+
for (let routeIndex = 0; routeIndex < fork.routes.length; routeIndex += 1) {
|
|
256
|
+
const route = fork.routes[routeIndex];
|
|
257
|
+
if (!insideAabb(pos, route.checkpoints[0]))
|
|
258
|
+
continue;
|
|
259
|
+
r.fork = { forkIndex, routeIndex, nextRouteCheckpoint: 0 };
|
|
260
|
+
r.routesTaken[fork.id] = route.id;
|
|
261
|
+
events.push({ type: "fork.taken", racerId: id, forkId: fork.id, routeId: route.id, time: now });
|
|
262
|
+
tookRoute = true;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
if (tookRoute)
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
if (tookRoute)
|
|
269
|
+
continue;
|
|
270
|
+
const cp = cps[r.nextCheckpoint];
|
|
271
|
+
if (!insideAabb(pos, cp))
|
|
272
|
+
break;
|
|
170
273
|
const hit = r.nextCheckpoint;
|
|
171
274
|
r.lastCheckpoint = hit;
|
|
275
|
+
r.lastHit = cp;
|
|
276
|
+
r.forksDone = {};
|
|
172
277
|
r.progress += 1;
|
|
173
278
|
r.splits.push(now - r.startTime);
|
|
174
279
|
events.push({ type: "checkpoint.hit", racerId: id, checkpoint: hit, lap: r.lap, time: now });
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface RecordStorage {
|
|
2
|
+
getItem(key: string): string | null;
|
|
3
|
+
setItem(key: string, value: string): void;
|
|
4
|
+
removeItem(key: string): void;
|
|
5
|
+
}
|
|
6
|
+
export type RecordDirection = "lower" | "higher";
|
|
7
|
+
export interface RecordBookConfig<K extends string> {
|
|
8
|
+
readonly key: string;
|
|
9
|
+
readonly fields: Readonly<Record<K, RecordDirection>>;
|
|
10
|
+
readonly storage?: RecordStorage | null;
|
|
11
|
+
}
|
|
12
|
+
export interface RecordSubmission<K extends string> {
|
|
13
|
+
readonly improved: readonly K[];
|
|
14
|
+
readonly best: Readonly<Partial<Record<K, number>>>;
|
|
15
|
+
}
|
|
16
|
+
export interface RecordBook<K extends string> {
|
|
17
|
+
best(): Readonly<Partial<Record<K, number>>>;
|
|
18
|
+
bestOf(field: K): number | null;
|
|
19
|
+
submit(run: Readonly<Partial<Record<K, number>>>): RecordSubmission<K>;
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A personal-best record book: named numeric fields each racing toward "lower"
|
|
24
|
+
* (times) or "higher" (scores, streaks), persisted through a structural
|
|
25
|
+
* key-value storage (pass `localStorage` in a browser, a stub in tests, or
|
|
26
|
+
* `null` for in-memory only). Corrupt or unavailable storage degrades to an
|
|
27
|
+
* empty book — a record write never throws into a game tick.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createRecordBook<K extends string>(config: RecordBookConfig<K>): RecordBook<K>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
function loadStored(config, storage) {
|
|
2
|
+
if (storage === null)
|
|
3
|
+
return {};
|
|
4
|
+
let raw;
|
|
5
|
+
try {
|
|
6
|
+
raw = storage.getItem(config.key);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return {};
|
|
10
|
+
}
|
|
11
|
+
if (raw === null)
|
|
12
|
+
return {};
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(raw);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
21
|
+
return {};
|
|
22
|
+
const best = {};
|
|
23
|
+
for (const field of Object.keys(config.fields)) {
|
|
24
|
+
const value = parsed[field];
|
|
25
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
26
|
+
best[field] = value;
|
|
27
|
+
}
|
|
28
|
+
return best;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A personal-best record book: named numeric fields each racing toward "lower"
|
|
32
|
+
* (times) or "higher" (scores, streaks), persisted through a structural
|
|
33
|
+
* key-value storage (pass `localStorage` in a browser, a stub in tests, or
|
|
34
|
+
* `null` for in-memory only). Corrupt or unavailable storage degrades to an
|
|
35
|
+
* empty book — a record write never throws into a game tick.
|
|
36
|
+
*/
|
|
37
|
+
export function createRecordBook(config) {
|
|
38
|
+
const storage = config.storage ?? null;
|
|
39
|
+
let best = loadStored(config, storage);
|
|
40
|
+
function persist() {
|
|
41
|
+
if (storage === null)
|
|
42
|
+
return;
|
|
43
|
+
try {
|
|
44
|
+
storage.setItem(config.key, JSON.stringify(best));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
best() {
|
|
52
|
+
return { ...best };
|
|
53
|
+
},
|
|
54
|
+
bestOf(field) {
|
|
55
|
+
return best[field] ?? null;
|
|
56
|
+
},
|
|
57
|
+
submit(run) {
|
|
58
|
+
const improved = [];
|
|
59
|
+
for (const field of Object.keys(config.fields)) {
|
|
60
|
+
const value = run[field];
|
|
61
|
+
if (value === undefined || !Number.isFinite(value))
|
|
62
|
+
continue;
|
|
63
|
+
const current = best[field];
|
|
64
|
+
const beats = current === undefined || (config.fields[field] === "lower" ? value < current : value > current);
|
|
65
|
+
if (beats) {
|
|
66
|
+
best[field] = value;
|
|
67
|
+
improved.push(field);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (improved.length > 0)
|
|
71
|
+
persist();
|
|
72
|
+
return { improved, best: { ...best } };
|
|
73
|
+
},
|
|
74
|
+
clear() {
|
|
75
|
+
best = {};
|
|
76
|
+
if (storage === null)
|
|
77
|
+
return;
|
|
78
|
+
try {
|
|
79
|
+
storage.removeItem(config.key);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
package/dist/game/social.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface SocialDeps {
|
|
|
23
23
|
presence?: (userId: string) => PresenceInfo;
|
|
24
24
|
now?: () => number;
|
|
25
25
|
emotes?: EmotesDeps;
|
|
26
|
+
worldInviteTtlMs?: number;
|
|
26
27
|
}
|
|
27
28
|
export interface FriendEntry {
|
|
28
29
|
userId: string;
|
|
@@ -32,6 +33,10 @@ export interface FriendsSnapshot {
|
|
|
32
33
|
friends: string[];
|
|
33
34
|
blocked: string[];
|
|
34
35
|
}
|
|
36
|
+
export interface FriendRequestEntry {
|
|
37
|
+
requestId: string;
|
|
38
|
+
fromUserId: string;
|
|
39
|
+
}
|
|
35
40
|
export interface Friends {
|
|
36
41
|
canRequest(fromUserId: string, toUserId: string): {
|
|
37
42
|
reason: string;
|
|
@@ -44,9 +49,11 @@ export interface Friends {
|
|
|
44
49
|
accept(userId: string, requestId: string): {
|
|
45
50
|
reason: string;
|
|
46
51
|
} | null;
|
|
52
|
+
decline(userId: string, requestId: string): void;
|
|
47
53
|
remove(userId: string, friendUserId: string): void;
|
|
48
54
|
block(userId: string, targetUserId: string): void;
|
|
49
55
|
list(userId: string): FriendEntry[];
|
|
56
|
+
requestsFor(userId: string): FriendRequestEntry[];
|
|
50
57
|
snapshot(userId: string): FriendsSnapshot;
|
|
51
58
|
hydrate(userId: string, data: FriendsSnapshot): void;
|
|
52
59
|
}
|
|
@@ -59,6 +66,11 @@ export interface PartyConfig {
|
|
|
59
66
|
maxMembers: number;
|
|
60
67
|
inviteTtlMs?: number;
|
|
61
68
|
}
|
|
69
|
+
export interface PartyInviteEntry {
|
|
70
|
+
inviteId: string;
|
|
71
|
+
fromUserId: string;
|
|
72
|
+
createdAt: number;
|
|
73
|
+
}
|
|
62
74
|
export interface Party {
|
|
63
75
|
register(config: PartyConfig): void;
|
|
64
76
|
canInvite(fromUserId: string, toUserId: string): {
|
|
@@ -72,6 +84,7 @@ export interface Party {
|
|
|
72
84
|
accept(userId: string, inviteId: string): {
|
|
73
85
|
reason: string;
|
|
74
86
|
} | null;
|
|
87
|
+
decline(userId: string, inviteId: string): void;
|
|
75
88
|
kick(leaderUserId: string, memberUserId: string): {
|
|
76
89
|
reason: string;
|
|
77
90
|
} | null;
|
|
@@ -81,6 +94,7 @@ export interface Party {
|
|
|
81
94
|
} | null;
|
|
82
95
|
list(userId: string): PartyMemberEntry[];
|
|
83
96
|
membersOf(userId: string): string[];
|
|
97
|
+
invitesFor(userId: string): PartyInviteEntry[];
|
|
84
98
|
}
|
|
85
99
|
export declare const DEFAULT_EMOTE_RADIUS = 20;
|
|
86
100
|
export interface EmoteBroadcastResult {
|
|
@@ -94,6 +108,33 @@ export interface Emotes {
|
|
|
94
108
|
reason: string;
|
|
95
109
|
};
|
|
96
110
|
}
|
|
111
|
+
export interface WorldInviteTarget {
|
|
112
|
+
serverId: string;
|
|
113
|
+
joinCode?: string;
|
|
114
|
+
}
|
|
115
|
+
export interface WorldInvite extends WorldInviteTarget {
|
|
116
|
+
id: string;
|
|
117
|
+
fromUserId: string;
|
|
118
|
+
toUserId: string;
|
|
119
|
+
createdAt: number;
|
|
120
|
+
}
|
|
121
|
+
export interface WorldInvites {
|
|
122
|
+
canInvite(fromUserId: string, toUserId: string): {
|
|
123
|
+
reason: string;
|
|
124
|
+
} | null;
|
|
125
|
+
invite(fromUserId: string, toUserId: string, target: WorldInviteTarget): {
|
|
126
|
+
inviteId: string;
|
|
127
|
+
} | {
|
|
128
|
+
reason: string;
|
|
129
|
+
};
|
|
130
|
+
accept(userId: string, inviteId: string): {
|
|
131
|
+
target: WorldInviteTarget;
|
|
132
|
+
} | {
|
|
133
|
+
reason: string;
|
|
134
|
+
};
|
|
135
|
+
decline(userId: string, inviteId: string): void;
|
|
136
|
+
listFor(userId: string): WorldInvite[];
|
|
137
|
+
}
|
|
97
138
|
export interface Social {
|
|
98
139
|
friends: Friends;
|
|
99
140
|
party: Party;
|
|
@@ -101,5 +142,6 @@ export interface Social {
|
|
|
101
142
|
get(userId: string): PresenceInfo;
|
|
102
143
|
};
|
|
103
144
|
emotes: Emotes;
|
|
145
|
+
worldInvites: WorldInvites;
|
|
104
146
|
}
|
|
105
147
|
export declare function createSocial(deps: SocialDeps): Social;
|