fix active action inheritance accounting
This commit is contained in:
@@ -1573,12 +1573,6 @@ export const createReservedTurnHandler = async (options: {
|
|||||||
} else {
|
} else {
|
||||||
meta.killturn = worldKillturn;
|
meta.killturn = worldKillturn;
|
||||||
}
|
}
|
||||||
const active = typeof meta.inherit_active_action === 'number' ? meta.inherit_active_action : 0;
|
|
||||||
if (generalResult.actionKey !== DEFAULT_ACTION) {
|
|
||||||
meta.inherit_active_action = active + 1;
|
|
||||||
} else {
|
|
||||||
meta.inherit_active_action = active;
|
|
||||||
}
|
|
||||||
currentGeneral = { ...currentGeneral, meta };
|
currentGeneral = { ...currentGeneral, meta };
|
||||||
worldOverlay?.syncGeneral(currentGeneral);
|
worldOverlay?.syncGeneral(currentGeneral);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ export const projectCoreDatabaseSnapshot = (rows: {
|
|||||||
dex5: readNumber(meta, 'dex5'),
|
dex5: readNumber(meta, 'dex5'),
|
||||||
specAge: readNumber(meta, 'specage'),
|
specAge: readNumber(meta, 'specage'),
|
||||||
specAge2: readNumber(meta, 'specage2'),
|
specAge2: readNumber(meta, 'specage2'),
|
||||||
|
inheritActiveActionPoints: readNumber(meta, 'inherit_active_action') * 3,
|
||||||
makeLimit: readNumber(meta, 'makelimit'),
|
makeLimit: readNumber(meta, 'makelimit'),
|
||||||
penalty: asRecord(row.penalty),
|
penalty: asRecord(row.penalty),
|
||||||
killTurn: readNumber(meta, 'killturn'),
|
killTurn: readNumber(meta, 'killturn'),
|
||||||
|
|||||||
@@ -580,6 +580,7 @@ const projectWorld = (
|
|||||||
dex5: toDatabaseInt(readNumber(general.meta, 'dex5')),
|
dex5: toDatabaseInt(readNumber(general.meta, 'dex5')),
|
||||||
specAge: toDatabaseInt(readNumber(general.meta, 'specage')),
|
specAge: toDatabaseInt(readNumber(general.meta, 'specage')),
|
||||||
specAge2: toDatabaseInt(readNumber(general.meta, 'specage2')),
|
specAge2: toDatabaseInt(readNumber(general.meta, 'specage2')),
|
||||||
|
inheritActiveActionPoints: readNumber(general.meta, 'inherit_active_action') * 3,
|
||||||
makeLimit: toDatabaseInt(readNumber(general.meta, 'makelimit')),
|
makeLimit: toDatabaseInt(readNumber(general.meta, 'makelimit')),
|
||||||
penalty: asRecord(general.penalty),
|
penalty: asRecord(general.penalty),
|
||||||
killTurn: readNumber(general.meta, 'killturn'),
|
killTurn: readNumber(general.meta, 'killturn'),
|
||||||
|
|||||||
@@ -426,6 +426,89 @@ integration('general command success matrix', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type GeneralActiveActionInheritanceCase = {
|
||||||
|
name: string;
|
||||||
|
action: string;
|
||||||
|
args?: Record<string, unknown>;
|
||||||
|
expectedPointDelta?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generalActiveActionInheritanceCases: GeneralActiveActionInheritanceCase[] = [
|
||||||
|
{
|
||||||
|
name: 'ordinary training does not count as a legacy active action',
|
||||||
|
action: 'che_훈련',
|
||||||
|
expectedPointDelta: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'spy contributes the legacy half active action',
|
||||||
|
action: 'che_첩보',
|
||||||
|
args: { destCityID: 70 },
|
||||||
|
expectedPointDelta: 1.5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'abdication contributes one active action',
|
||||||
|
action: 'che_선양',
|
||||||
|
args: { destGeneralID: 3 },
|
||||||
|
expectedPointDelta: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rest does not count as a legacy active action',
|
||||||
|
action: '휴식',
|
||||||
|
expectedPointDelta: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'talent scouting preserves its probability-weighted active action',
|
||||||
|
action: 'che_인재탐색',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const readActiveActionPoints = (
|
||||||
|
snapshot: { generals: Array<Record<string, unknown>> },
|
||||||
|
generalId: number
|
||||||
|
): number => {
|
||||||
|
const general = snapshot.generals.find((entry) => entry.id === generalId);
|
||||||
|
const value = general?.inheritActiveActionPoints;
|
||||||
|
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
integration('general active-action inheritance point parity', () => {
|
||||||
|
it.each(generalActiveActionInheritanceCases)(
|
||||||
|
'$name',
|
||||||
|
async ({ name, action, args, expectedPointDelta }) => {
|
||||||
|
const request = buildRequest(action, args);
|
||||||
|
request.setup!.world!.hiddenSeed = `general-active-action-${name}`;
|
||||||
|
const reference = runReferenceTurnCommandTraceRequest(
|
||||||
|
workspaceRoot!,
|
||||||
|
request as unknown as Record<string, unknown>
|
||||||
|
);
|
||||||
|
const core = await runCoreTurnCommandTrace(request, reference.before);
|
||||||
|
const referencePointDelta =
|
||||||
|
readActiveActionPoints(reference.after, 1) - readActiveActionPoints(reference.before, 1);
|
||||||
|
const corePointDelta = readActiveActionPoints(core.after, 1) - readActiveActionPoints(core.before, 1);
|
||||||
|
|
||||||
|
expect(reference.execution.outcome).toMatchObject({ completed: true });
|
||||||
|
expect(core.execution.outcome).toMatchObject({
|
||||||
|
requestedAction: action,
|
||||||
|
actionKey: action,
|
||||||
|
usedFallback: false,
|
||||||
|
});
|
||||||
|
if (expectedPointDelta !== undefined) {
|
||||||
|
expect(referencePointDelta).toBe(expectedPointDelta);
|
||||||
|
} else {
|
||||||
|
expect(referencePointDelta).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
expect(corePointDelta).toBe(referencePointDelta);
|
||||||
|
expect(core.rng).toEqual(reference.rng);
|
||||||
|
expect(
|
||||||
|
compareTurnSnapshotDeltas(reference.before, reference.after, core.before, core.after, {
|
||||||
|
ignoredPathPatterns: ignoredLifecyclePaths,
|
||||||
|
})
|
||||||
|
).toEqual([]);
|
||||||
|
},
|
||||||
|
120_000
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
interface NpcActiveBoundaryCase {
|
interface NpcActiveBoundaryCase {
|
||||||
name: string;
|
name: string;
|
||||||
args: Record<string, unknown>;
|
args: Record<string, unknown>;
|
||||||
|
|||||||
@@ -702,6 +702,80 @@ integration('nation command success matrix', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type NationActiveActionInheritanceCase = {
|
||||||
|
name: string;
|
||||||
|
request: NationMatrixCase;
|
||||||
|
expectedPointDelta: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const nationActiveActionInheritanceCases: NationActiveActionInheritanceCase[] = [
|
||||||
|
{
|
||||||
|
name: 'ordinary award does not count as a legacy active action',
|
||||||
|
request: ['che_포상', { isGold: true, amount: 100, destGeneralID: 3 }],
|
||||||
|
expectedPointDelta: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nation rename contributes one active action',
|
||||||
|
request: ['che_국호변경', { nationName: '능동아국' }],
|
||||||
|
expectedPointDelta: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'event research contributes one active action on completion',
|
||||||
|
request: researchCase('event_화시병연구', '화시병 연구', 11),
|
||||||
|
expectedPointDelta: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nation rest does not count as a legacy active action',
|
||||||
|
request: ['휴식', undefined],
|
||||||
|
expectedPointDelta: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const readNationActorActiveActionPoints = (
|
||||||
|
snapshot: { generals: Array<Record<string, unknown>> },
|
||||||
|
generalId: number
|
||||||
|
): number => {
|
||||||
|
const general = snapshot.generals.find((entry) => entry.id === generalId);
|
||||||
|
const value = general?.inheritActiveActionPoints;
|
||||||
|
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
integration('nation active-action inheritance point parity', () => {
|
||||||
|
it.each(nationActiveActionInheritanceCases)(
|
||||||
|
'$name',
|
||||||
|
async ({ name, request: [action, args, fixturePatches], expectedPointDelta }) => {
|
||||||
|
const request = buildRequest(action, args, fixturePatches);
|
||||||
|
request.setup!.world!.hiddenSeed = `nation-active-action-${name}`;
|
||||||
|
const reference = runReferenceTurnCommandTraceRequest(
|
||||||
|
workspaceRoot!,
|
||||||
|
request as unknown as Record<string, unknown>
|
||||||
|
);
|
||||||
|
const core = await runCoreTurnCommandTrace(request, reference.before);
|
||||||
|
const referencePointDelta =
|
||||||
|
readNationActorActiveActionPoints(reference.after, 1) -
|
||||||
|
readNationActorActiveActionPoints(reference.before, 1);
|
||||||
|
const corePointDelta =
|
||||||
|
readNationActorActiveActionPoints(core.after, 1) - readNationActorActiveActionPoints(core.before, 1);
|
||||||
|
|
||||||
|
expect(reference.execution.outcome).toMatchObject({ completed: true });
|
||||||
|
expect(core.execution.outcome).toMatchObject({
|
||||||
|
requestedAction: action,
|
||||||
|
actionKey: action,
|
||||||
|
usedFallback: false,
|
||||||
|
});
|
||||||
|
expect(referencePointDelta).toBe(expectedPointDelta);
|
||||||
|
expect(corePointDelta).toBe(referencePointDelta);
|
||||||
|
expect(core.rng).toEqual(reference.rng);
|
||||||
|
expect(
|
||||||
|
compareTurnSnapshotDeltas(reference.before, reference.after, core.before, core.after, {
|
||||||
|
ignoredPathPatterns: ignoredLifecyclePaths,
|
||||||
|
})
|
||||||
|
).toEqual([]);
|
||||||
|
},
|
||||||
|
120_000
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const nationNameBoundaryCases: Array<{
|
const nationNameBoundaryCases: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
nationName: string;
|
nationName: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user