feat: complete legacy unique item battle parity
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import type { RandUtil } from '@sammo-ts/common';
|
||||
|
||||
import type { General } from '@sammo-ts/logic/domain/entities.js';
|
||||
import { TriggerCaller, type Trigger } from '@sammo-ts/logic/triggers/core.js';
|
||||
import type { WarUnit } from './units.js';
|
||||
import { removeEquippedItem } from '@sammo-ts/logic/items/inventory.js';
|
||||
|
||||
export interface WarTriggerContext {
|
||||
rng: RandUtil;
|
||||
@@ -87,11 +89,28 @@ export abstract class BaseWarUnitTrigger implements WarTrigger {
|
||||
opposeEnv: Record<string, unknown>
|
||||
): boolean;
|
||||
|
||||
// 아이템 소모 트리거는 아직 미구현. 필요 시 raiseType을 활용해 확장 가능하다.
|
||||
public processConsumableItem(): boolean {
|
||||
if (!(this.raiseType & BaseWarUnitTrigger.TYPE_ITEM)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
if (this.unit.hasActivatedSkill('아이템사용')) {
|
||||
return false;
|
||||
}
|
||||
this.unit.activateSkill('아이템사용');
|
||||
if (this.raiseType !== BaseWarUnitTrigger.TYPE_CONSUMABLE_ITEM) {
|
||||
return false;
|
||||
}
|
||||
if (this.unit.hasActivatedSkill('아이템소모')) {
|
||||
return false;
|
||||
}
|
||||
const unit = this.unit as WarUnit & {
|
||||
getGeneral?: () => General;
|
||||
};
|
||||
const general = unit.getGeneral?.();
|
||||
if (!general) {
|
||||
return false;
|
||||
}
|
||||
this.unit.activateSkill('아이템소모');
|
||||
return removeEquippedItem(general, 'item') !== null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { TriggerPriority } from '@sammo-ts/logic/triggers/core.js';
|
||||
import { BaseWarUnitTrigger } from '@sammo-ts/logic/war/triggers.js';
|
||||
import type { WarUnit } from '@sammo-ts/logic/war/units.js';
|
||||
|
||||
export class 전투력보정 extends BaseWarUnitTrigger {
|
||||
constructor(
|
||||
unit: WarUnit,
|
||||
private readonly attackMultiplier: number,
|
||||
private readonly defenceMultiplier = 1
|
||||
) {
|
||||
super(unit, TriggerPriority.Begin + 20);
|
||||
}
|
||||
|
||||
protected actionWar(
|
||||
self: WarUnit,
|
||||
oppose: WarUnit,
|
||||
_selfEnv: Record<string, unknown>,
|
||||
_opposeEnv: Record<string, unknown>
|
||||
): boolean {
|
||||
self.multiplyWarPowerMultiply(this.attackMultiplier);
|
||||
oppose.multiplyWarPowerMultiply(this.defenceMultiplier);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,7 @@ import { getTechAbility, getTechCost } from '@sammo-ts/logic/world/unitSet.js';
|
||||
import type { WarActionPipeline, WarActionContext } from '../actions.js';
|
||||
import type { WarEngineConfig } from '../types.js';
|
||||
import type { WarCrewType } from '../crewType.js';
|
||||
import {
|
||||
clamp,
|
||||
clampMin,
|
||||
getMetaNumber,
|
||||
getMetaString,
|
||||
increaseMetaNumber,
|
||||
round,
|
||||
} from '../utils.js';
|
||||
import { clamp, clampMin, getMetaNumber, getMetaString, increaseMetaNumber, round } from '../utils.js';
|
||||
import { WAR_CRITICAL_RANGE, WarUnit, resolveNationTech } from './base.js';
|
||||
type CityUnit = WarUnit & { getCityId: () => number };
|
||||
|
||||
@@ -150,24 +143,47 @@ export class WarUnitGeneral<
|
||||
public getComputedStat(
|
||||
statName: keyof StatBlock,
|
||||
base: number,
|
||||
options: { withInjury?: boolean; withStatAdjust?: boolean; withActions?: boolean } = {}
|
||||
options: {
|
||||
withInjury?: boolean;
|
||||
withStatAdjust?: boolean;
|
||||
withActions?: boolean;
|
||||
truncate?: boolean;
|
||||
} = {}
|
||||
): number {
|
||||
const withInjury = options.withInjury ?? true;
|
||||
const withStatAdjust = options.withStatAdjust ?? true;
|
||||
const withActions = options.withActions ?? true;
|
||||
const truncate = options.truncate ?? true;
|
||||
const injuryRatio = withInjury ? (100 - this.general.injury) / 100 : 1;
|
||||
let value = base * injuryRatio;
|
||||
if (withStatAdjust && statName === 'strength') {
|
||||
value += round((this.general.stats.intelligence * injuryRatio) / 4);
|
||||
value += round(
|
||||
this.getComputedStat('intelligence', this.general.stats.intelligence, {
|
||||
withInjury,
|
||||
withStatAdjust: false,
|
||||
withActions,
|
||||
truncate: false,
|
||||
}) / 4
|
||||
);
|
||||
} else if (withStatAdjust && statName === 'intelligence') {
|
||||
value += round((this.general.stats.strength * injuryRatio) / 4);
|
||||
value += round(
|
||||
this.getComputedStat('strength', this.general.stats.strength, {
|
||||
withInjury,
|
||||
withStatAdjust: false,
|
||||
withActions,
|
||||
truncate: false,
|
||||
}) / 4
|
||||
);
|
||||
}
|
||||
const maxGeneralStat = this.config.maxGeneralStat ?? 255;
|
||||
value = clamp(value, 0, maxGeneralStat);
|
||||
if (withActions) {
|
||||
value = this.actionPipeline.onCalcStat(this.getActionContext(), statName, value);
|
||||
}
|
||||
return clamp(value, 0, maxGeneralStat);
|
||||
// Legacy General::getStatValue() applies every action and then returns
|
||||
// Util::toInt(), which truncates toward zero on every stat read.
|
||||
const clamped = clamp(value, 0, maxGeneralStat);
|
||||
return truncate ? Math.trunc(clamped) : clamped;
|
||||
}
|
||||
|
||||
private resolveMainStat(armType: number, withInjury = true): number {
|
||||
@@ -345,7 +361,8 @@ export class WarUnitGeneral<
|
||||
: crewType.armType;
|
||||
const key = `${META_DEX_PREFIX}${armType}`;
|
||||
const base = getMetaNumber(this.general.meta, key);
|
||||
this.general.meta[key] = round(base + exp);
|
||||
const adjustedExp = this.actionPipeline.onCalcStat(this.getActionContext(), 'addDex', exp, { armType });
|
||||
this.general.meta[key] = base + adjustedExp;
|
||||
}
|
||||
|
||||
public calcRiceConsumption(damage: number): number {
|
||||
|
||||
Reference in New Issue
Block a user