fix: 장수 런타임 레벨 상한을 Ref 기준으로 복구
가입 능력치 배분 상한과 런타임 레벨 상한을 분리하고, Ref 기본값 255를 공유 상수로 통합한다. 모병 후 레벨 유지와 국가 장수 목록의 고레벨 표시를 회귀 테스트로 고정한다.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { General } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { ScenarioConfig } from '@sammo-ts/logic/scenario/types.js';
|
||||
import { LEGACY_DEFAULT_MAX_LEVEL } from '@sammo-ts/logic/scenario/constants.js';
|
||||
import type { ScenarioMeta } from '@sammo-ts/logic/world/types.js';
|
||||
import type { WarAftermathConfig, WarEngineConfig, WarTimeContext } from '@sammo-ts/logic/war/types.js';
|
||||
import type { UnitSetDefinition } from '@sammo-ts/logic/world/types.js';
|
||||
@@ -208,7 +209,7 @@ export const buildWarConfig = (scenarioConfig: ScenarioConfig, unitSet: UnitSetD
|
||||
maxAtmosByCommand: resolveNumber(constValues, ['maxAtmosByCommand'], DEFAULT_WAR_CONFIG.maxAtmosByCommand),
|
||||
maxTrainByWar: resolveNumber(constValues, ['maxTrainByWar'], DEFAULT_WAR_CONFIG.maxTrainByWar),
|
||||
maxAtmosByWar: resolveNumber(constValues, ['maxAtmosByWar'], DEFAULT_WAR_CONFIG.maxAtmosByWar),
|
||||
maxGeneralStat: resolveNumber(constValues, ['maxLevel'], 255),
|
||||
maxGeneralStat: resolveNumber(constValues, ['maxLevel'], LEGACY_DEFAULT_MAX_LEVEL),
|
||||
statUpgradeLimit: resolveNumber(constValues, ['upgradeLimit'], 30),
|
||||
castleCrewTypeId,
|
||||
armTypes: {
|
||||
|
||||
@@ -26,6 +26,7 @@ import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js'
|
||||
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
import { parseArgsWithSchema } from '../parseArgs.js';
|
||||
import { LEGACY_DEFAULT_MAX_LEVEL } from '@sammo-ts/logic/scenario/constants.js';
|
||||
|
||||
const ACTION_NAME = '등용수락';
|
||||
const ACTION_KEY = 'che_등용수락';
|
||||
@@ -103,7 +104,7 @@ export class ActionResolver<
|
||||
const recruiterExpLevel = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
this.env.maxStatLevel ?? 255,
|
||||
this.env.maxStatLevel ?? LEGACY_DEFAULT_MAX_LEVEL,
|
||||
recruiterExperience < 1_000
|
||||
? Math.trunc(recruiterExperience / 100)
|
||||
: Math.trunc(Math.sqrt(recruiterExperience / 10))
|
||||
|
||||
@@ -13,6 +13,7 @@ import { LogCategory, LogFormat } from '@sammo-ts/logic/logging/types.js';
|
||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
||||
import { LEGACY_DEFAULT_MAX_LEVEL } from '@sammo-ts/logic/scenario/constants.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
export interface ProcureArgs {}
|
||||
@@ -32,7 +33,10 @@ export const roundLegacyAccumulatedInteger = (current: number, delta: number): n
|
||||
export const resolveLegacyExperienceLevel = (experience: number): number =>
|
||||
Math.max(
|
||||
0,
|
||||
Math.min(255, experience < 1_000 ? Math.trunc(experience / 100) : Math.trunc(Math.sqrt(experience / 10)))
|
||||
Math.min(
|
||||
LEGACY_DEFAULT_MAX_LEVEL,
|
||||
experience < 1_000 ? Math.trunc(experience / 100) : Math.trunc(Math.sqrt(experience / 10))
|
||||
)
|
||||
);
|
||||
export const resolveLegacyDedicationLevel = (dedication: number): number =>
|
||||
Math.max(0, Math.min(30, Math.ceil(Math.sqrt(dedication) / 10)));
|
||||
@@ -65,7 +69,7 @@ export class ActionResolver<
|
||||
const rawLeadership = general.stats.leadership * injuryMultiplier;
|
||||
const rawStrength = general.stats.strength * injuryMultiplier;
|
||||
const rawIntelligence = general.stats.intelligence * injuryMultiplier;
|
||||
const maxStat = 255;
|
||||
const maxStat = LEGACY_DEFAULT_MAX_LEVEL;
|
||||
const legacyStat = (stat: 'leadership' | 'strength' | 'intelligence', value: number): number =>
|
||||
Math.trunc(Math.max(0, Math.min(maxStat, this.pipeline.onCalcStat(context, stat, value))));
|
||||
let score =
|
||||
|
||||
@@ -27,6 +27,7 @@ import type {
|
||||
ActionResolveContext,
|
||||
} from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
||||
import { LEGACY_DEFAULT_MAX_LEVEL } from '@sammo-ts/logic/scenario/constants.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
import { clamp } from 'es-toolkit';
|
||||
|
||||
@@ -203,7 +204,7 @@ export class CommandResolver<TriggerState extends GeneralTriggerState = GeneralT
|
||||
} else if (this.config.statKey === 'intelligence') {
|
||||
rawStats.intelligence += Math.round(rawStats.strength / 4);
|
||||
}
|
||||
const maxStatLevel = this.env.maxStatLevel ?? 255;
|
||||
const maxStatLevel = this.env.maxStatLevel ?? LEGACY_DEFAULT_MAX_LEVEL;
|
||||
let score = clamp(rawStats[this.config.statKey], 0, maxStatLevel);
|
||||
score = this.pipeline.onCalcStat(context, this.config.statKey, score);
|
||||
|
||||
@@ -234,7 +235,7 @@ export class CommandResolver<TriggerState extends GeneralTriggerState = GeneralT
|
||||
strength: context.general.stats.strength + Math.round(context.general.stats.intelligence / 4),
|
||||
intelligence: context.general.stats.intelligence + Math.round(context.general.stats.strength / 4),
|
||||
};
|
||||
const maxStatLevel = this.env.maxStatLevel ?? 255;
|
||||
const maxStatLevel = this.env.maxStatLevel ?? LEGACY_DEFAULT_MAX_LEVEL;
|
||||
const leadership = this.pipeline.onCalcStat(
|
||||
context,
|
||||
'leadership',
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// Ref GameConstBase::$maxLevel. Scenario `stat.max` is a separate join-time
|
||||
// allocation rule and must not be used as this runtime fallback.
|
||||
export const LEGACY_DEFAULT_MAX_LEVEL = 255;
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './types.js';
|
||||
export * from './parseScenario.js';
|
||||
export * from './scenarioEffect.js';
|
||||
export * from './constants.js';
|
||||
|
||||
@@ -6,6 +6,7 @@ import { GeneralActionPipeline } from '@sammo-ts/logic/actionModules/general.js'
|
||||
import { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
|
||||
import { LogCategory, LogFormat, LogScope, type LogEntryDraft } from '@sammo-ts/logic/logging/types.js';
|
||||
import { buildCrewTypeIndex, getTechCost, getTechLevel } from '@sammo-ts/logic/world/unitSet.js';
|
||||
import { LEGACY_DEFAULT_MAX_LEVEL } from '@sammo-ts/logic/scenario/constants.js';
|
||||
import type { WarUnitReport } from './types.js';
|
||||
import type {
|
||||
ConquerCityOutcome,
|
||||
@@ -27,7 +28,6 @@ import {
|
||||
} from './utils.js';
|
||||
|
||||
const META_DEAD = 'dead';
|
||||
const MAX_EXP_LEVEL = 255;
|
||||
const MAX_DEDICATION_LEVEL = 30;
|
||||
|
||||
const updateLegacyProgressionLevels = (general: General): void => {
|
||||
@@ -35,7 +35,7 @@ const updateLegacyProgressionLevels = (general: General): void => {
|
||||
general.experience < 1_000
|
||||
? Math.trunc(general.experience / 100)
|
||||
: Math.trunc(Math.sqrt(general.experience / 10));
|
||||
general.meta.explevel = clamp(expLevel, 0, MAX_EXP_LEVEL);
|
||||
general.meta.explevel = clamp(expLevel, 0, LEGACY_DEFAULT_MAX_LEVEL);
|
||||
general.meta.dedlevel = clamp(Math.ceil(Math.sqrt(general.dedication) / 10), 0, MAX_DEDICATION_LEVEL);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
|
||||
import { LogFormat } from '@sammo-ts/logic/logging/types.js';
|
||||
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 { WarCrewType } from '../crewType.js';
|
||||
@@ -28,7 +29,6 @@ const META_RANK_PREFIX = 'rank_';
|
||||
const META_INTEL_EXP = 'intel_exp';
|
||||
const META_STRENGTH_EXP = 'strength_exp';
|
||||
const META_LEADERSHIP_EXP = 'leadership_exp';
|
||||
const MAX_EXP_LEVEL = 255;
|
||||
|
||||
const RANK_WARNUM = `${META_RANK_PREFIX}warnum`;
|
||||
const RANK_KILLNUM = `${META_RANK_PREFIX}killnum`;
|
||||
@@ -178,7 +178,7 @@ export class WarUnitGeneral<
|
||||
}) / 4
|
||||
);
|
||||
}
|
||||
const maxGeneralStat = this.config.maxGeneralStat ?? 255;
|
||||
const maxGeneralStat = this.config.maxGeneralStat ?? LEGACY_DEFAULT_MAX_LEVEL;
|
||||
value = clamp(value, 0, maxGeneralStat);
|
||||
if (withActions) {
|
||||
value = this.actionPipeline.onCalcStat(this.getActionContext(), statName, value);
|
||||
@@ -360,7 +360,7 @@ export class WarUnitGeneral<
|
||||
this.general.experience < 1000
|
||||
? Math.trunc(this.general.experience / 100)
|
||||
: Math.trunc(Math.sqrt(this.general.experience / 10));
|
||||
const resolvedExpLevel = clamp(nextExpLevel, 0, MAX_EXP_LEVEL);
|
||||
const resolvedExpLevel = clamp(nextExpLevel, 0, LEGACY_DEFAULT_MAX_LEVEL);
|
||||
this.general.meta[META_EXP_LEVEL] = resolvedExpLevel;
|
||||
if (resolvedExpLevel === previousExpLevel) {
|
||||
return;
|
||||
@@ -546,7 +546,7 @@ export class WarUnitGeneral<
|
||||
// one accumulated stat-exp threshold even though che_출병 itself does
|
||||
// not have the generic command progression tail.
|
||||
const limit = this.config.statUpgradeLimit ?? 30;
|
||||
const maxStat = this.config.maxGeneralStat ?? 255;
|
||||
const maxStat = this.config.maxGeneralStat ?? LEGACY_DEFAULT_MAX_LEVEL;
|
||||
const entries = [
|
||||
['leadership', META_LEADERSHIP_EXP, '통솔'],
|
||||
['strength', META_STRENGTH_EXP, '무력'],
|
||||
|
||||
Reference in New Issue
Block a user