100 lines
3.5 KiB
TypeScript
100 lines
3.5 KiB
TypeScript
import type { TurnCalendarContext, TurnCalendarHandler, InMemoryTurnWorld } from './inMemoryWorld.js';
|
|
import type { City, Nation } from '@sammo-ts/logic';
|
|
import { joinYearMonth, readNumber } from './ai/aiUtils.js';
|
|
|
|
const calcNationDevelopedRate = (cities: City[]): { pop: number; all: number } => {
|
|
if (cities.length === 0) {
|
|
return { pop: 0, all: 0 };
|
|
}
|
|
let pop = 0;
|
|
let agri = 0;
|
|
let comm = 0;
|
|
let secu = 0;
|
|
let def = 0;
|
|
let wall = 0;
|
|
for (const city of cities) {
|
|
pop += city.populationMax > 0 ? city.population / city.populationMax : 0;
|
|
agri += city.agricultureMax > 0 ? city.agriculture / city.agricultureMax : 0;
|
|
comm += city.commerceMax > 0 ? city.commerce / city.commerceMax : 0;
|
|
secu += city.securityMax > 0 ? city.security / city.securityMax : 0;
|
|
def += city.defenceMax > 0 ? city.defence / city.defenceMax : 0;
|
|
wall += city.wallMax > 0 ? city.wall / city.wallMax : 0;
|
|
}
|
|
const count = cities.length;
|
|
pop /= count;
|
|
agri /= count;
|
|
comm /= count;
|
|
secu /= count;
|
|
def /= count;
|
|
wall /= count;
|
|
const all = (pop + agri + comm + secu + def + wall) / 6;
|
|
return { pop, all };
|
|
};
|
|
|
|
const resolveNpcTaxRate = (cities: City[]): number => {
|
|
if (cities.length === 0) {
|
|
return 15;
|
|
}
|
|
const devRate = calcNationDevelopedRate(cities);
|
|
const avg = (devRate.pop + devRate.all) / 2;
|
|
if (avg > 0.95) {
|
|
return 25;
|
|
}
|
|
if (avg > 0.7) {
|
|
return 20;
|
|
}
|
|
if (avg > 0.5) {
|
|
return 15;
|
|
}
|
|
return 10;
|
|
};
|
|
|
|
const shouldUpdateRate = (month: number): boolean => month === 6 || month === 12;
|
|
|
|
const isNpcMonarchNation = (nation: Nation, world: InMemoryTurnWorld): boolean => {
|
|
if (!nation.capitalCityId || !nation.chiefGeneralId) {
|
|
return false;
|
|
}
|
|
const chief = world.getGeneralById(nation.chiefGeneralId);
|
|
return Boolean(chief && chief.npcState >= 2);
|
|
};
|
|
|
|
export const createNpcTaxHandler = (options: { getWorld: () => InMemoryTurnWorld | null }): TurnCalendarHandler => {
|
|
return {
|
|
onMonthChanged: (context: TurnCalendarContext) => {
|
|
if (!shouldUpdateRate(context.currentMonth)) {
|
|
return;
|
|
}
|
|
const world = options.getWorld();
|
|
if (!world) {
|
|
return;
|
|
}
|
|
const worldState = world.getState();
|
|
const meta = worldState.meta ?? {};
|
|
const initYear = readNumber(meta.initYear, NaN);
|
|
const initMonth = readNumber(meta.initMonth, NaN);
|
|
const hasInit = Number.isFinite(initYear) && Number.isFinite(initMonth);
|
|
const monthsSinceStart = hasInit
|
|
? joinYearMonth(context.currentYear, context.currentMonth) - joinYearMonth(initYear, initMonth)
|
|
: null;
|
|
const cities = world.listCities();
|
|
for (const nation of world.listNations()) {
|
|
if (!isNpcMonarchNation(nation, world)) {
|
|
continue;
|
|
}
|
|
const nationCities = cities.filter((city) => city.nationId === nation.id && city.supplyState > 0);
|
|
let rate = resolveNpcTaxRate(nationCities);
|
|
if (monthsSinceStart !== null && monthsSinceStart <= 4) {
|
|
rate = Math.min(rate, 10);
|
|
}
|
|
world.updateNation(nation.id, {
|
|
meta: {
|
|
...nation.meta,
|
|
rate,
|
|
},
|
|
});
|
|
}
|
|
},
|
|
};
|
|
};
|