fix: Ref 게임 로직과 시나리오 풀 호환을 보정
월 경계, 전투 기술 상한, 연감과 베팅·설문·경매 정산 순서를 Ref 계약에 맞춘다.\n\n시나리오 일반 풀을 ENGINE mutation과 logical tick 기반으로 직렬화하고 914·915 catalog 및 조건부 100기 pool 실행 경계를 추가한다.\n\n경매 worker는 세대별 durable event만 만들고 ENGINE이 row lock 후 상태 전이와 정산을 단일 transaction으로 소유한다.
This commit is contained in:
@@ -4,6 +4,7 @@ import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic
|
||||
import type { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
|
||||
import type { WarStatName } from '@sammo-ts/logic/actionModules/types.js';
|
||||
import type { WarUnit } from './units.js';
|
||||
import type { WarTimeContext } from './types.js';
|
||||
import { WarTriggerCaller } from './triggers.js';
|
||||
|
||||
export interface WarActionContext<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
@@ -13,6 +14,8 @@ export interface WarActionContext<TriggerState extends GeneralTriggerState = Gen
|
||||
log?: ActionLogger;
|
||||
rng?: RandUtil;
|
||||
unit?: WarUnit<TriggerState>;
|
||||
time?: WarTimeContext;
|
||||
maxTechLevel?: number;
|
||||
}
|
||||
|
||||
export interface WarActionModule<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
|
||||
@@ -520,7 +520,7 @@ export const resolveWarAftermath = <TriggerState extends GeneralTriggerState = G
|
||||
|
||||
let rice = (cityKilled / 100) * 0.8;
|
||||
rice *= riceCoef;
|
||||
rice *= getTechCost(getMetaNumber(defenderNation.meta, 'tech', 0));
|
||||
rice *= getTechCost(getMetaNumber(defenderNation.meta, 'tech', 0), input.config.maxTechLevel);
|
||||
rice *= resolveCityTrainAtmos(input.time.year, input.time.startYear) / 100 - 0.2;
|
||||
rice = round(rice);
|
||||
|
||||
|
||||
@@ -278,7 +278,8 @@ export const resolveWarBattle = <TriggerState extends GeneralTriggerState = Gene
|
||||
true,
|
||||
resolveCrewType(crewTypeIndex, input.attacker.general.crewTypeId),
|
||||
attackerLogger,
|
||||
attackerPipeline
|
||||
attackerPipeline,
|
||||
input.time
|
||||
);
|
||||
|
||||
const cityLogger = loggerFactory({
|
||||
@@ -313,7 +314,8 @@ export const resolveWarBattle = <TriggerState extends GeneralTriggerState = Gene
|
||||
false,
|
||||
resolveCrewType(crewTypeIndex, defender.general.crewTypeId),
|
||||
defenderLogger,
|
||||
createPipeline(defender, crewTypeCatalog.warActionModule)
|
||||
createPipeline(defender, crewTypeCatalog.warActionModule),
|
||||
input.time
|
||||
);
|
||||
if (computeBattleOrder(unit, attackerUnit) <= 0) {
|
||||
continue;
|
||||
@@ -750,7 +752,8 @@ export const resolveDefenderOrder = <TriggerState extends GeneralTriggerState =
|
||||
true,
|
||||
resolveCrewType(crewTypeIndex, input.attacker.general.crewTypeId),
|
||||
attackerLogger,
|
||||
attackerPipeline
|
||||
attackerPipeline,
|
||||
input.time
|
||||
);
|
||||
|
||||
const defenderUnits: WarUnitGeneral<TriggerState>[] = [];
|
||||
@@ -770,7 +773,8 @@ export const resolveDefenderOrder = <TriggerState extends GeneralTriggerState =
|
||||
false,
|
||||
resolveCrewType(crewTypeIndex, defender.general.crewTypeId),
|
||||
defenderLogger,
|
||||
createPipeline(defender, crewTypeCatalog.warActionModule)
|
||||
createPipeline(defender, crewTypeCatalog.warActionModule),
|
||||
input.time
|
||||
);
|
||||
if (computeBattleOrder(unit, attackerUnit) <= 0) {
|
||||
continue;
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface WarEngineConfig {
|
||||
maxAtmosByWar: number;
|
||||
maxGeneralStat?: number;
|
||||
statUpgradeLimit?: number;
|
||||
maxTechLevel?: number;
|
||||
castleCrewTypeId: number;
|
||||
armTypes: WarArmTypes;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { WarStatName } from '@sammo-ts/logic/actionModules/types.js';
|
||||
import { getTechAbility, getTechCost } from '@sammo-ts/logic/world/unitSet.js';
|
||||
import { LEGACY_DEFAULT_MAX_LEVEL } from '@sammo-ts/logic/scenario/constants.js';
|
||||
import type { WarActionPipeline, WarActionContext } from '../actions.js';
|
||||
import type { WarEngineConfig } from '../types.js';
|
||||
import type { WarEngineConfig, WarTimeContext } from '../types.js';
|
||||
import type { WarCrewType } from '../crewType.js';
|
||||
import { clamp, clampMin, getMetaNumber, increaseMetaNumber, round } from '../utils.js';
|
||||
import { WAR_CRITICAL_RANGE, WarUnit, resolveNationTech } from './base.js';
|
||||
@@ -58,7 +58,8 @@ export class WarUnitGeneral<
|
||||
isAttacker: boolean,
|
||||
crewType: WarCrewType,
|
||||
logger: ActionLogger,
|
||||
pipeline: WarActionPipeline<TriggerState>
|
||||
pipeline: WarActionPipeline<TriggerState>,
|
||||
private readonly time?: WarTimeContext
|
||||
) {
|
||||
super(rng, config, crewType, logger, isAttacker, nation);
|
||||
this.actionPipeline = pipeline;
|
||||
@@ -89,6 +90,8 @@ export class WarUnitGeneral<
|
||||
log: this.logger,
|
||||
rng: this.rng,
|
||||
unit: this,
|
||||
...(this.time ? { time: this.time } : {}),
|
||||
maxTechLevel: this.config.maxTechLevel ?? 12,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -243,13 +246,13 @@ export class WarUnitGeneral<
|
||||
ratio = 50 + ratio / 2;
|
||||
}
|
||||
|
||||
const attack = this.getCrewType().attack + getTechAbility(tech);
|
||||
const attack = this.getCrewType().attack + getTechAbility(tech, this.config.maxTechLevel);
|
||||
return attack * (ratio / 100);
|
||||
}
|
||||
|
||||
public override getComputedDefence(): number {
|
||||
const tech = resolveNationTech(this.nation);
|
||||
const defence = this.getCrewType().defence + getTechAbility(tech);
|
||||
const defence = this.getCrewType().defence + getTechAbility(tech, this.config.maxTechLevel);
|
||||
const crew = this.general.crew / (7000 / 30) + 70;
|
||||
return defence * (crew / 100);
|
||||
}
|
||||
@@ -407,7 +410,7 @@ export class WarUnitGeneral<
|
||||
rice *= 0.8;
|
||||
}
|
||||
rice *= this.getCrewType().rice;
|
||||
rice *= getTechCost(resolveNationTech(this.nation));
|
||||
rice *= getTechCost(resolveNationTech(this.nation), this.config.maxTechLevel);
|
||||
rice = this.actionPipeline.onCalcStat(this.getActionContext(), 'killRice', rice);
|
||||
return rice;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user