인게임 예약 명령과 장수 상태 표시를 바로잡음
This commit is contained in:
@@ -1149,6 +1149,8 @@ export class GeneralAI {
|
||||
const effectiveOfficerLevel = new Map(generals.map((candidate) => [candidate.id, candidate.officerLevel]));
|
||||
|
||||
let userChiefCount = 0;
|
||||
let ambassadorCount = 0;
|
||||
const assignedAmbassadorIds = new Set<number>();
|
||||
const worldKillturn = readMetaNumber(asRecord(this.world.meta), 'killturn', 0);
|
||||
const minUserKillturn = worldKillturn - Math.trunc(240 / this.turnTermMinutes);
|
||||
const minNpcKillturn = 36;
|
||||
@@ -1162,13 +1164,25 @@ export class GeneralAI {
|
||||
const killturn = readRequiredMetaNumber(asRecord(chief.meta), 'killturn', `generalId=${chief.id}`);
|
||||
if (chief.npcState < 2 && killturn >= minUserKillturn && penalty.noAmbassador !== true) {
|
||||
userChiefCount += 1;
|
||||
chief.meta = { ...chief.meta, permission: 'ambassador' };
|
||||
this.promotionPatches.push({
|
||||
generalId: chief.id,
|
||||
officerLevel: chief.officerLevel,
|
||||
officerCity: readMetaNumber(asRecord(chief.meta), 'officer_city', 0),
|
||||
permission: 'ambassador',
|
||||
});
|
||||
if (ambassadorCount < 2) {
|
||||
ambassadorCount += 1;
|
||||
assignedAmbassadorIds.add(chief.id);
|
||||
chief.meta = { ...chief.meta, permission: 'ambassador' };
|
||||
this.promotionPatches.push({
|
||||
generalId: chief.id,
|
||||
officerLevel: chief.officerLevel,
|
||||
officerCity: readMetaNumber(asRecord(chief.meta), 'officer_city', 0),
|
||||
permission: 'ambassador',
|
||||
});
|
||||
} else if (asRecord(chief.meta).permission === 'ambassador') {
|
||||
chief.meta = { ...chief.meta, permission: 'normal' };
|
||||
this.promotionPatches.push({
|
||||
generalId: chief.id,
|
||||
officerLevel: chief.officerLevel,
|
||||
officerCity: readMetaNumber(asRecord(chief.meta), 'officer_city', 0),
|
||||
permission: 'normal',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1222,7 +1236,7 @@ export class GeneralAI {
|
||||
this.promotionPatches.push({ generalId: oldChief.id, officerLevel: 1, officerCity: 0 });
|
||||
effectiveOfficerLevel.set(oldChief.id, 1);
|
||||
}
|
||||
const permission = penalty.noAmbassador === true ? undefined : 'ambassador';
|
||||
const permission = penalty.noAmbassador !== true && ambassadorCount < 2 ? 'ambassador' : undefined;
|
||||
candidate.officerLevel = 11;
|
||||
candidate.meta = {
|
||||
...candidate.meta,
|
||||
@@ -1238,6 +1252,10 @@ export class GeneralAI {
|
||||
effectiveOfficerLevel.set(candidate.id, 11);
|
||||
chiefSet |= 1 << 11;
|
||||
userChiefCount += 1;
|
||||
if (permission) {
|
||||
ambassadorCount += 1;
|
||||
assignedAmbassadorIds.add(candidate.id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1307,10 +1325,16 @@ export class GeneralAI {
|
||||
this.promotionPatches.push({ generalId: oldChief.id, officerLevel: 1, officerCity: 0 });
|
||||
}
|
||||
const permission =
|
||||
nextChief.npcState < 2 && asRecord(nextChief.penalty).noAmbassador !== true ? 'ambassador' : undefined;
|
||||
nextChief.npcState < 2 && asRecord(nextChief.penalty).noAmbassador !== true && ambassadorCount < 2
|
||||
? 'ambassador'
|
||||
: undefined;
|
||||
if (nextChief.npcState < 2) {
|
||||
userChiefCount += 1;
|
||||
}
|
||||
if (permission) {
|
||||
ambassadorCount += 1;
|
||||
assignedAmbassadorIds.add(nextChief.id);
|
||||
}
|
||||
this.promotionPatches.push({
|
||||
generalId: nextChief.id,
|
||||
officerLevel: chiefLevel,
|
||||
@@ -1331,6 +1355,25 @@ export class GeneralAI {
|
||||
chiefSet |= 1 << chiefLevel;
|
||||
}
|
||||
|
||||
for (const candidate of generals) {
|
||||
if (
|
||||
asRecord(candidate.meta).permission !== 'ambassador' ||
|
||||
assignedAmbassadorIds.has(candidate.id) ||
|
||||
this.promotionPatches.some(
|
||||
(patch) => patch.generalId === candidate.id && patch.permission === 'normal'
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
candidate.meta = { ...candidate.meta, permission: 'normal' };
|
||||
this.promotionPatches.push({
|
||||
generalId: candidate.id,
|
||||
officerLevel: effectiveOfficerLevel.get(candidate.id) ?? candidate.officerLevel,
|
||||
officerCity: readMetaNumber(asRecord(candidate.meta), 'officer_city', 0),
|
||||
permission: 'normal',
|
||||
});
|
||||
}
|
||||
|
||||
if (chiefSet !== initialChiefSet) {
|
||||
this.promotionNationMeta = {
|
||||
...this.nation.meta,
|
||||
|
||||
@@ -711,7 +711,7 @@ describe('legacy NPC user-chief promotion parity', () => {
|
||||
expect(ai.consumePromotionPatches()).toEqual({ generals: [], nationMeta: null });
|
||||
});
|
||||
|
||||
it('does not appoint a fourth user chief in the ordinary NPC-ruler fill pass', () => {
|
||||
it('keeps NPC-assigned diplomatic authority at two even when three user chiefs exist', () => {
|
||||
const ruler = makePromotionGeneral({
|
||||
id: 1,
|
||||
officerLevel: 12,
|
||||
@@ -745,11 +745,11 @@ describe('legacy NPC user-chief promotion parity', () => {
|
||||
const promotion = ai.consumePromotionPatches();
|
||||
const result = promotion.generals;
|
||||
expect(result.filter((entry) => entry.generalId === candidate.id)).toEqual([]);
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(promotion.nationMeta).toBeNull();
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining(
|
||||
existingChiefs.map((chief) => ({
|
||||
existingChiefs.slice(1).map((chief) => ({
|
||||
generalId: chief.id,
|
||||
officerLevel: chief.officerLevel,
|
||||
officerCity: 0,
|
||||
@@ -759,6 +759,52 @@ describe('legacy NPC user-chief promotion parity', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('repairs a third diplomatic authority left by an earlier NPC promotion pass', () => {
|
||||
const ruler = makePromotionGeneral({
|
||||
id: 1,
|
||||
officerLevel: 12,
|
||||
npcState: 2,
|
||||
meta: { killturn: 100, belong: 4 },
|
||||
});
|
||||
const existingChiefs = [11, 10, 9].map((officerLevel, index) =>
|
||||
makePromotionGeneral({
|
||||
id: index + 2,
|
||||
npcState: 0,
|
||||
officerLevel,
|
||||
meta: { killturn: 100, belong: 4, officer_city: 0, permission: 'ambassador' },
|
||||
})
|
||||
);
|
||||
const formerChief = makePromotionGeneral({
|
||||
id: 5,
|
||||
npcState: 0,
|
||||
officerLevel: 1,
|
||||
meta: { killturn: 100, belong: 4, officer_city: 0, permission: 'ambassador' },
|
||||
});
|
||||
const ai = makePromotionAi({
|
||||
ruler,
|
||||
generals: [ruler, ...existingChiefs, formerChief],
|
||||
nation: { level: 6 },
|
||||
userGenerals: [...existingChiefs, formerChief],
|
||||
chiefGenerals: [ruler, ...existingChiefs],
|
||||
});
|
||||
|
||||
chooseNpcPromotion(ai);
|
||||
|
||||
const promotion = ai.consumePromotionPatches();
|
||||
expect(promotion.generals).toContainEqual({
|
||||
generalId: existingChiefs[0]!.id,
|
||||
officerLevel: 11,
|
||||
officerCity: 0,
|
||||
permission: 'normal',
|
||||
});
|
||||
expect(promotion.generals).toContainEqual({
|
||||
generalId: formerChief.id,
|
||||
officerLevel: 1,
|
||||
officerCity: 0,
|
||||
permission: 'normal',
|
||||
});
|
||||
});
|
||||
|
||||
it('lets an NPC non-ruler fill an open seat with a user immediately when no NPC pool exists', () => {
|
||||
const actor = makePromotionGeneral({
|
||||
id: 1,
|
||||
@@ -1561,6 +1607,7 @@ describe('legacy NPC AI final-decision parity', () => {
|
||||
});
|
||||
expect(do집합(ai)?.action).toBe('che_집합');
|
||||
expect(ai.general.meta.killturn).toBe(72);
|
||||
expect(ai.general.meta.killturn).toBeGreaterThanOrEqual(70);
|
||||
});
|
||||
|
||||
it('does not warp to the rear when recruitment is disabled', () => {
|
||||
|
||||
Reference in New Issue
Block a user