feat: migrate monthly city trade rates
This commit is contained in:
@@ -261,9 +261,9 @@ const buildCityUpdate = (
|
||||
if (trust !== null) {
|
||||
data.trust = trust;
|
||||
}
|
||||
if (trade !== null) {
|
||||
data.trade = trade;
|
||||
}
|
||||
// trade가 없는 도시는 레거시 DB에서 NULL이다. City meta에 trade가
|
||||
// 없다는 사실도 dirty city의 전체 snapshot 일부로 영속화한다.
|
||||
data.trade = trade;
|
||||
if (region !== null) {
|
||||
data.region = region;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { LiteHashDRBG, RandUtil } from '@sammo-ts/common';
|
||||
import { simpleSerialize } from '@sammo-ts/logic/war/utils.js';
|
||||
|
||||
import type { InMemoryTurnWorld, TurnCalendarContext, TurnCalendarHandler } from './inMemoryWorld.js';
|
||||
import type { TurnEvent } from './types.js';
|
||||
|
||||
@@ -18,6 +21,50 @@ export type MonthlyEventActionHandler = (
|
||||
export type MonthlyEventActionRegistry = ReadonlyMap<string, MonthlyEventActionHandler>;
|
||||
|
||||
const COMPARATORS = new Set(['==', '!=', '<', '>', '<=', '>=']);
|
||||
const CITY_TRADE_PROBABILITY_BY_LEVEL: Readonly<Record<number, number>> = {
|
||||
1: 0,
|
||||
2: 0,
|
||||
3: 0,
|
||||
4: 0.2,
|
||||
5: 0.4,
|
||||
6: 0.6,
|
||||
7: 0.8,
|
||||
8: 1,
|
||||
};
|
||||
|
||||
const resolveHiddenSeed = (world: InMemoryTurnWorld): string | number => {
|
||||
const state = world.getState();
|
||||
const rawSeed = state.meta.hiddenSeed ?? state.meta.seed ?? state.id;
|
||||
return typeof rawSeed === 'string' || typeof rawSeed === 'number' ? rawSeed : String(rawSeed);
|
||||
};
|
||||
|
||||
export const createRandomizeCityTradeRateHandler = (options: {
|
||||
getWorld: () => InMemoryTurnWorld | null;
|
||||
}): MonthlyEventActionHandler => {
|
||||
return (_args, environment) => {
|
||||
const world = options.getWorld();
|
||||
if (!world) {
|
||||
return;
|
||||
}
|
||||
const rng = new RandUtil(
|
||||
new LiteHashDRBG(
|
||||
simpleSerialize(resolveHiddenSeed(world), 'randomizeCityTradeRate', environment.year, environment.month)
|
||||
)
|
||||
);
|
||||
|
||||
for (const city of world.listCities()) {
|
||||
const probability = CITY_TRADE_PROBABILITY_BY_LEVEL[city.level];
|
||||
if (probability === undefined) {
|
||||
throw new Error(`Unsupported city level for RandomizeCityTradeRate: ${city.level} (cityId=${city.id})`);
|
||||
}
|
||||
const trade = probability > 0 && rng.nextBool(probability) ? rng.nextRangeInt(95, 105) : null;
|
||||
const { trade: _previousTrade, ...metaWithoutTrade } = city.meta;
|
||||
world.updateCity(city.id, {
|
||||
meta: trade === null ? metaWithoutTrade : { ...metaWithoutTrade, trade },
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const compare = (left: readonly (number | null)[], operator: string, right: readonly (number | null)[]): boolean => {
|
||||
const normalize = (values: readonly (number | null)[]): string =>
|
||||
|
||||
@@ -34,7 +34,11 @@ import { createNeutralAuctionRegistrar } from '../auction/neutralRegistrar.js';
|
||||
import { createTournamentRewardFinalizer } from '../tournament/finalizer.js';
|
||||
import { createTournamentAutoStartHandler } from './tournamentAutoStart.js';
|
||||
import { createYearbookHandler } from './yearbookHandler.js';
|
||||
import { createMonthlyEventHandler, type MonthlyEventActionHandler } from './monthlyEventHandler.js';
|
||||
import {
|
||||
createMonthlyEventHandler,
|
||||
createRandomizeCityTradeRateHandler,
|
||||
type MonthlyEventActionHandler,
|
||||
} from './monthlyEventHandler.js';
|
||||
import { DatabaseTurnDaemonLease, TurnDaemonLeaseUnavailableError } from '../lifecycle/databaseTurnDaemonLease.js';
|
||||
|
||||
export interface TurnDaemonRuntimeOptions {
|
||||
@@ -142,6 +146,12 @@ const createTurnDaemonRuntimeWithLease = async (
|
||||
event.action.some((action) => Array.isArray(action) && action[0] === name)
|
||||
);
|
||||
const eventActions = new Map<string, MonthlyEventActionHandler>();
|
||||
eventActions.set(
|
||||
'RandomizeCityTradeRate',
|
||||
createRandomizeCityTradeRateHandler({
|
||||
getWorld: () => worldRef,
|
||||
})
|
||||
);
|
||||
eventActions.set('ProcessIncome', (_args, environment) => {
|
||||
void incomeHandler.onMonthChanged?.({
|
||||
previousYear: environment.month === 1 ? environment.year - 1 : environment.year,
|
||||
|
||||
@@ -246,7 +246,7 @@ const mapCityRow = (row: TurnEngineCityRow): City => {
|
||||
meta: {
|
||||
...meta,
|
||||
trust: row.trust,
|
||||
trade: row.trade,
|
||||
...(row.trade === null ? {} : { trade: row.trade }),
|
||||
region: row.region,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user