merge: 최신 main을 자원 수입 문구 수정에 통합한다
This commit is contained in:
@@ -1997,7 +1997,7 @@ async function handleKick(
|
||||
}
|
||||
|
||||
const target = world.getGeneralById(command.destGeneralId);
|
||||
if (!target || target.id === general.id || target.nationId !== general.nationId) {
|
||||
if (!target || target.nationId !== general.nationId) {
|
||||
return {
|
||||
type: 'kick',
|
||||
ok: false,
|
||||
@@ -2005,7 +2005,18 @@ async function handleKick(
|
||||
reason: '대상을 찾을 수 없거나 같은 국가가 아닙니다.',
|
||||
};
|
||||
}
|
||||
if (resolveMaxSecretPermission(target) === 4 && resolvePermissionKind(target) === 'ambassador') {
|
||||
if (target.id === general.id) {
|
||||
return { type: 'kick', ok: false, generalId: command.generalId, reason: '본인은 추방할 수 없습니다.' };
|
||||
}
|
||||
// Ref 화면은 군주와 본인을 후보에서 제외하지만 서버는 조작 요청을 막지 못했다.
|
||||
// 국가 소유권을 깨뜨리는 대상은 UI와 무관하게 durable command 경계에서 거부한다.
|
||||
if (target.id === nation.chiefGeneralId || target.officerLevel === 12) {
|
||||
return { type: 'kick', ok: false, generalId: command.generalId, reason: '군주는 추방할 수 없습니다.' };
|
||||
}
|
||||
if (target.officerLevel >= 5) {
|
||||
return { type: 'kick', ok: false, generalId: command.generalId, reason: '수뇌는 추방할 수 없습니다.' };
|
||||
}
|
||||
if (resolvePermissionKind(target) === 'ambassador') {
|
||||
return {
|
||||
type: 'kick',
|
||||
ok: false,
|
||||
|
||||
@@ -312,6 +312,39 @@ describe('nation personnel world commands', () => {
|
||||
expect(fixture.world.peekDirtyState().logs).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('rejects self, ruler, head officer, and ambassador targets without partial mutation', async () => {
|
||||
const cases = [
|
||||
{ label: 'self', targetId: 2, reason: '본인은 추방할 수 없습니다.' },
|
||||
{ label: 'ruler', targetId: 1, reason: '군주는 추방할 수 없습니다.' },
|
||||
{ label: 'head officer', targetId: 3, reason: '수뇌는 추방할 수 없습니다.' },
|
||||
{ label: 'ambassador', targetId: 4, reason: '외교권자는 추방할 수 없습니다.' },
|
||||
] as const;
|
||||
|
||||
for (const testCase of cases) {
|
||||
const fixture = buildWorld({
|
||||
generals: [
|
||||
buildGeneral(1, { officerLevel: 12 }),
|
||||
buildGeneral(2, { officerLevel: 5 }),
|
||||
buildGeneral(3, { officerLevel: 7 }),
|
||||
buildGeneral(4, {
|
||||
meta: { killturn: 12, belong: 5, permission: 'ambassador' },
|
||||
penalty: { noAmbassador: true },
|
||||
}),
|
||||
buildGeneral(5),
|
||||
],
|
||||
});
|
||||
const originalTarget = fixture.world.getGeneralById(testCase.targetId);
|
||||
|
||||
await expect(
|
||||
fixture.handler.handle({ type: 'kick', generalId: 2, destGeneralId: testCase.targetId })
|
||||
).resolves.toMatchObject({ ok: false, reason: testCase.reason });
|
||||
expect(fixture.world.getGeneralById(testCase.targetId), testCase.label).toEqual(originalTarget);
|
||||
expect(fixture.world.getGeneralById(2)?.meta.killturn, testCase.label).toBe(12);
|
||||
expect(fixture.world.peekDirtyState().logs, testCase.label).toEqual([]);
|
||||
expect(fixture.world.peekDirtyState().nations, testCase.label).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('preserves the legacy kick year boundaries and deterministic NPC public message', async () => {
|
||||
const early = buildWorld({
|
||||
currentYear: 181,
|
||||
|
||||
Reference in New Issue
Block a user