refactor(logic): 과도한 수치 호환 보정을 제거한다

Core 수치 상태는 JavaScript와 PostgreSQL의 자연 정밀도를 유지한다. 정수 상태 경계와 절삭, 수입 배분 및 사망자 분할 순서는 보존한다.
This commit is contained in:
2026-08-24 19:17:06 +00:00
parent fc9fa0c9a6
commit 60dcf815ea
32 changed files with 148 additions and 522 deletions
+1 -7
View File
@@ -89,14 +89,12 @@ const isAssignedToOfficerCity = <TriggerState extends GeneralTriggerState>(
(key) => getMetaNumber(general.meta, key, Number.NaN) === cityId
);
// REF-COMPAT:BEGIN ref-dead-split-int-binding
const increaseDeadCounter = (city: City, delta: number): void => {
// Ref binds each `dead + %i` increment as an integer before MariaDB adds
// it. Truncate each 40/60 percent split independently; rounding the
// accumulated counter changes monthly recovery and war income.
city.meta[META_DEAD] = getDeadCounter(city) + Math.trunc(delta);
};
// REF-COMPAT:END ref-dead-split-int-binding
const isSupplyCity = (city: City): boolean => {
const raw = city.meta.supply;
@@ -168,11 +166,7 @@ const applyNationTechGain = <TriggerState extends GeneralTriggerState>(
const divisor = Math.max(config.initialNationGenLimit, total);
const currentTech = getMetaNumber(nation.meta, 'tech', 0);
const delta = gain / divisor;
// REF-COMPAT:BEGIN ref-mariadb-float-boundary
// Ref executes `tech + delta` inside MariaDB for battle gains, so the
// arithmetic starts from the stored binary32 value without a PHP text read.
nation.meta.tech = Math.fround(currentTech + delta);
// REF-COMPAT:END ref-mariadb-float-boundary
nation.meta.tech = currentTech + delta;
if (input.trace?.isEnabled('WAR_TECH_TRACE', { nationIds: [nation.id] })) {
input.trace.write('WAR_TECH_TRACE', {
engine: 'core',
+1 -6
View File
@@ -402,12 +402,7 @@ export class WarUnitGeneral<
nextExp *= 0.9;
}
const adjustedExp = this.actionPipeline.onCalcStat(this.getActionContext(), 'addDex', nextExp, { armType });
// REF-COMPAT:BEGIN ref-meekrodb-sql-precision
// PHP interpolates floats into MeekroDB SQL with precision=14 before
// MariaDB rounds the integer dex column. Normalize this accumulated
// battle value at the same boundary (for example ...499999999996 -> .5).
this.general.meta[key] = Number((base + adjustedExp).toPrecision(14));
// REF-COMPAT:END ref-meekrodb-sql-precision
this.general.meta[key] = base + adjustedExp;
}
public calcRiceConsumption(damage: number): number {
+1 -19
View File
@@ -9,25 +9,7 @@ export const clamp = (value: number, min: number, max: number): number => Math.m
export const clampMin = (value: number, min: number): number => (value < min ? min : value);
// REF-COMPAT:BEGIN ref-php-half-rounding
// PHP's round() compensates for small binary floating-point drift around a
// half boundary and rounds halves away from zero. War state is persisted to
// integer columns through legacy Util::round(), so Math.round() is not enough:
// e.g. accumulated siege damage can produce 4159.499999999999, which PHP
// rounds to 4160 while Math.round() returns 4159.
export const round = (value: number): number => {
if (!Number.isFinite(value)) {
return Math.round(value);
}
// PHP 8.3's round() uses a wider half-boundary fuzz than one JavaScript
// ulp at four-digit battle totals. Accumulating several fractional battle
// rewards can land about three ulps below .5 (for example
// 8719.4999999999945), which PHP still rounds upward.
const corrected = value + Math.sign(value) * Number.EPSILON * Math.max(1, Math.abs(value)) * 4;
return corrected < 0 ? Math.ceil(corrected - 0.5) : Math.floor(corrected + 0.5);
};
// REF-COMPAT:END ref-php-half-rounding
export const round = (value: number): number => Math.round(value);
export const getMetaNumber = (meta: Record<string, TriggerValue>, key: string, fallback = 0): number => {
const value = meta[key];