Release Healer Man 0.1.32
This commit is contained in:
1 parent
d3d6aaebdb
commit
6dcb1034e5
102 files changed
+10772
-788
No files matched your search
@@ -0,0 +1,5 @@
|
||||
import type { WorldSpawn } from "./dungeonTypes";
|
||||
|
||||
export function shelvedDungeonSpawn(_dungeonId: string, requested?: WorldSpawn): WorldSpawn | undefined {
|
||||
return requested;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { WorldSpawn } from "./dungeonTypes";
|
||||
import { useManastormAdminStore } from "./manastormAdminStore";
|
||||
|
||||
export function shelvedDungeonSpawn(dungeonId: string, requested?: WorldSpawn): WorldSpawn | undefined {
|
||||
return requested ?? useManastormAdminStore.getState().config.dungeonSpawns[dungeonId];
|
||||
}
|
||||
@@ -1060,7 +1060,7 @@ export function resourceProfileForClass(classId: ClassId): ClassResourceProfile
|
||||
return profile;
|
||||
}
|
||||
|
||||
if (import.meta.env.MODE === "test") {
|
||||
if (import.meta.env.MODE === "test" || import.meta.env.MODE === "shelved") {
|
||||
await preloadAbilityCatalogsForClasses([
|
||||
...BASE_CLASS_IDS,
|
||||
...COA_CLASS_IDS,
|
||||
|
||||
@@ -8,8 +8,8 @@ import { usePartyStore } from "./partyStore";
|
||||
import { useGameStore, type GameMode } from "./store";
|
||||
import { dungeonCanEnter, dungeonDefinitionById, type DungeonId } from "./dungeonRegistry";
|
||||
import type { WorldSpawn } from "./dungeonTypes";
|
||||
import { useManastormAdminStore } from "./manastormAdminStore";
|
||||
import type { GmReviewTarget } from "./gmContentReview";
|
||||
import { shelvedDungeonSpawn } from "@shelvedDungeonSession";
|
||||
import {
|
||||
activateDungeonEncounterDefinitions,
|
||||
exportDungeonEncounterSnapshots,
|
||||
@@ -92,7 +92,7 @@ export function activateDungeonSession(
|
||||
}
|
||||
useGameStore.getState().activateDungeon(dungeonId as DungeonId, difficultyId, {
|
||||
gameMode: options.gameMode,
|
||||
spawn: options.spawn ?? useManastormAdminStore.getState().config.dungeonSpawns[dungeonId],
|
||||
spawn: shelvedDungeonSpawn(dungeonId, options.spawn),
|
||||
gmTestMode: options.gmTestMode,
|
||||
gmReviewTarget: options.gmReviewTarget,
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import { usePartyStore } from "./partyStore";
|
||||
import { useGameStore } from "./store";
|
||||
import { DEFAULT_MANASTORM_ADMIN_CONFIG } from "./manastormAdminConfig";
|
||||
import { useManastormAdminStore } from "./manastormAdminStore";
|
||||
import { SHELVED_MODES_ENABLED } from "../app/releaseFeatures";
|
||||
import {
|
||||
dungeonEncounterSnapshot,
|
||||
importDungeonEncounterSnapshots,
|
||||
@@ -79,7 +80,11 @@ describe("dungeon session asset eviction", () => {
|
||||
config: { ...DEFAULT_MANASTORM_ADMIN_CONFIG, dungeonSpawns: { "wailing-caverns": spawn } },
|
||||
});
|
||||
expect(activateDungeonSession("wailing-caverns")).toBe(true);
|
||||
expect(useGameStore.getState().activeSpawn).toEqual(spawn);
|
||||
const definition = dungeonDefinitionById("wailing-caverns")!;
|
||||
expect(useGameStore.getState().activeSpawn).toEqual(SHELVED_MODES_ENABLED ? spawn : {
|
||||
footPosition: definition.entrance.footPosition,
|
||||
yaw: definition.entrance.yaw,
|
||||
});
|
||||
});
|
||||
|
||||
it("hydrates a same-process checkpoint even when the reset revision is newer", () => {
|
||||
|
||||
@@ -15,6 +15,110 @@ export const GRAFU_CASTLE_MECHANICS_AUDIT = {
|
||||
"loot": 0
|
||||
},
|
||||
"bossRows": [],
|
||||
"scriptedBossSpawns": [
|
||||
{
|
||||
"scriptedPlacementId": "cayus",
|
||||
"sourceAnchorSpawnId": 41927224,
|
||||
"controllerSpawnId": 41928351,
|
||||
"templateId": 105616,
|
||||
"name": "Cayus",
|
||||
"sourcePosition": [
|
||||
-348.7113952636719,
|
||||
1254.5040283203125,
|
||||
-999.6005249023438
|
||||
],
|
||||
"sourceScriptRoot": "Dy_Z19_BOSSN_AIC",
|
||||
"objective": true,
|
||||
"evidence": [
|
||||
"Resource/luascript/02345.lua:CreateObjByFlag(105616,780696,1,1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptedPlacementId": "ardmond",
|
||||
"sourceAnchorSpawnId": 41926939,
|
||||
"controllerSpawnId": 41928345,
|
||||
"templateId": 105542,
|
||||
"name": "Ardmond",
|
||||
"sourcePosition": [
|
||||
-1427.9969482421875,
|
||||
1133.35498046875,
|
||||
-1353.3699951171875
|
||||
],
|
||||
"sourceScriptRoot": "kg_105542_01",
|
||||
"objective": true,
|
||||
"evidence": [
|
||||
"Resource/luascript/02324.lua:kg_105542_CreateBoss"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptedPlacementId": "issac",
|
||||
"sourceAnchorSpawnId": 41926939,
|
||||
"controllerSpawnId": 41928345,
|
||||
"templateId": 105543,
|
||||
"name": "Issac",
|
||||
"sourcePosition": [
|
||||
-1427.9969482421875,
|
||||
1133.35498046875,
|
||||
-1313.3699951171875
|
||||
],
|
||||
"sourceScriptRoot": "kg_105542_01",
|
||||
"objective": false,
|
||||
"evidence": [
|
||||
"Resource/luascript/02324.lua:kg_105542_CreateBoss"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptedPlacementId": "boro-boro-brothers",
|
||||
"sourceAnchorSpawnId": 41926924,
|
||||
"controllerSpawnId": 41927347,
|
||||
"templateId": 105224,
|
||||
"name": "Boro-Boro Brothers",
|
||||
"sourcePosition": [
|
||||
-2172.174072265625,
|
||||
1095.10400390625,
|
||||
-22.212709426879883
|
||||
],
|
||||
"sourceScriptRoot": "ic_105224_01",
|
||||
"objective": true,
|
||||
"evidence": [
|
||||
"Resource/luascript/02231.lua:CreateObjByFlag(105224,780775,1,1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptedPlacementId": "conjurer-chapeaunoir",
|
||||
"sourceAnchorSpawnId": 41926979,
|
||||
"controllerSpawnId": 41927358,
|
||||
"templateId": 105204,
|
||||
"name": "Conjurer Chapeaunoir",
|
||||
"sourcePosition": [
|
||||
-1732.9649658203125,
|
||||
914.618896484375,
|
||||
1190.8310546875
|
||||
],
|
||||
"sourceScriptRoot": "cl_136_105205_N",
|
||||
"objective": true,
|
||||
"evidence": [
|
||||
"Resource/luascript/02330.lua:CreateObjByFlag(105204,780767,0,1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptedPlacementId": "larvanger-barkud",
|
||||
"sourceAnchorSpawnId": 41927071,
|
||||
"controllerSpawnId": 41927369,
|
||||
"templateId": 105351,
|
||||
"name": "Larvanger Barkud",
|
||||
"sourcePosition": [
|
||||
-305.8063049316406,
|
||||
902.5551147460938,
|
||||
941.4002075195312
|
||||
],
|
||||
"sourceScriptRoot": "un_zone19_boss5_allconreal_1_01",
|
||||
"objective": true,
|
||||
"evidence": [
|
||||
"Resource/luascript/02297.lua:CreateObjByFlag(105351,780838,1,1)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"templateCandidates": [
|
||||
{
|
||||
"templateId": 102678,
|
||||
@@ -94,6 +198,18 @@ export const GRAFU_CASTLE_MECHANICS_AUDIT = {
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105204,
|
||||
"name": "Conjurer Chapeaunoir",
|
||||
"classification": "boss",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [
|
||||
"cl_136_105205_N"
|
||||
],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105205,
|
||||
"name": "Template 105205",
|
||||
@@ -109,7 +225,7 @@ export const GRAFU_CASTLE_MECHANICS_AUDIT = {
|
||||
{
|
||||
"templateId": 105210,
|
||||
"name": "Template 105210",
|
||||
"classification": "combat",
|
||||
"classification": "deferred-object",
|
||||
"spawnCount": 1,
|
||||
"autoPlots": [
|
||||
"kg_105542_01"
|
||||
@@ -204,10 +320,82 @@ export const GRAFU_CASTLE_MECHANICS_AUDIT = {
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105224,
|
||||
"name": "Boro-Boro Brothers",
|
||||
"classification": "boss",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [
|
||||
"ic_105224_01"
|
||||
],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105229,
|
||||
"name": "Smirking Castle Clown",
|
||||
"classification": "combat",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105230,
|
||||
"name": "Odd Castle Clown",
|
||||
"classification": "combat",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105233,
|
||||
"name": "Cursed Steel Soldier",
|
||||
"classification": "combat",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105235,
|
||||
"name": "Conjurer's Apprentice",
|
||||
"classification": "combat",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105236,
|
||||
"name": "Giant Enraged Bear",
|
||||
"classification": "combat",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105237,
|
||||
"name": "Manipulated Bear",
|
||||
"classification": "combat",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105254,
|
||||
"name": "Template 105254",
|
||||
"classification": "combat",
|
||||
"classification": "deferred-object",
|
||||
"spawnCount": 10,
|
||||
"autoPlots": [
|
||||
"cl_136_01_01_N",
|
||||
@@ -225,7 +413,7 @@ export const GRAFU_CASTLE_MECHANICS_AUDIT = {
|
||||
{
|
||||
"templateId": 105258,
|
||||
"name": "Template 105258",
|
||||
"classification": "combat",
|
||||
"classification": "deferred-object",
|
||||
"spawnCount": 5,
|
||||
"autoPlots": [
|
||||
"cl_105258_1",
|
||||
@@ -265,6 +453,42 @@ export const GRAFU_CASTLE_MECHANICS_AUDIT = {
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105351,
|
||||
"name": "Larvanger Barkud",
|
||||
"classification": "boss",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [
|
||||
"un_zone19_boss5_allconreal_1_01"
|
||||
],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105542,
|
||||
"name": "Ardmond",
|
||||
"classification": "boss",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [
|
||||
"kg_105542_01"
|
||||
],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105543,
|
||||
"name": "Issac",
|
||||
"classification": "boss",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [
|
||||
"kg_105542_01"
|
||||
],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105596,
|
||||
"name": "Smirking Castle Clown",
|
||||
@@ -332,6 +556,16 @@ export const GRAFU_CASTLE_MECHANICS_AUDIT = {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"templateId": 105599,
|
||||
"name": "Cursed Steel Soldier",
|
||||
"classification": "combat",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 105600,
|
||||
"name": "Juggler's Apprentice",
|
||||
@@ -420,6 +654,18 @@ export const GRAFU_CASTLE_MECHANICS_AUDIT = {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"templateId": 105616,
|
||||
"name": "Cayus",
|
||||
"classification": "boss",
|
||||
"spawnCount": 0,
|
||||
"autoPlots": [
|
||||
"Dy_Z19_BOSSN_AIC"
|
||||
],
|
||||
"plotClassNames": [],
|
||||
"templateScripts": [],
|
||||
"spells": []
|
||||
},
|
||||
{
|
||||
"templateId": 116434,
|
||||
"name": "Skeleton of an Adventurer",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
GRAFU_CASTLE_BOSSES,
|
||||
GRAFU_CASTLE_ENTITIES,
|
||||
GRAFU_CASTLE_POPULATION_PROVENANCE,
|
||||
GRAFU_CASTLE_STATIC_SPAWNS,
|
||||
} from "./generated/grafuCastlePopulation.generated";
|
||||
|
||||
const CONTROLLER_ENTITY_IDS = [
|
||||
"rw-grafu-castle-105210",
|
||||
"rw-grafu-castle-105254",
|
||||
"rw-grafu-castle-105258",
|
||||
] as const;
|
||||
|
||||
const BOSS_ENTITY_IDS = [
|
||||
"rw-grafu-castle-105204",
|
||||
"rw-grafu-castle-105224",
|
||||
"rw-grafu-castle-105351",
|
||||
"rw-grafu-castle-105542",
|
||||
"rw-grafu-castle-105543",
|
||||
"rw-grafu-castle-105616",
|
||||
] as const;
|
||||
|
||||
describe("Grafu Castle population", () => {
|
||||
it("does not render Lua controllers as mushroom combat mobs", () => {
|
||||
for (const controllerId of CONTROLLER_ENTITY_IDS) {
|
||||
expect(GRAFU_CASTLE_ENTITIES).not.toHaveProperty(controllerId);
|
||||
expect(GRAFU_CASTLE_STATIC_SPAWNS.some((spawn) => (
|
||||
String(spawn.entityId) === controllerId
|
||||
))).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("materializes the source-authored patrol formations", () => {
|
||||
const formationSpawns = GRAFU_CASTLE_STATIC_SPAWNS.filter((spawn) => (
|
||||
spawn.id.startsWith("rw-grafu-castle-scripted-castle-patrol-")
|
||||
));
|
||||
expect(formationSpawns).toHaveLength(38);
|
||||
expect(new Set(formationSpawns.map((spawn) => GRAFU_CASTLE_ENTITIES[spawn.entityId]?.name)))
|
||||
.toEqual(new Set([
|
||||
"Conjurer's Apprentice",
|
||||
"Cursed Steel Soldier",
|
||||
"Giant Enraged Bear",
|
||||
"Manipulated Bear",
|
||||
"Manipulated Steel Soldier",
|
||||
"Odd Castle Clown",
|
||||
"Smirking Castle Clown",
|
||||
]));
|
||||
});
|
||||
|
||||
it("ships all five normal encounters and both members of the duo", () => {
|
||||
expect(GRAFU_CASTLE_BOSSES.map((boss) => boss.name)).toEqual([
|
||||
"Cayus",
|
||||
"Ardmond and Issac",
|
||||
"Boro-Boro Brothers",
|
||||
"Conjurer Chapeaunoir",
|
||||
"Larvanger Barkud",
|
||||
]);
|
||||
expect(GRAFU_CASTLE_POPULATION_PROVENANCE.bossSpawnCount).toBe(5);
|
||||
expect(GRAFU_CASTLE_POPULATION_PROVENANCE.scriptedSpawnCount).toBe(44);
|
||||
|
||||
for (const entityId of BOSS_ENTITY_IDS) {
|
||||
expect(GRAFU_CASTLE_ENTITIES[entityId].kind).toBe("boss");
|
||||
expect(GRAFU_CASTLE_ENTITIES[entityId].visual.model?.url).toMatch(
|
||||
/^\/assets\/creatures\/grafu-castle\/.+\.glb$/,
|
||||
);
|
||||
expect(GRAFU_CASTLE_STATIC_SPAWNS.some((spawn) => spawn.entityId === entityId)).toBe(true);
|
||||
}
|
||||
for (const objective of GRAFU_CASTLE_BOSSES) {
|
||||
expect(GRAFU_CASTLE_STATIC_SPAWNS.some((spawn) => spawn.id === objective.id)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -478,23 +478,4 @@ export function isGameMasterSession(session: { readonly kind: string; readonly a
|
||||
return Boolean(session?.kind === "account" && session.accessToken && session.roles?.includes("gm"));
|
||||
}
|
||||
|
||||
export function derivePartySpawnSlots(spawn: WorldSpawn, partySize = 5): readonly WorldSpawn[] {
|
||||
const size = Math.max(1, Math.min(5, Math.trunc(partySize)));
|
||||
const offsets = [
|
||||
[0, 0],
|
||||
[-1.65, -1.8],
|
||||
[1.65, -1.8],
|
||||
[-3.05, -3.5],
|
||||
[3.05, -3.5],
|
||||
] as const;
|
||||
const sin = Math.sin(spawn.yaw);
|
||||
const cos = Math.cos(spawn.yaw);
|
||||
return Object.freeze(offsets.slice(0, size).map(([lateral, forward]) => ({
|
||||
footPosition: [
|
||||
spawn.footPosition[0] + lateral * cos + forward * sin,
|
||||
spawn.footPosition[1],
|
||||
spawn.footPosition[2] - lateral * sin + forward * cos,
|
||||
] as const,
|
||||
yaw: spawn.yaw,
|
||||
})));
|
||||
}
|
||||
export { derivePartySpawnSlots } from "./partySpawnSlots";
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { WorldSpawn } from "./dungeonTypes";
|
||||
|
||||
export function derivePartySpawnSlots(spawn: WorldSpawn, partySize = 5): readonly WorldSpawn[] {
|
||||
const size = Math.max(1, Math.min(5, Math.trunc(partySize)));
|
||||
const offsets = [
|
||||
[0, 0],
|
||||
[-1.65, -1.8],
|
||||
[1.65, -1.8],
|
||||
[-3.05, -3.5],
|
||||
[3.05, -3.5],
|
||||
] as const;
|
||||
const sin = Math.sin(spawn.yaw);
|
||||
const cos = Math.cos(spawn.yaw);
|
||||
return Object.freeze(offsets.slice(0, size).map(([lateral, forward]) => ({
|
||||
footPosition: [
|
||||
spawn.footPosition[0] + lateral * cos + forward * sin,
|
||||
spawn.footPosition[1],
|
||||
spawn.footPosition[2] - lateral * sin + forward * cos,
|
||||
] as const,
|
||||
yaw: spawn.yaw,
|
||||
})));
|
||||
}
|
||||
@@ -24,7 +24,7 @@ describe("modular RuneWaker boss mechanics", () => {
|
||||
));
|
||||
|
||||
it("gives every packaged boss a catalog-backed special-mechanic loadout", () => {
|
||||
expect(bosses).toHaveLength(56);
|
||||
expect(bosses).toHaveLength(62);
|
||||
for (const { dungeon, entity } of bosses) {
|
||||
const label = `${dungeon.id}:${entity.id}`;
|
||||
const authored = catalog.bosses[entity.id];
|
||||
@@ -70,6 +70,7 @@ describe("modular RuneWaker boss mechanics", () => {
|
||||
"rw-fa-100561",
|
||||
"rw-fa-100562",
|
||||
"rw-fa-100563",
|
||||
"rw-grafu-castle-105351",
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -283,8 +283,8 @@ describe("RuneWaker dungeon creature animation coverage", () => {
|
||||
}
|
||||
|
||||
expect(counts).toEqual({
|
||||
configured: 268,
|
||||
native: 267,
|
||||
configured: 275,
|
||||
native: 274,
|
||||
poseOnly: 1,
|
||||
dynamicProxy: 0,
|
||||
});
|
||||
@@ -301,7 +301,7 @@ describe("RuneWaker dungeon creature animation coverage", () => {
|
||||
}
|
||||
}
|
||||
expect(runtimeCounts, "runtime proxies: " + runtimeProxyIds.join(", ")).toEqual({
|
||||
native: 436,
|
||||
native: 446,
|
||||
poseOnly: 1,
|
||||
proxy: 0,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user