feat: eslint 적용 및 관련 코드 일괄 수정
This commit is contained in:
@@ -1,11 +1,6 @@
|
||||
import type { RandUtil } from '@sammo-ts/common';
|
||||
|
||||
import type {
|
||||
City,
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
Nation,
|
||||
} from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
|
||||
import type { WarStatName } from '@sammo-ts/logic/triggers/types.js';
|
||||
import type { WarUnit } from './units.js';
|
||||
@@ -24,13 +19,9 @@ export interface WarActionModule<TriggerState extends GeneralTriggerState = Gene
|
||||
getName?(): string;
|
||||
getInfo?(): string;
|
||||
|
||||
getBattleInitTriggerList?(
|
||||
context: WarActionContext<TriggerState>
|
||||
): WarTriggerCaller | null;
|
||||
getBattleInitTriggerList?(context: WarActionContext<TriggerState>): WarTriggerCaller | null;
|
||||
|
||||
getBattlePhaseTriggerList?(
|
||||
context: WarActionContext<TriggerState>
|
||||
): WarTriggerCaller | null;
|
||||
getBattlePhaseTriggerList?(context: WarActionContext<TriggerState>): WarTriggerCaller | null;
|
||||
|
||||
onCalcStat?(
|
||||
context: WarActionContext<TriggerState>,
|
||||
@@ -53,9 +44,7 @@ export interface WarActionModule<TriggerState extends GeneralTriggerState = Gene
|
||||
): [number, number];
|
||||
}
|
||||
|
||||
export class WarActionPipeline<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
export class WarActionPipeline<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
// 전투용 iAction 파이프라인: 스탯/트리거/전투력 보정 흐름을 순서대로 적용한다.
|
||||
private readonly modules: WarActionModule<TriggerState>[];
|
||||
|
||||
@@ -63,9 +52,7 @@ export class WarActionPipeline<
|
||||
this.modules = modules.filter(Boolean) as WarActionModule<TriggerState>[];
|
||||
}
|
||||
|
||||
getBattleInitTriggerList(
|
||||
context: WarActionContext<TriggerState>
|
||||
): WarTriggerCaller {
|
||||
getBattleInitTriggerList(context: WarActionContext<TriggerState>): WarTriggerCaller {
|
||||
const caller = new WarTriggerCaller();
|
||||
for (const module of this.modules) {
|
||||
const triggers = module.getBattleInitTriggerList?.(context);
|
||||
@@ -76,9 +63,7 @@ export class WarActionPipeline<
|
||||
return caller;
|
||||
}
|
||||
|
||||
getBattlePhaseTriggerList(
|
||||
context: WarActionContext<TriggerState>
|
||||
): WarTriggerCaller {
|
||||
getBattlePhaseTriggerList(context: WarActionContext<TriggerState>): WarTriggerCaller {
|
||||
const caller = new WarTriggerCaller();
|
||||
for (const module of this.modules) {
|
||||
const triggers = module.getBattlePhaseTriggerList?.(context);
|
||||
@@ -132,11 +117,7 @@ export class WarActionPipeline<
|
||||
if (!module.getWarPowerMultiplier) {
|
||||
continue;
|
||||
}
|
||||
const [attMul, defMul] = module.getWarPowerMultiplier(
|
||||
context,
|
||||
unit,
|
||||
oppose
|
||||
);
|
||||
const [attMul, defMul] = module.getWarPowerMultiplier(context, unit, oppose);
|
||||
attack *= attMul;
|
||||
defence *= defMul;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,9 @@
|
||||
import {
|
||||
JosaUtil,
|
||||
LiteHashDRBG,
|
||||
RandUtil,
|
||||
} from '@sammo-ts/common';
|
||||
import { JosaUtil, LiteHashDRBG, RandUtil } from '@sammo-ts/common';
|
||||
|
||||
import type {
|
||||
City,
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
Nation,
|
||||
} from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
|
||||
import { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
|
||||
import { LogFormat, type LogEntryDraft } from '@sammo-ts/logic/logging/types.js';
|
||||
import {
|
||||
buildCrewTypeIndex,
|
||||
getTechCost,
|
||||
getTechLevel,
|
||||
} from '@sammo-ts/logic/world/unitSet.js';
|
||||
import { buildCrewTypeIndex, getTechCost, getTechLevel } from '@sammo-ts/logic/world/unitSet.js';
|
||||
import type { WarUnitReport } from './types.js';
|
||||
import type {
|
||||
ConquerCityOutcome,
|
||||
@@ -26,21 +13,12 @@ import type {
|
||||
WarAftermathTechContext,
|
||||
WarDiplomacyDelta,
|
||||
} from './types.js';
|
||||
import {
|
||||
clamp,
|
||||
clampMin,
|
||||
getMetaNumber,
|
||||
round,
|
||||
simpleSerialize,
|
||||
} from './utils.js';
|
||||
import { clamp, clampMin, getMetaNumber, round, simpleSerialize } from './utils.js';
|
||||
|
||||
const META_DEAD = 'dead';
|
||||
const META_CONFLICT = 'conflict';
|
||||
|
||||
const findReport = (
|
||||
reports: WarUnitReport[],
|
||||
predicate: (report: WarUnitReport) => boolean
|
||||
): WarUnitReport | null => {
|
||||
const findReport = (reports: WarUnitReport[], predicate: (report: WarUnitReport) => boolean): WarUnitReport | null => {
|
||||
for (const report of reports) {
|
||||
if (predicate(report)) {
|
||||
return report;
|
||||
@@ -49,8 +27,7 @@ const findReport = (
|
||||
return null;
|
||||
};
|
||||
|
||||
const getDeadCounter = (city: City): number =>
|
||||
getMetaNumber(city.meta, META_DEAD, 0);
|
||||
const getDeadCounter = (city: City): number => getMetaNumber(city.meta, META_DEAD, 0);
|
||||
|
||||
const setDeadCounter = (city: City, value: number): void => {
|
||||
city.meta[META_DEAD] = round(value);
|
||||
@@ -67,19 +44,12 @@ const isSupplyCity = (city: City): boolean => {
|
||||
return city.supplyState > 0;
|
||||
};
|
||||
|
||||
const resolveCityTrainAtmos = (year: number, startYear: number): number =>
|
||||
clamp(year - startYear + 59, 60, 110);
|
||||
const resolveCityTrainAtmos = (year: number, startYear: number): number => clamp(year - startYear + 59, 60, 110);
|
||||
|
||||
const isTechLimited = (
|
||||
tech: number,
|
||||
year: number,
|
||||
startYear: number,
|
||||
config: WarAftermathConfig
|
||||
): boolean => {
|
||||
const isTechLimited = (tech: number, year: number, startYear: number, config: WarAftermathConfig): boolean => {
|
||||
const relYear = clampMin(year - startYear, 0);
|
||||
const relMaxTech = clamp(
|
||||
Math.floor(relYear / config.techLevelIncYear) +
|
||||
config.initialAllowedTechLevel,
|
||||
Math.floor(relYear / config.techLevelIncYear) + config.initialAllowedTechLevel,
|
||||
1,
|
||||
config.maxTechLevel
|
||||
);
|
||||
@@ -92,15 +62,9 @@ const resolveNationGenCount = <TriggerState extends GeneralTriggerState>(
|
||||
generals: General<TriggerState>[],
|
||||
config: WarAftermathConfig
|
||||
): { total: number; effective: number } => {
|
||||
const fallback = generals.filter(
|
||||
(general) => general.nationId === nation.id
|
||||
).length;
|
||||
const fallback = generals.filter((general) => general.nationId === nation.id).length;
|
||||
let total = getMetaNumber(nation.meta, 'gennum', fallback);
|
||||
let effective = generals.filter(
|
||||
(general) =>
|
||||
general.nationId === nation.id &&
|
||||
general.npcState !== 5
|
||||
).length;
|
||||
let effective = generals.filter((general) => general.nationId === nation.id && general.npcState !== 5).length;
|
||||
|
||||
if (effective < config.initialNationGenLimit) {
|
||||
total = config.initialNationGenLimit;
|
||||
@@ -126,11 +90,7 @@ const applyNationTechGain = <TriggerState extends GeneralTriggerState>(
|
||||
});
|
||||
}
|
||||
|
||||
const { total, effective } = resolveNationGenCount(
|
||||
nation,
|
||||
input.generals,
|
||||
config
|
||||
);
|
||||
const { total, effective } = resolveNationGenCount(nation, input.generals, config);
|
||||
|
||||
if (total !== effective) {
|
||||
gain *= total / effective;
|
||||
@@ -145,10 +105,7 @@ const applyNationTechGain = <TriggerState extends GeneralTriggerState>(
|
||||
nation.meta.tech = round(tech);
|
||||
};
|
||||
|
||||
const resolveConquerNation = (
|
||||
city: City,
|
||||
attackerNationId: number
|
||||
): number => {
|
||||
const resolveConquerNation = (city: City, attackerNationId: number): number => {
|
||||
const rawConflict = city.meta[META_CONFLICT];
|
||||
if (!rawConflict) {
|
||||
return attackerNationId;
|
||||
@@ -183,26 +140,20 @@ const findNextCapital = (
|
||||
capturedCityId: number,
|
||||
oldCapital: City
|
||||
): City | null => {
|
||||
const candidates = cities.filter(
|
||||
(city) => city.nationId === defenderNationId && city.id !== capturedCityId
|
||||
);
|
||||
const candidates = cities.filter((city) => city.nationId === defenderNationId && city.id !== capturedCityId);
|
||||
if (!candidates.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const oldPos = getCityPosition(oldCapital);
|
||||
if (!oldPos) {
|
||||
return candidates.sort(
|
||||
(lhs, rhs) => rhs.population - lhs.population
|
||||
)[0]!;
|
||||
return candidates.sort((lhs, rhs) => rhs.population - lhs.population)[0]!;
|
||||
}
|
||||
|
||||
return candidates
|
||||
.map((city) => {
|
||||
const pos = getCityPosition(city);
|
||||
const distance = pos
|
||||
? Math.hypot(pos.x - oldPos.x, pos.y - oldPos.y)
|
||||
: Number.MAX_SAFE_INTEGER;
|
||||
const distance = pos ? Math.hypot(pos.x - oldPos.x, pos.y - oldPos.y) : Number.MAX_SAFE_INTEGER;
|
||||
return { city, distance };
|
||||
})
|
||||
.sort((lhs, rhs) => {
|
||||
@@ -224,14 +175,7 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
input: WarAftermathInput<TriggerState>,
|
||||
rng: RandUtil
|
||||
): ConquerCityOutcome<TriggerState> => {
|
||||
const {
|
||||
attackerNation,
|
||||
defenderNation,
|
||||
defenderCity,
|
||||
cities,
|
||||
generals,
|
||||
config,
|
||||
} = input;
|
||||
const { attackerNation, defenderNation, defenderCity, cities, generals, config } = input;
|
||||
const attacker = input.battle.attacker;
|
||||
|
||||
const logs: LogEntryDraft[] = [];
|
||||
@@ -247,9 +191,7 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
|
||||
const defenderNationId = defenderNation?.id ?? 0;
|
||||
const defenderNationName = defenderNation?.name ?? '공백지';
|
||||
const defenderNationDecoration = defenderNationId
|
||||
? `<D><b>${defenderNationName}</b></>의`
|
||||
: '공백지인';
|
||||
const defenderNationDecoration = defenderNationId ? `<D><b>${defenderNationName}</b></>의` : '공백지인';
|
||||
|
||||
const attackerNationName = attackerNation.name;
|
||||
const attackerGeneralName = attacker.name;
|
||||
@@ -260,13 +202,8 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
const josaYiGen = JosaUtil.pick(attackerGeneralName, '이');
|
||||
const josaYiCity = JosaUtil.pick(cityName, '이');
|
||||
|
||||
attackerLogger.pushGeneralActionLog(
|
||||
`<G><b>${cityName}</b></> 공략에 <S>성공</>했습니다.`,
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
attackerLogger.pushGeneralHistoryLog(
|
||||
`<G><b>${cityName}</b></>${josaUl} <S>점령</>`
|
||||
);
|
||||
attackerLogger.pushGeneralActionLog(`<G><b>${cityName}</b></> 공략에 <S>성공</>했습니다.`, LogFormat.PLAIN);
|
||||
attackerLogger.pushGeneralHistoryLog(`<G><b>${cityName}</b></>${josaUl} <S>점령</>`);
|
||||
attackerLogger.pushGlobalActionLog(
|
||||
`<Y>${attackerGeneralName}</>${josaYiGen} <G><b>${cityName}</b></> 공략에 <S>성공</>했습니다.`
|
||||
);
|
||||
@@ -285,9 +222,7 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
pushLoggers([defenderNationLogger], logs);
|
||||
}
|
||||
|
||||
const defenderCityCount = defenderNationId
|
||||
? cities.filter((city) => city.nationId === defenderNationId).length
|
||||
: 0;
|
||||
const defenderCityCount = defenderNationId ? cities.filter((city) => city.nationId === defenderNationId).length : 0;
|
||||
const nationCollapsed = defenderNationId !== 0 && defenderCityCount === 1;
|
||||
|
||||
let collapseRewardGold = 0;
|
||||
@@ -295,9 +230,7 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
|
||||
// 국가 붕괴 시 자원 손실과 포상 정산.
|
||||
if (nationCollapsed && defenderNation) {
|
||||
const defenderGenerals = generals.filter(
|
||||
(general) => general.nationId === defenderNationId
|
||||
);
|
||||
const defenderGenerals = generals.filter((general) => general.nationId === defenderNationId);
|
||||
let totalGoldLoss = 0;
|
||||
let totalRiceLoss = 0;
|
||||
|
||||
@@ -324,12 +257,8 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
affectedGenerals.add(general);
|
||||
}
|
||||
|
||||
collapseRewardGold =
|
||||
Math.max(0, defenderNation.gold - config.baseGold) * 0.5 +
|
||||
totalGoldLoss * 0.5;
|
||||
collapseRewardRice =
|
||||
Math.max(0, defenderNation.rice - config.baseRice) * 0.5 +
|
||||
totalRiceLoss * 0.5;
|
||||
collapseRewardGold = Math.max(0, defenderNation.gold - config.baseGold) * 0.5 + totalGoldLoss * 0.5;
|
||||
collapseRewardRice = Math.max(0, defenderNation.rice - config.baseRice) * 0.5 + totalRiceLoss * 0.5;
|
||||
|
||||
attackerNation.gold = round(attackerNation.gold + collapseRewardGold);
|
||||
attackerNation.rice = round(attackerNation.rice + collapseRewardRice);
|
||||
@@ -341,12 +270,7 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
|
||||
// 수도 함락 시 수도 이전 및 내부 사기/자원 페널티.
|
||||
if (!nationCollapsed && defenderNation && defenderNation.capitalCityId === defenderCity.id) {
|
||||
const nextCapital = findNextCapital(
|
||||
cities,
|
||||
defenderNationId,
|
||||
defenderCity.id,
|
||||
defenderCity
|
||||
);
|
||||
const nextCapital = findNextCapital(cities, defenderNationId, defenderCity.id, defenderCity);
|
||||
if (nextCapital) {
|
||||
defenderNation.capitalCityId = nextCapital.id;
|
||||
defenderNation.gold = round(defenderNation.gold * 0.5);
|
||||
@@ -373,8 +297,7 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
const conquerNation =
|
||||
conquerNationId === attackerNation.id
|
||||
? attackerNation
|
||||
: input.nations.find((nation) => nation.id === conquerNationId) ??
|
||||
attackerNation;
|
||||
: (input.nations.find((nation) => nation.id === conquerNationId) ?? attackerNation);
|
||||
|
||||
if (conquerNationId === attackerNation.id) {
|
||||
attacker.cityId = defenderCity.id;
|
||||
@@ -431,9 +354,7 @@ const resolveConquerCity = <TriggerState extends GeneralTriggerState>(
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveWarAftermath = <
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
>(
|
||||
export const resolveWarAftermath = <TriggerState extends GeneralTriggerState = GeneralTriggerState>(
|
||||
input: WarAftermathInput<TriggerState>
|
||||
): WarAftermathOutcome<TriggerState> => {
|
||||
const logs: LogEntryDraft[] = [];
|
||||
@@ -442,14 +363,8 @@ export const resolveWarAftermath = <
|
||||
const affectedCities = new Set<City>();
|
||||
const affectedGenerals = new Set<General<TriggerState>>();
|
||||
|
||||
const attackerReport = findReport(
|
||||
input.battle.reports,
|
||||
(report) => report.type === 'general' && report.isAttacker
|
||||
);
|
||||
const cityReport = findReport(
|
||||
input.battle.reports,
|
||||
(report) => report.type === 'city'
|
||||
);
|
||||
const attackerReport = findReport(input.battle.reports, (report) => report.type === 'general' && report.isAttacker);
|
||||
const cityReport = findReport(input.battle.reports, (report) => report.type === 'city');
|
||||
|
||||
const attackerKilled = attackerReport?.killed ?? 0;
|
||||
const attackerDead = attackerReport?.dead ?? 0;
|
||||
@@ -484,10 +399,7 @@ export const resolveWarAftermath = <
|
||||
defenderNation.rice = clampMin(defenderNation.rice - rice, 0);
|
||||
affectedNations.add(defenderNation);
|
||||
} else if (input.battle.conquered) {
|
||||
const bonus =
|
||||
defenderNation.capitalCityId === input.defenderCity.id
|
||||
? 1000
|
||||
: 500;
|
||||
const bonus = defenderNation.capitalCityId === input.defenderCity.id ? 1000 : 500;
|
||||
defenderNation.rice = round(defenderNation.rice + bonus);
|
||||
affectedNations.add(defenderNation);
|
||||
}
|
||||
@@ -496,31 +408,21 @@ export const resolveWarAftermath = <
|
||||
// 기술 경험치와 외교 사망자 수치 갱신.
|
||||
if (input.attackerNation.id && attackerReport) {
|
||||
const attackerTechGain = attackerDead * 0.012;
|
||||
applyNationTechGain(
|
||||
input.attackerNation,
|
||||
attackerTechGain,
|
||||
input,
|
||||
{
|
||||
side: 'attacker',
|
||||
nation: input.attackerNation,
|
||||
attackerReport,
|
||||
}
|
||||
);
|
||||
applyNationTechGain(input.attackerNation, attackerTechGain, input, {
|
||||
side: 'attacker',
|
||||
nation: input.attackerNation,
|
||||
attackerReport,
|
||||
});
|
||||
affectedNations.add(input.attackerNation);
|
||||
}
|
||||
|
||||
if (input.defenderNation && input.defenderNation.id !== 0 && attackerReport) {
|
||||
const defenderTechGain = attackerKilled * 0.009;
|
||||
applyNationTechGain(
|
||||
input.defenderNation,
|
||||
defenderTechGain,
|
||||
input,
|
||||
{
|
||||
side: 'defender',
|
||||
nation: input.defenderNation,
|
||||
attackerReport,
|
||||
}
|
||||
);
|
||||
applyNationTechGain(input.defenderNation, defenderTechGain, input, {
|
||||
side: 'defender',
|
||||
nation: input.defenderNation,
|
||||
attackerReport,
|
||||
});
|
||||
affectedNations.add(input.defenderNation);
|
||||
|
||||
diplomacyDeltas.push(
|
||||
|
||||
@@ -1,30 +1,13 @@
|
||||
import {
|
||||
JosaUtil,
|
||||
LiteHashDRBG,
|
||||
RandUtil,
|
||||
} from '@sammo-ts/common';
|
||||
import { JosaUtil, LiteHashDRBG, RandUtil } from '@sammo-ts/common';
|
||||
|
||||
import type {
|
||||
City,
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
} from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { City, General, GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||
import { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
|
||||
import { LogFormat } from '@sammo-ts/logic/logging/types.js';
|
||||
import { buildCrewTypeIndex as buildCrewTypeDefinitionIndex } from '@sammo-ts/logic/world/unitSet.js';
|
||||
import { WarActionPipeline } from './actions.js';
|
||||
import { WarCrewType } from './crewType.js';
|
||||
import {
|
||||
WarTriggerCaller,
|
||||
createWarTriggerEnv,
|
||||
type WarTriggerRegistry,
|
||||
} from './triggers.js';
|
||||
import type {
|
||||
WarBattleInput,
|
||||
WarBattleOutcome,
|
||||
WarGeneralInput,
|
||||
WarUnitReport,
|
||||
} from './types.js';
|
||||
import { WarTriggerCaller, createWarTriggerEnv, type WarTriggerRegistry } from './triggers.js';
|
||||
import type { WarBattleInput, WarBattleOutcome, WarGeneralInput, WarUnitReport } from './types.js';
|
||||
import { getMetaNumber } from './utils.js';
|
||||
import { WarUnitCity, WarUnitGeneral, type WarUnit } from './units.js';
|
||||
|
||||
@@ -33,15 +16,10 @@ const META_FULL_STRENGTH = 'fullStrength';
|
||||
const META_FULL_INTELLIGENCE = 'fullIntelligence';
|
||||
const META_DEFENCE_TRAIN = 'defenceTrain';
|
||||
|
||||
const defaultLoggerFactory = (options: {
|
||||
generalId?: number;
|
||||
nationId?: number;
|
||||
}): ActionLogger => new ActionLogger(options);
|
||||
const defaultLoggerFactory = (options: { generalId?: number; nationId?: number }): ActionLogger =>
|
||||
new ActionLogger(options);
|
||||
|
||||
const resolveCrewType = (
|
||||
crewTypeIndex: Map<number, WarCrewType>,
|
||||
crewTypeId: number
|
||||
): WarCrewType => {
|
||||
const resolveCrewType = (crewTypeIndex: Map<number, WarCrewType>, crewTypeId: number): WarCrewType => {
|
||||
const crewType = crewTypeIndex.get(crewTypeId);
|
||||
if (!crewType) {
|
||||
throw new Error(`Invalid crew type: ${crewTypeId}`);
|
||||
@@ -49,9 +27,7 @@ const resolveCrewType = (
|
||||
return crewType;
|
||||
};
|
||||
|
||||
const buildWarCrewTypeIndex = (
|
||||
unitSet: WarBattleInput['unitSet']
|
||||
): Map<number, WarCrewType> => {
|
||||
const buildWarCrewTypeIndex = (unitSet: WarBattleInput['unitSet']): Map<number, WarCrewType> => {
|
||||
const index = new Map<number, WarCrewType>();
|
||||
const crewTypes = buildCrewTypeDefinitionIndex(unitSet);
|
||||
for (const [id, crewType] of crewTypes) {
|
||||
@@ -62,8 +38,7 @@ const buildWarCrewTypeIndex = (
|
||||
|
||||
const createPipeline = <TriggerState extends GeneralTriggerState>(
|
||||
input: WarGeneralInput<TriggerState>
|
||||
): WarActionPipeline<TriggerState> =>
|
||||
new WarActionPipeline(input.modules ?? []);
|
||||
): WarActionPipeline<TriggerState> => new WarActionPipeline(input.modules ?? []);
|
||||
|
||||
const appendCrewTypeTriggers = (
|
||||
caller: WarTriggerCaller,
|
||||
@@ -91,10 +66,7 @@ const appendCrewTypeTriggers = (
|
||||
}
|
||||
};
|
||||
|
||||
const buildBattleInitTriggers = (
|
||||
unit: WarUnit,
|
||||
registry: WarTriggerRegistry | undefined
|
||||
): WarTriggerCaller => {
|
||||
const buildBattleInitTriggers = (unit: WarUnit, registry: WarTriggerRegistry | undefined): WarTriggerCaller => {
|
||||
const caller = new WarTriggerCaller();
|
||||
if (unit instanceof WarUnitGeneral) {
|
||||
const context = unit.getActionContext();
|
||||
@@ -104,10 +76,7 @@ const buildBattleInitTriggers = (
|
||||
return caller;
|
||||
};
|
||||
|
||||
const buildBattlePhaseTriggers = (
|
||||
unit: WarUnit,
|
||||
registry: WarTriggerRegistry | undefined
|
||||
): WarTriggerCaller => {
|
||||
const buildBattlePhaseTriggers = (unit: WarUnit, registry: WarTriggerRegistry | undefined): WarTriggerCaller => {
|
||||
const caller = new WarTriggerCaller();
|
||||
if (unit instanceof WarUnitGeneral) {
|
||||
appendCrewTypeTriggers(caller, unit, ['che_필살'], registry);
|
||||
@@ -118,18 +87,16 @@ const buildBattlePhaseTriggers = (
|
||||
return caller;
|
||||
};
|
||||
|
||||
const resolveFullStats = (general: General): {
|
||||
const resolveFullStats = (
|
||||
general: General
|
||||
): {
|
||||
leadership: number;
|
||||
strength: number;
|
||||
intelligence: number;
|
||||
} => ({
|
||||
leadership: getMetaNumber(general.meta, META_FULL_LEADERSHIP, general.stats.leadership),
|
||||
strength: getMetaNumber(general.meta, META_FULL_STRENGTH, general.stats.strength),
|
||||
intelligence: getMetaNumber(
|
||||
general.meta,
|
||||
META_FULL_INTELLIGENCE,
|
||||
general.stats.intelligence
|
||||
),
|
||||
intelligence: getMetaNumber(general.meta, META_FULL_INTELLIGENCE, general.stats.intelligence),
|
||||
});
|
||||
|
||||
const isSupplyCity = (city: City): boolean => {
|
||||
@@ -143,15 +110,10 @@ const isSupplyCity = (city: City): boolean => {
|
||||
return city.supplyState > 0;
|
||||
};
|
||||
|
||||
export const computeBattleOrder = (
|
||||
defender: WarUnit,
|
||||
attacker: WarUnitGeneral
|
||||
): number => {
|
||||
export const computeBattleOrder = (defender: WarUnit, attacker: WarUnitGeneral): number => {
|
||||
if (defender instanceof WarUnitCity) {
|
||||
const context = attacker.getActionContext();
|
||||
return attacker
|
||||
.getActionPipeline()
|
||||
.onCalcOpposeStat(context, 'cityBattleOrder', -1);
|
||||
return attacker.getActionPipeline().onCalcOpposeStat(context, 'cityBattleOrder', -1);
|
||||
}
|
||||
|
||||
if (!(defender instanceof WarUnitGeneral)) {
|
||||
@@ -174,18 +136,12 @@ export const computeBattleOrder = (
|
||||
return 0;
|
||||
}
|
||||
|
||||
const realStat =
|
||||
general.stats.leadership +
|
||||
general.stats.strength +
|
||||
general.stats.intelligence;
|
||||
const realStat = general.stats.leadership + general.stats.strength + general.stats.intelligence;
|
||||
const fullStats = resolveFullStats(general);
|
||||
const fullStat =
|
||||
fullStats.leadership + fullStats.strength + fullStats.intelligence;
|
||||
const fullStat = fullStats.leadership + fullStats.strength + fullStats.intelligence;
|
||||
const totalStat = (realStat + fullStat) / 2;
|
||||
|
||||
const totalCrew =
|
||||
(general.crew / 1_000_000) *
|
||||
Math.pow(general.train * general.atmos, 1.5);
|
||||
const totalCrew = (general.crew / 1_000_000) * Math.pow(general.train * general.atmos, 1.5);
|
||||
|
||||
return totalStat + totalCrew / 100;
|
||||
};
|
||||
@@ -231,17 +187,11 @@ const flushLoggers = (loggers: ActionLogger[]): Array<ReturnType<ActionLogger['f
|
||||
return logs;
|
||||
};
|
||||
|
||||
export const resolveWarBattle = <
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
>(
|
||||
export const resolveWarBattle = <TriggerState extends GeneralTriggerState = GeneralTriggerState>(
|
||||
input: WarBattleInput<TriggerState>
|
||||
): WarBattleOutcome<TriggerState> => {
|
||||
// process_war.php 전투 루프를 순수 로직으로 이식한다.
|
||||
const rng =
|
||||
input.rng ??
|
||||
new RandUtil(
|
||||
LiteHashDRBG.build(input.seed ?? '')
|
||||
);
|
||||
const rng = input.rng ?? new RandUtil(LiteHashDRBG.build(input.seed ?? ''));
|
||||
const loggerFactory = input.loggerFactory ?? defaultLoggerFactory;
|
||||
const triggerRegistry = input.triggerRegistry;
|
||||
|
||||
@@ -307,18 +257,11 @@ export const resolveWarBattle = <
|
||||
defenderGenerals.push(unit);
|
||||
}
|
||||
|
||||
if (
|
||||
defenderGenerals.length > 0 &&
|
||||
computeBattleOrder(cityUnit, attackerUnit) > 0
|
||||
) {
|
||||
if (defenderGenerals.length > 0 && computeBattleOrder(cityUnit, attackerUnit) > 0) {
|
||||
defenderUnits.push(cityUnit);
|
||||
}
|
||||
|
||||
defenderUnits.sort(
|
||||
(lhs, rhs) =>
|
||||
computeBattleOrder(rhs, attackerUnit) -
|
||||
computeBattleOrder(lhs, attackerUnit)
|
||||
);
|
||||
defenderUnits.sort((lhs, rhs) => computeBattleOrder(rhs, attackerUnit) - computeBattleOrder(lhs, attackerUnit));
|
||||
|
||||
const iter = defenderUnits.values();
|
||||
let defender: WarUnit<TriggerState> | null = null;
|
||||
@@ -345,8 +288,7 @@ export const resolveWarBattle = <
|
||||
let conquerCity = false;
|
||||
let logWritten = false;
|
||||
|
||||
const attackerNationName =
|
||||
(attackerUnit.getNationVar('name') as string | null) ?? 'UNKNOWN';
|
||||
const attackerNationName = (attackerUnit.getNationVar('name') as string | null) ?? 'UNKNOWN';
|
||||
const attackerName = attackerUnit.getName();
|
||||
const cityName = cityUnit.getName();
|
||||
const seedText = input.seed ? `(전투시드: ${input.seed})` : '';
|
||||
@@ -413,17 +355,21 @@ export const resolveWarBattle = <
|
||||
|
||||
const josaRo = JosaUtil.pick(attackerCrewName, '로');
|
||||
const josaUl = JosaUtil.pick(defenderCrewName, '을');
|
||||
attackerUnit.getLogger().pushGeneralActionLog(
|
||||
`${attackerCrewName}${josaRo} <Y>${defenderName}</>의 ${defenderCrewName}${josaUl} <M>공격</>합니다.`,
|
||||
LogFormat.MONTH
|
||||
);
|
||||
attackerUnit
|
||||
.getLogger()
|
||||
.pushGeneralActionLog(
|
||||
`${attackerCrewName}${josaRo} <Y>${defenderName}</>의 ${defenderCrewName}${josaUl} <M>공격</>합니다.`,
|
||||
LogFormat.MONTH
|
||||
);
|
||||
|
||||
const defJosaRo = JosaUtil.pick(defenderCrewName, '로');
|
||||
const defJosaUl = JosaUtil.pick(attackerCrewName, '을');
|
||||
defender.getLogger().pushGeneralActionLog(
|
||||
`${defenderCrewName}${defJosaRo} <Y>${attackerName}</>의 ${attackerCrewName}${defJosaUl} <M>수비</>합니다.`,
|
||||
LogFormat.MONTH
|
||||
);
|
||||
defender
|
||||
.getLogger()
|
||||
.pushGeneralActionLog(
|
||||
`${defenderCrewName}${defJosaRo} <Y>${attackerName}</>의 ${attackerCrewName}${defJosaUl} <M>수비</>합니다.`,
|
||||
LogFormat.MONTH
|
||||
);
|
||||
} else {
|
||||
const josaYiName = JosaUtil.pick(attackerName, '이');
|
||||
const josaRoCrew = JosaUtil.pick(attackerCrewName, '로');
|
||||
@@ -442,10 +388,7 @@ export const resolveWarBattle = <
|
||||
|
||||
const initCaller = buildBattleInitTriggers(attackerUnit, triggerRegistry);
|
||||
initCaller.merge(buildBattleInitTriggers(defender, triggerRegistry));
|
||||
initCaller.fire(
|
||||
{ rng, attacker: attackerUnit, defender },
|
||||
createWarTriggerEnv()
|
||||
);
|
||||
initCaller.fire({ rng, attacker: attackerUnit, defender }, createWarTriggerEnv());
|
||||
}
|
||||
|
||||
attackerUnit.beginPhase();
|
||||
@@ -453,10 +396,7 @@ export const resolveWarBattle = <
|
||||
|
||||
const battleCaller = buildBattlePhaseTriggers(attackerUnit, triggerRegistry);
|
||||
battleCaller.merge(buildBattlePhaseTriggers(defender, triggerRegistry));
|
||||
battleCaller.fire(
|
||||
{ rng, attacker: attackerUnit, defender },
|
||||
createWarTriggerEnv()
|
||||
);
|
||||
battleCaller.fire({ rng, attacker: attackerUnit, defender }, createWarTriggerEnv());
|
||||
|
||||
let deadDefender = attackerUnit.calcDamage();
|
||||
let deadAttacker = defender.calcDamage();
|
||||
@@ -486,18 +426,20 @@ export const resolveWarBattle = <
|
||||
attackerUnit.increaseKilled(deadDefender);
|
||||
defender.increaseKilled(deadAttacker);
|
||||
|
||||
const phaseNickname = defender.getPhase() < 0
|
||||
? '先'
|
||||
: `${attackerUnit.getPhase() + 1} `;
|
||||
const phaseNickname = defender.getPhase() < 0 ? '先' : `${attackerUnit.getPhase() + 1} `;
|
||||
|
||||
if (deadAttacker > 0 || deadDefender > 0) {
|
||||
attackerUnit.getLogger().pushGeneralBattleDetailLog(
|
||||
`${phaseNickname}: <Y1>【${attackerUnit.getName()}】</> <C>${attackerUnit.getHP()} (-${deadAttacker})</> VS <C>${defender.getHP()} (-${deadDefender})</> <Y1>【${defender.getName()}】</>`
|
||||
);
|
||||
attackerUnit
|
||||
.getLogger()
|
||||
.pushGeneralBattleDetailLog(
|
||||
`${phaseNickname}: <Y1>【${attackerUnit.getName()}】</> <C>${attackerUnit.getHP()} (-${deadAttacker})</> VS <C>${defender.getHP()} (-${deadDefender})</> <Y1>【${defender.getName()}】</>`
|
||||
);
|
||||
|
||||
defender.getLogger().pushGeneralBattleDetailLog(
|
||||
`${phaseNickname}: <Y1>【${defender.getName()}】</> <C>${defender.getHP()} (-${deadDefender})</> VS <C>${attackerUnit.getHP()} (-${deadAttacker})</> <Y1>【${attackerUnit.getName()}】</>`
|
||||
);
|
||||
defender
|
||||
.getLogger()
|
||||
.pushGeneralBattleDetailLog(
|
||||
`${phaseNickname}: <Y1>【${defender.getName()}】</> <C>${defender.getHP()} (-${deadDefender})</> VS <C>${attackerUnit.getHP()} (-${deadAttacker})</> <Y1>【${attackerUnit.getName()}】</>`
|
||||
);
|
||||
}
|
||||
|
||||
attackerUnit.addPhase();
|
||||
@@ -521,14 +463,18 @@ export const resolveWarBattle = <
|
||||
`<Y>${attackerUnit.getName()}</>의 ${attackerUnit.getCrewTypeName()}${josaYiCrew} 퇴각했습니다.`,
|
||||
LogFormat.MONTH
|
||||
);
|
||||
attackerUnit.getLogger().pushGeneralActionLog(
|
||||
attackerState.noRice ? '군량 부족으로 퇴각합니다.' : '퇴각했습니다.',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
defender.getLogger().pushGeneralActionLog(
|
||||
`<Y>${attackerUnit.getName()}</>의 ${attackerUnit.getCrewTypeName()}${josaYiCrew} 퇴각했습니다.`,
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
attackerUnit
|
||||
.getLogger()
|
||||
.pushGeneralActionLog(
|
||||
attackerState.noRice ? '군량 부족으로 퇴각합니다.' : '퇴각했습니다.',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
defender
|
||||
.getLogger()
|
||||
.pushGeneralActionLog(
|
||||
`<Y>${attackerUnit.getName()}</>의 ${attackerUnit.getCrewTypeName()}${josaYiCrew} 퇴각했습니다.`,
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -562,27 +508,25 @@ export const resolveWarBattle = <
|
||||
`<Y>${defender.getName()}</>의 ${defender.getCrewTypeName()}${josaYiDefCrew} 패퇴했습니다.`,
|
||||
LogFormat.MONTH
|
||||
);
|
||||
attackerUnit.getLogger().pushGeneralActionLog(
|
||||
`<Y>${defender.getName()}</>의 ${defender.getCrewTypeName()}${josaYiDefCrew} 패퇴했습니다.`,
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
defender.getLogger().pushGeneralActionLog(
|
||||
'군량 부족으로 패퇴합니다.',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
attackerUnit
|
||||
.getLogger()
|
||||
.pushGeneralActionLog(
|
||||
`<Y>${defender.getName()}</>의 ${defender.getCrewTypeName()}${josaYiDefCrew} 패퇴했습니다.`,
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
defender.getLogger().pushGeneralActionLog('군량 부족으로 패퇴합니다.', LogFormat.PLAIN);
|
||||
} else {
|
||||
attackerLogger.pushGlobalActionLog(
|
||||
`<Y>${defender.getName()}</>의 ${defender.getCrewTypeName()}${josaYiDefCrew} 전멸했습니다.`,
|
||||
LogFormat.MONTH
|
||||
);
|
||||
attackerUnit.getLogger().pushGeneralActionLog(
|
||||
`<Y>${defender.getName()}</>의 ${defender.getCrewTypeName()}${josaYiDefCrew} 전멸했습니다.`,
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
defender.getLogger().pushGeneralActionLog(
|
||||
'전멸했습니다.',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
attackerUnit
|
||||
.getLogger()
|
||||
.pushGeneralActionLog(
|
||||
`<Y>${defender.getName()}</>의 ${defender.getCrewTypeName()}${josaYiDefCrew} 전멸했습니다.`,
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
defender.getLogger().pushGeneralActionLog('전멸했습니다.', LogFormat.PLAIN);
|
||||
}
|
||||
|
||||
if (attackerUnit.getPhase() >= attackerUnit.getMaxPhase()) {
|
||||
@@ -623,10 +567,7 @@ export const resolveWarBattle = <
|
||||
}
|
||||
}
|
||||
|
||||
const logs = flushLoggers([
|
||||
attackerLogger,
|
||||
...defenderGenerals.map((unit) => unit.getLogger()),
|
||||
]);
|
||||
const logs = flushLoggers([attackerLogger, ...defenderGenerals.map((unit) => unit.getLogger())]);
|
||||
|
||||
const reports: WarUnitReport[] = [
|
||||
resolveUnitReport(attackerUnit),
|
||||
@@ -644,23 +585,15 @@ export const resolveWarBattle = <
|
||||
metrics: {
|
||||
attackerPhase: attackerUnit.getPhase(),
|
||||
attackerActivatedSkills: attackerUnit.getActivatedSkillLog(),
|
||||
defenderActivatedSkills: defenderUnits.map((unit) =>
|
||||
unit.getActivatedSkillLog()
|
||||
),
|
||||
defenderActivatedSkills: defenderUnits.map((unit) => unit.getActivatedSkillLog()),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveDefenderOrder = <
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
>(
|
||||
export const resolveDefenderOrder = <TriggerState extends GeneralTriggerState = GeneralTriggerState>(
|
||||
input: WarBattleInput<TriggerState>
|
||||
): number[] => {
|
||||
const rng =
|
||||
input.rng ??
|
||||
new RandUtil(
|
||||
LiteHashDRBG.build(input.seed ?? '')
|
||||
);
|
||||
const rng = input.rng ?? new RandUtil(LiteHashDRBG.build(input.seed ?? ''));
|
||||
const loggerFactory = input.loggerFactory ?? defaultLoggerFactory;
|
||||
|
||||
const crewTypeIndex = buildWarCrewTypeIndex(input.unitSet);
|
||||
@@ -709,11 +642,7 @@ export const resolveDefenderOrder = <
|
||||
defenderUnits.push(unit);
|
||||
}
|
||||
|
||||
defenderUnits.sort(
|
||||
(lhs, rhs) =>
|
||||
computeBattleOrder(rhs, attackerUnit) -
|
||||
computeBattleOrder(lhs, attackerUnit)
|
||||
);
|
||||
defenderUnits.sort((lhs, rhs) => computeBattleOrder(rhs, attackerUnit) - computeBattleOrder(lhs, attackerUnit));
|
||||
|
||||
return defenderUnits.map((unit) => unit.getGeneral().id);
|
||||
};
|
||||
|
||||
@@ -24,9 +24,7 @@ export const createWarTriggerEnv = (): WarTriggerEnv => ({
|
||||
|
||||
export class WarTriggerCaller extends TriggerCaller<WarTriggerContext, WarTriggerEnv> {}
|
||||
|
||||
export type WarTriggerFactory = (
|
||||
unit: WarUnit
|
||||
) => WarTrigger | WarTriggerCaller | null;
|
||||
export type WarTriggerFactory = (unit: WarUnit) => WarTrigger | WarTriggerCaller | null;
|
||||
|
||||
export type WarTriggerRegistry = Record<string, WarTriggerFactory>;
|
||||
|
||||
@@ -51,10 +49,7 @@ export abstract class BaseWarUnitTrigger implements WarTrigger {
|
||||
return `${this.priority}_${this.constructor.name}_${this.unit.getUnitId()}_${this.raiseType}`;
|
||||
}
|
||||
|
||||
action(
|
||||
context: WarTriggerContext,
|
||||
env: WarTriggerEnv
|
||||
): WarTriggerEnv {
|
||||
action(context: WarTriggerContext, env: WarTriggerEnv): WarTriggerEnv {
|
||||
const nextEnv: WarTriggerEnv = {
|
||||
e_attacker: env.e_attacker ?? {},
|
||||
e_defender: env.e_defender ?? {},
|
||||
|
||||
@@ -54,15 +54,8 @@ class ActivateTrigger extends BaseWarUnitTrigger {
|
||||
}
|
||||
selfEnv['치료발동'] = true;
|
||||
|
||||
oppose
|
||||
.getLogger()
|
||||
.pushGeneralBattleDetailLog(
|
||||
'상대가 <R>치료</>했다!',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
self
|
||||
.getLogger()
|
||||
.pushGeneralBattleDetailLog('<C>치료</>했다!', LogFormat.PLAIN);
|
||||
oppose.getLogger().pushGeneralBattleDetailLog('상대가 <R>치료</>했다!', LogFormat.PLAIN);
|
||||
self.getLogger().pushGeneralBattleDetailLog('<C>치료</>했다!', LogFormat.PLAIN);
|
||||
|
||||
oppose.multiplyWarPowerMultiply(0.7);
|
||||
if (self instanceof WarUnitGeneral) {
|
||||
@@ -79,9 +72,5 @@ export const triggerModule: WarTriggerModule = {
|
||||
key: 'che_의술',
|
||||
name: '의술',
|
||||
info: '[전투] 페이즈마다 치료 발동(아군 피해 30% 감소, 부상 회복)',
|
||||
createTriggerList: (unit) =>
|
||||
new WarTriggerCaller(
|
||||
new AttemptTrigger(unit),
|
||||
new ActivateTrigger(unit)
|
||||
),
|
||||
createTriggerList: (unit) => new WarTriggerCaller(new AttemptTrigger(unit), new ActivateTrigger(unit)),
|
||||
};
|
||||
|
||||
@@ -11,14 +11,7 @@ export class CheSnipingAttemptTrigger extends BaseWarUnitTrigger {
|
||||
private readonly woundMax: number;
|
||||
private readonly addAtmos: number;
|
||||
|
||||
constructor(
|
||||
unit: WarUnit,
|
||||
raiseType: number,
|
||||
ratio: number,
|
||||
woundMin: number,
|
||||
woundMax: number,
|
||||
addAtmos = 20
|
||||
) {
|
||||
constructor(unit: WarUnit, raiseType: number, ratio: number, woundMin: number, woundMax: number, addAtmos = 20) {
|
||||
super(unit, TriggerPriority.Pre + 100, raiseType);
|
||||
this.ratio = ratio;
|
||||
this.woundMin = woundMin;
|
||||
@@ -85,27 +78,12 @@ export class CheSnipingActivateTrigger extends BaseWarUnitTrigger {
|
||||
|
||||
if (oppose instanceof WarUnitGeneral) {
|
||||
self.getLogger().pushGeneralActionLog('상대를 <C>저격</>했다!', LogFormat.PLAIN);
|
||||
self.getLogger().pushGeneralBattleDetailLog(
|
||||
'상대를 <C>저격</>했다!',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
oppose.getLogger().pushGeneralActionLog(
|
||||
'상대에게 <R>저격</>당했다!',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
oppose.getLogger().pushGeneralBattleDetailLog(
|
||||
'상대에게 <R>저격</>당했다!',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
self.getLogger().pushGeneralBattleDetailLog('상대를 <C>저격</>했다!', LogFormat.PLAIN);
|
||||
oppose.getLogger().pushGeneralActionLog('상대에게 <R>저격</>당했다!', LogFormat.PLAIN);
|
||||
oppose.getLogger().pushGeneralBattleDetailLog('상대에게 <R>저격</>당했다!', LogFormat.PLAIN);
|
||||
} else {
|
||||
self.getLogger().pushGeneralActionLog(
|
||||
'성벽 수비대장을 <C>저격</>했다!',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
self.getLogger().pushGeneralBattleDetailLog(
|
||||
'성벽 수비대장을 <C>저격</>했다!',
|
||||
LogFormat.PLAIN
|
||||
);
|
||||
self.getLogger().pushGeneralActionLog('성벽 수비대장을 <C>저격</>했다!', LogFormat.PLAIN);
|
||||
self.getLogger().pushGeneralBattleDetailLog('성벽 수비대장을 <C>저격</>했다!', LogFormat.PLAIN);
|
||||
}
|
||||
|
||||
const addAtmos = Number(selfEnv['addAtmos'] ?? 0);
|
||||
@@ -115,8 +93,7 @@ export class CheSnipingActivateTrigger extends BaseWarUnitTrigger {
|
||||
const woundMin = Number(selfEnv['woundMin'] ?? 10);
|
||||
const woundMax = Number(selfEnv['woundMax'] ?? 80);
|
||||
oppose.getGeneral().injury = clamp(
|
||||
oppose.getGeneral().injury +
|
||||
self.rng.nextRangeInt(woundMin, woundMax),
|
||||
oppose.getGeneral().injury + self.rng.nextRangeInt(woundMin, woundMax),
|
||||
0,
|
||||
80
|
||||
);
|
||||
|
||||
@@ -53,12 +53,8 @@ class ActivateTrigger extends BaseWarUnitTrigger {
|
||||
}
|
||||
selfEnv['필살발동'] = true;
|
||||
|
||||
oppose
|
||||
.getLogger()
|
||||
.pushGeneralBattleDetailLog('상대의 <R>필살</>공격!</>', LogFormat.PLAIN);
|
||||
self
|
||||
.getLogger()
|
||||
.pushGeneralBattleDetailLog('<C>필살</>공격!</>', LogFormat.PLAIN);
|
||||
oppose.getLogger().pushGeneralBattleDetailLog('상대의 <R>필살</>공격!</>', LogFormat.PLAIN);
|
||||
self.getLogger().pushGeneralBattleDetailLog('<C>필살</>공격!</>', LogFormat.PLAIN);
|
||||
|
||||
self.multiplyWarPowerMultiply(self.criticalDamage());
|
||||
return true;
|
||||
@@ -69,9 +65,5 @@ export const triggerModule: WarTriggerModule = {
|
||||
key: 'che_필살',
|
||||
name: '필살',
|
||||
info: '[전투] 페이즈마다 확률로 필살 발동',
|
||||
createTriggerList: (unit) =>
|
||||
new WarTriggerCaller(
|
||||
new AttemptTrigger(unit),
|
||||
new ActivateTrigger(unit)
|
||||
),
|
||||
createTriggerList: (unit) => new WarTriggerCaller(new AttemptTrigger(unit), new ActivateTrigger(unit)),
|
||||
};
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import type { WarTriggerModule, WarTriggerModuleExport } from './types.js';
|
||||
import type { WarTriggerRegistry } from '@sammo-ts/logic/war/triggers.js';
|
||||
|
||||
export const WAR_TRIGGER_KEYS = [
|
||||
'che_필살',
|
||||
'che_의술',
|
||||
] as const;
|
||||
export const WAR_TRIGGER_KEYS = ['che_필살', 'che_의술'] as const;
|
||||
|
||||
export type WarTriggerKey =
|
||||
(typeof WAR_TRIGGER_KEYS)[number];
|
||||
export type WarTriggerKey = (typeof WAR_TRIGGER_KEYS)[number];
|
||||
|
||||
export type WarTriggerImporter = () => Promise<WarTriggerModuleExport>;
|
||||
|
||||
@@ -22,9 +18,7 @@ export const isWarTriggerKey = (value: string): value is WarTriggerKey =>
|
||||
export class WarTriggerLoader {
|
||||
private readonly cache = new Map<WarTriggerKey, Promise<WarTriggerModule>>();
|
||||
|
||||
constructor(
|
||||
private readonly importers: Record<WarTriggerKey, WarTriggerImporter> = defaultImporters
|
||||
) {}
|
||||
constructor(private readonly importers: Record<WarTriggerKey, WarTriggerImporter> = defaultImporters) {}
|
||||
|
||||
async load(key: WarTriggerKey): Promise<WarTriggerModule> {
|
||||
const cached = this.cache.get(key);
|
||||
@@ -41,9 +35,7 @@ export class WarTriggerLoader {
|
||||
}
|
||||
const resolved = module.triggerModule;
|
||||
if (resolved.key !== key) {
|
||||
throw new Error(
|
||||
`War trigger key mismatch: expected ${key}, got ${resolved.key}`
|
||||
);
|
||||
throw new Error(`War trigger key mismatch: expected ${key}, got ${resolved.key}`);
|
||||
}
|
||||
return resolved;
|
||||
});
|
||||
@@ -68,9 +60,7 @@ export const loadWarTriggerModules = async (
|
||||
return modules;
|
||||
};
|
||||
|
||||
export const createWarTriggerRegistry = (
|
||||
modules: WarTriggerModule[]
|
||||
): WarTriggerRegistry => {
|
||||
export const createWarTriggerRegistry = (modules: WarTriggerModule[]): WarTriggerRegistry => {
|
||||
const registry: WarTriggerRegistry = {};
|
||||
for (const module of modules) {
|
||||
registry[module.key] = (unit) => module.createTriggerList(unit);
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import type { RandUtil } from '@sammo-ts/common';
|
||||
|
||||
import type {
|
||||
City,
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
Nation,
|
||||
} from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
|
||||
import type { LogEntryDraft } from '@sammo-ts/logic/logging/types.js';
|
||||
import type { UnitSetDefinition } from '@sammo-ts/logic/world/types.js';
|
||||
@@ -38,9 +33,7 @@ export interface WarTimeContext {
|
||||
startYear: number;
|
||||
}
|
||||
|
||||
export interface WarGeneralInput<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
export interface WarGeneralInput<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
general: General<TriggerState>;
|
||||
city: City;
|
||||
nation: Nation | null;
|
||||
@@ -48,9 +41,7 @@ export interface WarGeneralInput<
|
||||
modules?: Array<WarActionModule<TriggerState> | null | undefined>;
|
||||
}
|
||||
|
||||
export interface WarBattleInput<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
export interface WarBattleInput<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
seed?: string;
|
||||
rng?: RandUtil;
|
||||
unitSet: UnitSetDefinition;
|
||||
@@ -79,9 +70,7 @@ export interface WarBattleMetrics {
|
||||
defenderActivatedSkills: Array<Record<string, number>>;
|
||||
}
|
||||
|
||||
export interface WarBattleOutcome<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
export interface WarBattleOutcome<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
attacker: General<TriggerState>;
|
||||
defenders: General<TriggerState>[];
|
||||
defenderCity: City;
|
||||
@@ -115,9 +104,7 @@ export interface WarDiplomacyDelta {
|
||||
deadDelta: number;
|
||||
}
|
||||
|
||||
export interface ConquerCityOutcome<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
export interface ConquerCityOutcome<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
conquerNationId: number;
|
||||
nationCollapsed: boolean;
|
||||
collapseRewardGold: number;
|
||||
@@ -128,9 +115,7 @@ export interface ConquerCityOutcome<
|
||||
generals: General<TriggerState>[];
|
||||
}
|
||||
|
||||
export interface WarAftermathInput<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
export interface WarAftermathInput<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
battle: WarBattleOutcome<TriggerState>;
|
||||
attackerNation: Nation;
|
||||
defenderNation: Nation | null;
|
||||
@@ -147,9 +132,7 @@ export interface WarAftermathInput<
|
||||
calcNationTechGain?: (context: WarAftermathTechContext) => number;
|
||||
}
|
||||
|
||||
export interface WarAftermathOutcome<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
export interface WarAftermathOutcome<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
nations: Nation[];
|
||||
cities: City[];
|
||||
generals: General<TriggerState>[];
|
||||
|
||||
+32
-128
@@ -40,8 +40,7 @@ const META_LEADERSHIP_EXP = 'leadershipExp';
|
||||
const RANK_WARNUM = `${META_RANK_PREFIX}warnum`;
|
||||
const RANK_KILLNUM = `${META_RANK_PREFIX}killnum`;
|
||||
|
||||
const toDexStatName = (armType: number): WarStatName =>
|
||||
`dex${armType}` as WarStatName;
|
||||
const toDexStatName = (armType: number): WarStatName => `dex${armType}` as WarStatName;
|
||||
const RANK_DEATHNUM = `${META_RANK_PREFIX}deathnum`;
|
||||
const RANK_OCCUPIED = `${META_RANK_PREFIX}occupied`;
|
||||
const RANK_KILLCREW = `${META_RANK_PREFIX}killcrew`;
|
||||
@@ -51,8 +50,7 @@ const RANK_DEATHCREW_PERSON = `${META_RANK_PREFIX}deathcrew_person`;
|
||||
|
||||
const WAR_CRITICAL_RANGE: [number, number] = [1.3, 2.0];
|
||||
|
||||
const resolveNationTech = (nation: Nation | null): number =>
|
||||
nation ? getMetaNumber(nation.meta, 'tech', 0) : 0;
|
||||
const resolveNationTech = (nation: Nation | null): number => (nation ? getMetaNumber(nation.meta, 'tech', 0) : 0);
|
||||
|
||||
const resolveNationVar = (nation: Nation | null, key: string): TriggerValue | null => {
|
||||
if (!nation) {
|
||||
@@ -79,9 +77,7 @@ const resolveNationVar = (nation: Nation | null, key: string): TriggerValue | nu
|
||||
};
|
||||
|
||||
// 전투 유닛 공통 상태와 수치 계산(legacy WarUnit 계열 포팅).
|
||||
export abstract class WarUnit<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
export abstract class WarUnit<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
|
||||
private static nextUnitId = 1;
|
||||
private readonly unitId: number;
|
||||
|
||||
@@ -226,8 +222,7 @@ export abstract class WarUnit<
|
||||
if (!state) {
|
||||
continue;
|
||||
}
|
||||
this.logActivatedSkill[skillName] =
|
||||
(this.logActivatedSkill[skillName] ?? 0) + 1;
|
||||
this.logActivatedSkill[skillName] = (this.logActivatedSkill[skillName] ?? 0) + 1;
|
||||
}
|
||||
this.activatedSkill = {};
|
||||
}
|
||||
@@ -237,8 +232,7 @@ export abstract class WarUnit<
|
||||
}
|
||||
|
||||
public hasActivatedSkillOnLog(skillName: string): number {
|
||||
return (this.logActivatedSkill[skillName] ?? 0) +
|
||||
(this.hasActivatedSkill(skillName) ? 1 : 0);
|
||||
return (this.logActivatedSkill[skillName] ?? 0) + (this.hasActivatedSkill(skillName) ? 1 : 0);
|
||||
}
|
||||
|
||||
public getActivatedSkillLog(): Record<string, number> {
|
||||
@@ -295,9 +289,7 @@ export abstract class WarUnit<
|
||||
warPower *= getDexLog(myDex, opposeDex);
|
||||
|
||||
warPower *= this.getCrewType().getAttackCoef(oppose.getCrewType());
|
||||
opposeWarPowerMultiply *= this.getCrewType().getDefenceCoef(
|
||||
oppose.getCrewType()
|
||||
);
|
||||
opposeWarPowerMultiply *= this.getCrewType().getDefenceCoef(oppose.getCrewType());
|
||||
|
||||
this.warPower = warPower;
|
||||
oppose.setWarPowerMultiply(opposeWarPowerMultiply);
|
||||
@@ -394,7 +386,7 @@ export abstract class WarUnit<
|
||||
|
||||
// 장수 전투 유닛(legacy WarUnitGeneral 포팅).
|
||||
export class WarUnitGeneral<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||
> extends WarUnit<TriggerState> {
|
||||
private readonly actionPipeline: WarActionPipeline<TriggerState>;
|
||||
private killedPerson = 0;
|
||||
@@ -470,8 +462,8 @@ export class WarUnitGeneral<
|
||||
const baseTurnTime = this.isAttacker()
|
||||
? getMetaString(this.general.meta, META_TURN_TIME)
|
||||
: oppose instanceof WarUnitGeneral
|
||||
? getMetaString(oppose.general.meta, META_TURN_TIME)
|
||||
: getMetaString(this.general.meta, META_TURN_TIME);
|
||||
? getMetaString(oppose.general.meta, META_TURN_TIME)
|
||||
: getMetaString(this.general.meta, META_TURN_TIME);
|
||||
if (!baseTurnTime) {
|
||||
return;
|
||||
}
|
||||
@@ -486,36 +478,18 @@ export class WarUnitGeneral<
|
||||
const base = this.getCrewType().speed;
|
||||
const aux = { isAttacker: this.isAttacker() };
|
||||
const context = this.getActionContext();
|
||||
const phase = this.actionPipeline.onCalcStat(
|
||||
context,
|
||||
'initWarPhase',
|
||||
base,
|
||||
aux
|
||||
);
|
||||
const phase = this.actionPipeline.onCalcStat(context, 'initWarPhase', base, aux);
|
||||
return phase + this.bonusPhase;
|
||||
}
|
||||
|
||||
private resolveStatValue(statName: keyof StatBlock, base: number): number {
|
||||
return this.actionPipeline.onCalcStat(
|
||||
this.getActionContext(),
|
||||
statName,
|
||||
base
|
||||
);
|
||||
return this.actionPipeline.onCalcStat(this.getActionContext(), statName, base);
|
||||
}
|
||||
|
||||
private resolveMainStat(armType: number): number {
|
||||
const leadership = this.resolveStatValue(
|
||||
'leadership',
|
||||
this.general.stats.leadership
|
||||
);
|
||||
const strength = this.resolveStatValue(
|
||||
'strength',
|
||||
this.general.stats.strength
|
||||
);
|
||||
const intelligence = this.resolveStatValue(
|
||||
'intelligence',
|
||||
this.general.stats.intelligence
|
||||
);
|
||||
const leadership = this.resolveStatValue('leadership', this.general.stats.leadership);
|
||||
const strength = this.resolveStatValue('strength', this.general.stats.strength);
|
||||
const intelligence = this.resolveStatValue('intelligence', this.general.stats.intelligence);
|
||||
|
||||
if (armType === this.config.armTypes.wizard) {
|
||||
return intelligence;
|
||||
@@ -529,24 +503,18 @@ export class WarUnitGeneral<
|
||||
return strength;
|
||||
}
|
||||
|
||||
private resolveOpposeStatValue(
|
||||
statName: WarStatName,
|
||||
value: number,
|
||||
aux?: Record<string, unknown>
|
||||
): number {
|
||||
private resolveOpposeStatValue(statName: WarStatName, value: number, aux?: Record<string, unknown>): number {
|
||||
const oppose = this.getOppose();
|
||||
if (!(oppose instanceof WarUnitGeneral)) {
|
||||
return value;
|
||||
}
|
||||
return oppose
|
||||
.getActionPipeline()
|
||||
.onCalcOpposeStat(this.getActionContext(), statName, value, aux);
|
||||
return oppose.getActionPipeline().onCalcOpposeStat(this.getActionContext(), statName, value, aux);
|
||||
}
|
||||
|
||||
public override getDex(crewType: WarCrewType): number {
|
||||
const armType =
|
||||
crewType.armType === this.config.armTypes.castle
|
||||
? this.config.armTypes.siege ?? crewType.armType
|
||||
? (this.config.armTypes.siege ?? crewType.armType)
|
||||
: crewType.armType;
|
||||
const base = getMetaNumber(this.general.meta, `${META_DEX_PREFIX}${armType}`);
|
||||
const aux = {
|
||||
@@ -554,12 +522,7 @@ export class WarUnitGeneral<
|
||||
opposeType: this.oppose?.getCrewType() ?? null,
|
||||
};
|
||||
const statName = toDexStatName(armType);
|
||||
let dex = this.actionPipeline.onCalcStat(
|
||||
this.getActionContext(),
|
||||
statName,
|
||||
base,
|
||||
aux
|
||||
);
|
||||
let dex = this.actionPipeline.onCalcStat(this.getActionContext(), statName, base, aux);
|
||||
dex = this.resolveOpposeStatValue(statName, dex, aux);
|
||||
return dex;
|
||||
}
|
||||
@@ -588,12 +551,7 @@ export class WarUnitGeneral<
|
||||
|
||||
public override getComputedTrain(): number {
|
||||
const aux = { isAttacker: this.isAttacker() };
|
||||
let train = this.actionPipeline.onCalcStat(
|
||||
this.getActionContext(),
|
||||
'bonusTrain',
|
||||
this.general.train,
|
||||
aux
|
||||
);
|
||||
let train = this.actionPipeline.onCalcStat(this.getActionContext(), 'bonusTrain', this.general.train, aux);
|
||||
train = this.resolveOpposeStatValue('bonusTrain', train, aux);
|
||||
train += this.trainBonus;
|
||||
return train;
|
||||
@@ -601,12 +559,7 @@ export class WarUnitGeneral<
|
||||
|
||||
public override getComputedAtmos(): number {
|
||||
const aux = { isAttacker: this.isAttacker() };
|
||||
let atmos = this.actionPipeline.onCalcStat(
|
||||
this.getActionContext(),
|
||||
'bonusAtmos',
|
||||
this.general.atmos,
|
||||
aux
|
||||
);
|
||||
let atmos = this.actionPipeline.onCalcStat(this.getActionContext(), 'bonusAtmos', this.general.atmos, aux);
|
||||
atmos = this.resolveOpposeStatValue('bonusAtmos', atmos, aux);
|
||||
atmos += this.atmosBonus;
|
||||
return atmos;
|
||||
@@ -630,12 +583,7 @@ export class WarUnitGeneral<
|
||||
ratio = Math.min(50, ratio) / 100;
|
||||
|
||||
const aux = { isAttacker: this.isAttacker() };
|
||||
ratio = this.actionPipeline.onCalcStat(
|
||||
this.getActionContext(),
|
||||
'warCriticalRatio',
|
||||
ratio,
|
||||
aux
|
||||
);
|
||||
ratio = this.actionPipeline.onCalcStat(this.getActionContext(), 'warCriticalRatio', ratio, aux);
|
||||
ratio = this.resolveOpposeStatValue('warCriticalRatio', ratio, aux);
|
||||
return ratio;
|
||||
}
|
||||
@@ -646,18 +594,10 @@ export class WarUnitGeneral<
|
||||
avoidRatio *= this.getComputedTrain() / 100;
|
||||
|
||||
const aux = { isAttacker: this.isAttacker() };
|
||||
avoidRatio = this.actionPipeline.onCalcStat(
|
||||
this.getActionContext(),
|
||||
'warAvoidRatio',
|
||||
avoidRatio,
|
||||
aux
|
||||
);
|
||||
avoidRatio = this.actionPipeline.onCalcStat(this.getActionContext(), 'warAvoidRatio', avoidRatio, aux);
|
||||
avoidRatio = this.resolveOpposeStatValue('warAvoidRatio', avoidRatio, aux);
|
||||
|
||||
if (
|
||||
oppose &&
|
||||
oppose.getCrewType().armType === this.config.armTypes.footman
|
||||
) {
|
||||
if (oppose && oppose.getCrewType().armType === this.config.armTypes.footman) {
|
||||
avoidRatio *= 0.75;
|
||||
}
|
||||
|
||||
@@ -665,19 +605,11 @@ export class WarUnitGeneral<
|
||||
}
|
||||
|
||||
public override addTrain(train: number): void {
|
||||
this.general.train = clamp(
|
||||
this.general.train + train,
|
||||
0,
|
||||
this.config.maxTrainByWar
|
||||
);
|
||||
this.general.train = clamp(this.general.train + train, 0, this.config.maxTrainByWar);
|
||||
}
|
||||
|
||||
public override addAtmos(atmos: number): void {
|
||||
this.general.atmos = clamp(
|
||||
this.general.atmos + atmos,
|
||||
0,
|
||||
this.config.maxAtmosByWar
|
||||
);
|
||||
this.general.atmos = clamp(this.general.atmos + atmos, 0, this.config.maxAtmosByWar);
|
||||
}
|
||||
|
||||
public override addWin(): void {
|
||||
@@ -688,11 +620,7 @@ export class WarUnitGeneral<
|
||||
}
|
||||
|
||||
const atmosMultiplier = this.isAttacker() ? 1.1 : 1.05;
|
||||
this.general.atmos = clamp(
|
||||
Math.round(this.general.atmos * atmosMultiplier),
|
||||
0,
|
||||
this.config.maxAtmosByWar
|
||||
);
|
||||
this.general.atmos = clamp(Math.round(this.general.atmos * atmosMultiplier), 0, this.config.maxAtmosByWar);
|
||||
|
||||
this.addStatExp(1);
|
||||
}
|
||||
@@ -741,11 +669,7 @@ export class WarUnitGeneral<
|
||||
}
|
||||
rice *= this.getCrewType().rice;
|
||||
rice *= getTechCost(resolveNationTech(this.nation));
|
||||
rice = this.actionPipeline.onCalcStat(
|
||||
this.getActionContext(),
|
||||
'killRice',
|
||||
rice
|
||||
);
|
||||
rice = this.actionPipeline.onCalcStat(this.getActionContext(), 'killRice', rice);
|
||||
return rice;
|
||||
}
|
||||
|
||||
@@ -815,11 +739,7 @@ export class WarUnitGeneral<
|
||||
nextOpposeMultiply *= ratio;
|
||||
}
|
||||
|
||||
const [myMul, opposeMul] = this.actionPipeline.getWarPowerMultiplier(
|
||||
this.getActionContext(),
|
||||
this,
|
||||
oppose
|
||||
);
|
||||
const [myMul, opposeMul] = this.actionPipeline.getWarPowerMultiplier(this.getActionContext(), this, oppose);
|
||||
nextWarPower *= myMul;
|
||||
nextOpposeMultiply *= opposeMul;
|
||||
|
||||
@@ -830,11 +750,7 @@ export class WarUnitGeneral<
|
||||
|
||||
public override criticalDamage(): number {
|
||||
let range: [number, number] = WAR_CRITICAL_RANGE;
|
||||
range = this.actionPipeline.onCalcStat(
|
||||
this.getActionContext(),
|
||||
'criticalDamageRange',
|
||||
range
|
||||
);
|
||||
range = this.actionPipeline.onCalcStat(this.getActionContext(), 'criticalDamageRange', range);
|
||||
return this.rng.nextRange(...range);
|
||||
}
|
||||
|
||||
@@ -850,11 +766,7 @@ export class WarUnitGeneral<
|
||||
}
|
||||
|
||||
this.activateSkill('부상');
|
||||
this.general.injury = clamp(
|
||||
this.general.injury + this.rng.nextRangeInt(10, 80),
|
||||
0,
|
||||
80
|
||||
);
|
||||
this.general.injury = clamp(this.general.injury + this.rng.nextRangeInt(10, 80), 0, 80);
|
||||
this.logger.pushGeneralActionLog('전투중 <R>부상</>당했다!', LogFormat.PLAIN);
|
||||
return true;
|
||||
}
|
||||
@@ -880,18 +792,10 @@ export class WarUnitGeneral<
|
||||
increaseMetaNumber(this.general.meta, RANK_DEATHCREW, this.dead);
|
||||
|
||||
if (this.killedPerson) {
|
||||
increaseMetaNumber(
|
||||
this.general.meta,
|
||||
RANK_KILLCREW_PERSON,
|
||||
this.killedPerson
|
||||
);
|
||||
increaseMetaNumber(this.general.meta, RANK_KILLCREW_PERSON, this.killedPerson);
|
||||
}
|
||||
if (this.deadPerson) {
|
||||
increaseMetaNumber(
|
||||
this.general.meta,
|
||||
RANK_DEATHCREW_PERSON,
|
||||
this.deadPerson
|
||||
);
|
||||
increaseMetaNumber(this.general.meta, RANK_DEATHCREW_PERSON, this.deadPerson);
|
||||
}
|
||||
|
||||
this.general.rice = round(this.general.rice);
|
||||
|
||||
@@ -1,76 +1,33 @@
|
||||
import type { TriggerValue } from '@sammo-ts/logic/domain/entities.js';
|
||||
|
||||
const DEX_LEVEL_THRESHOLDS = [
|
||||
0,
|
||||
350,
|
||||
1375,
|
||||
3500,
|
||||
7125,
|
||||
12650,
|
||||
20475,
|
||||
31000,
|
||||
44625,
|
||||
61750,
|
||||
82775,
|
||||
108100,
|
||||
138125,
|
||||
173250,
|
||||
213875,
|
||||
260400,
|
||||
313225,
|
||||
372750,
|
||||
439375,
|
||||
513500,
|
||||
595525,
|
||||
685850,
|
||||
784875,
|
||||
893000,
|
||||
1010625,
|
||||
1138150,
|
||||
1275975,
|
||||
0, 350, 1375, 3500, 7125, 12650, 20475, 31000, 44625, 61750, 82775, 108100, 138125, 173250, 213875, 260400, 313225,
|
||||
372750, 439375, 513500, 595525, 685850, 784875, 893000, 1010625, 1138150, 1275975,
|
||||
];
|
||||
|
||||
export const clamp = (value: number, min: number, max: number): number =>
|
||||
Math.max(min, Math.min(value, max));
|
||||
export const clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(value, max));
|
||||
|
||||
export const clampMin = (value: number, min: number): number =>
|
||||
value < min ? min : value;
|
||||
export const clampMin = (value: number, min: number): number => (value < min ? min : value);
|
||||
|
||||
export const clampMax = (value: number, max: number): number =>
|
||||
value > max ? max : value;
|
||||
export const clampMax = (value: number, max: number): number => (value > max ? max : value);
|
||||
|
||||
export const round = (value: number): number => Math.round(value);
|
||||
|
||||
export const getMetaNumber = (
|
||||
meta: Record<string, TriggerValue>,
|
||||
key: string,
|
||||
fallback = 0
|
||||
): number => {
|
||||
export const getMetaNumber = (meta: Record<string, TriggerValue>, key: string, fallback = 0): number => {
|
||||
const value = meta[key];
|
||||
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
||||
};
|
||||
|
||||
export const getMetaString = (
|
||||
meta: Record<string, TriggerValue>,
|
||||
key: string
|
||||
): string | null => {
|
||||
export const getMetaString = (meta: Record<string, TriggerValue>, key: string): string | null => {
|
||||
const value = meta[key];
|
||||
return typeof value === 'string' ? value : null;
|
||||
};
|
||||
|
||||
export const setMetaNumber = (
|
||||
meta: Record<string, TriggerValue>,
|
||||
key: string,
|
||||
value: number
|
||||
): void => {
|
||||
export const setMetaNumber = (meta: Record<string, TriggerValue>, key: string, value: number): void => {
|
||||
meta[key] = round(value);
|
||||
};
|
||||
|
||||
export const increaseMetaNumber = (
|
||||
meta: Record<string, TriggerValue>,
|
||||
key: string,
|
||||
delta: number
|
||||
): number => {
|
||||
export const increaseMetaNumber = (meta: Record<string, TriggerValue>, key: string, delta: number): number => {
|
||||
const next = getMetaNumber(meta, key) + delta;
|
||||
meta[key] = round(next);
|
||||
return next;
|
||||
@@ -92,12 +49,9 @@ export const getDexLevel = (dex: number): number => {
|
||||
return level;
|
||||
};
|
||||
|
||||
export const getDexLog = (dex1: number, dex2: number): number =>
|
||||
(getDexLevel(dex1) - getDexLevel(dex2)) / 55 + 1;
|
||||
export const getDexLog = (dex1: number, dex2: number): number => (getDexLevel(dex1) - getDexLevel(dex2)) / 55 + 1;
|
||||
|
||||
export const parseConflict = (
|
||||
raw: TriggerValue | undefined
|
||||
): Record<number, number> | null => {
|
||||
export const parseConflict = (raw: TriggerValue | undefined): Record<number, number> | null => {
|
||||
if (raw === undefined || raw === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -120,9 +74,7 @@ export const parseConflict = (
|
||||
return null;
|
||||
};
|
||||
|
||||
export const stringifyConflict = (
|
||||
conflict: Record<number, number> | null
|
||||
): string => {
|
||||
export const stringifyConflict = (conflict: Record<number, number> | null): string => {
|
||||
if (!conflict) {
|
||||
return '{}';
|
||||
}
|
||||
@@ -139,9 +91,7 @@ export const stringifyConflict = (
|
||||
return JSON.stringify(ordered);
|
||||
};
|
||||
|
||||
export const sortConflict = (
|
||||
conflict: Record<number, number>
|
||||
): Record<number, number> => {
|
||||
export const sortConflict = (conflict: Record<number, number>): Record<number, number> => {
|
||||
const ordered: Record<number, number> = {};
|
||||
const entries = Object.entries(conflict)
|
||||
.map(([key, value]) => [Number(key), value] as const)
|
||||
|
||||
Reference in New Issue
Block a user