feat: add walkable Guangzong sortie camp

This commit is contained in:
2026-07-27 22:37:55 +09:00
parent 43e5533515
commit 9d67e0ae1d
17 changed files with 3519 additions and 558 deletions

View File

@@ -36,6 +36,12 @@ try {
const actionModule = await server.ssrLoadModule(
'/src/game/state/thirdCampPreparationActions.ts'
);
const explorationDataModule = await server.ssrLoadModule(
'/src/game/data/thirdCampExploration.ts'
);
const explorationActionModule = await server.ssrLoadModule(
'/src/game/state/thirdCampExplorationActions.ts'
);
const campaignModule = await server.ssrLoadModule(
'/src/game/state/campaignState.ts'
);
@@ -50,6 +56,8 @@ try {
verifyGuardedPersistentOverwrite(
dataModule,
actionModule,
explorationDataModule,
explorationActionModule,
campaignModule,
battleScenarios
);
@@ -172,17 +180,29 @@ function verifyActivityGates(dataModule) {
});
availability =
dataModule.resolveThirdCampPreparationAvailability(acknowledged);
assert(
availability.every(({ selectable }) => selectable === false),
'Opening reward categories alone must not complete walkable-camp activities.'
);
const explored = literalCampaign(dataModule, {
completedActivities: ['information', 'equipment']
});
availability =
dataModule.resolveThirdCampPreparationAvailability(explored);
assert.equal(availability[0].selectable, true);
assert.deepEqual(availability[0].evidence, {
kind: 'victory-reward-category',
kind: 'exploration-activity',
battleId: dataModule.thirdCampPreparationSourceBattleId,
category: 'unlocks'
visitId: 'third-guangzong-sortie-camp',
activityId: 'information'
});
assert.equal(availability[1].selectable, true);
assert.deepEqual(availability[1].evidence, {
kind: 'victory-reward-category',
kind: 'exploration-activity',
battleId: dataModule.thirdCampPreparationSourceBattleId,
category: 'equipment'
visitId: 'third-guangzong-sortie-camp',
activityId: 'equipment'
});
assert.equal(availability[2].selectable, false);
@@ -198,6 +218,7 @@ function verifyActivityGates(dataModule) {
assert.equal(availability[1].selectable, false);
const companion = literalCampaign(dataModule, {
completedActivities: ['companion'],
completePriorityDialogue: true
});
availability =
@@ -208,7 +229,7 @@ function verifyActivityGates(dataModule) {
assert.equal(companionAvailability?.selectable, true);
assert.equal(
companionAvailability?.evidence?.kind,
'priority-return-dialogue'
'exploration-activity'
);
assert.equal(
companionAvailability?.evidence?.dialogueId,
@@ -219,6 +240,18 @@ function verifyActivityGates(dataModule) {
'guan-yu__zhang-fei'
);
const legacyCompanion = literalCampaign(dataModule, {
completePriorityDialogue: true
});
assert.equal(
dataModule.resolveThirdCampPreparationAvailability(
legacyCompanion
).find(({ priority }) => priority.id === 'companion')
?.evidence?.kind,
'priority-return-dialogue',
'An already completed result dialogue must remain valid for old saves.'
);
const unrelatedDialogue = literalCampaign(dataModule);
unrelatedDialogue.completedCampDialogues = [
'liu-guan-after-guangzong-road'
@@ -299,7 +332,7 @@ function verifyTargetBattleMemoryIsolation(dataModule) {
);
const stale = eligibleLiteralCampaign(dataModule, 'information');
stale.acknowledgedVictoryRewardCategories = {};
stale.completedCampVisits = [];
assert.equal(
dataModule.resolveThirdCampPreparationMemory({
campaign: stale,
@@ -409,7 +442,7 @@ function verifyBattleLifecycleContract(dataModule) {
memory.evidence.unitIds[0] = 'mutated';
}
assert.notEqual(fresh?.label, 'mutated');
if (fresh?.evidence.kind === 'priority-return-dialogue') {
if (fresh && 'unitIds' in fresh.evidence) {
assert.notEqual(fresh.evidence.unitIds[0], 'mutated');
}
});
@@ -418,6 +451,8 @@ function verifyBattleLifecycleContract(dataModule) {
function verifyGuardedPersistentOverwrite(
dataModule,
actionModule,
explorationDataModule,
explorationActionModule,
campaignModule,
battleScenarios
) {
@@ -432,9 +467,16 @@ function verifyGuardedPersistentOverwrite(
'activity-required'
);
campaignModule.acknowledgeCampaignVictoryReward(
dataModule.thirdCampPreparationSourceBattleId,
['unlocks']
const entry = explorationActionModule.enterThirdCampExploration();
assert.equal(entry.ok, true);
const informationActivity =
explorationActionModule.recordThirdCampExplorationActivity(
'information'
);
assert.equal(informationActivity.ok, true);
assert.equal(
actionModule.clearThirdCampPreparationPriority().ok,
true
);
let result =
actionModule.setThirdCampPreparationPriority('information');
@@ -465,19 +507,35 @@ function verifyGuardedPersistentOverwrite(
'A rejected replacement must preserve the previous valid priority.'
);
campaignModule.acknowledgeCampaignVictoryReward(
dataModule.thirdCampPreparationSourceBattleId,
['equipment']
const equipmentActivity =
explorationActionModule.recordThirdCampExplorationActivity(
'equipment'
);
assert.equal(equipmentActivity.ok, true);
assert.equal(
actionModule.clearThirdCampPreparationPriority().ok,
true
);
result = actionModule.setThirdCampPreparationPriority('equipment');
assert.equal(result.ok, true);
assert.equal(result.changed, true);
assert.equal(result.memory.priorityId, 'equipment');
completePriorityDialogue(
dataModule,
campaignModule,
battleScenarios
const explorationDefinition =
explorationDataModule.resolveThirdCampExplorationDefinition(
campaignModule.getCampaignState()
);
const companionChoiceId =
explorationDefinition?.companionDialogue.choices[0]?.id;
assert(companionChoiceId);
const companionActivity =
explorationActionModule.completeThirdCampCompanionActivity(
companionChoiceId
);
assert.equal(companionActivity.ok, true);
assert.equal(
actionModule.clearThirdCampPreparationPriority().ok,
true
);
result = actionModule.setThirdCampPreparationPriority('companion');
assert.equal(result.ok, true);
@@ -883,12 +941,7 @@ function priorityDialogueForCampaign(campaign, dataModule) {
function eligibleLiteralCampaign(dataModule, priorityId) {
return literalCampaign(dataModule, {
acknowledgedCategories:
priorityId === 'information'
? ['unlocks']
: priorityId === 'equipment'
? ['equipment']
: [],
completedActivities: [priorityId],
completePriorityDialogue: priorityId === 'companion',
selectedPriorityId: priorityId
});
@@ -898,6 +951,7 @@ function literalCampaign(
dataModule,
{
acknowledgedCategories = [],
completedActivities = [],
completePriorityDialogue = false,
selectedPriorityId,
legacyReportOnly = false
@@ -926,7 +980,10 @@ function literalCampaign(
completedCampDialogues: completePriorityDialogue
? ['guan-zhang-after-guangzong-road']
: [],
completedCampVisits: [],
completedCampVisits: completedActivities.map(
(activityId) =>
`third-guangzong-sortie-camp:activity:${activityId}`
),
campVisitChoiceIds: {},
...(selectedPriorityId
? {