import type { Iwt2HealerAbility } from '../content/healerAbilities' import { IWT2_GEAR_SLOT_RECIPES, IWT2_GEAR_SLOTS, type Iwt2GearProgress, } from '../content/gear' import type { Iwt2ArenaState, Iwt2PartyEntityState } from './types' import { applyIwt2InfusionLoadout } from './infusionLoadout' const PER_LEVEL_OUTPUT = 0.04 const PER_LEVEL_HEALTH = 0.04 const PER_LEVEL_SPEED = 0.025 const PER_LEVEL_COOLDOWN = 0.03 const PER_LEVEL_PROJECTILE = 0.03 const PER_LEVEL_DAMAGE_TAKEN_REDUCTION = 0.03 const PER_LEVEL_STUN_REDUCTION = 0.08 export function applyIwt2PveGearStats>(state: TState, gearProgress: Iwt2GearProgress): TState { const party = state.party.map((member) => applyMemberGearStats(member, gearProgress)) return applyIwt2InfusionLoadout({ ...state, party, }, gearProgress) } export function applyIwt2PveGearToHealerAbilities( abilities: Iwt2HealerAbility[], gearProgress: Iwt2GearProgress, ): Iwt2HealerAbility[] { const healer = gearProgress.healer const healingLevel = healer.slots.weapon.level const cooldownLevel = healer.slots.helmet.level if (healingLevel <= 0 && cooldownLevel <= 0) return abilities return abilities.map((ability) => ({ ...ability, cooldownSeconds: roundOne(ability.cooldownSeconds * (1 - cooldownLevel * PER_LEVEL_COOLDOWN)), power: Math.max(1, Math.round(ability.power * (1 + healingLevel * PER_LEVEL_OUTPUT))), })) } function applyMemberGearStats( member: Iwt2PartyEntityState, gearProgress: Iwt2GearProgress, ): Iwt2PartyEntityState { const classProgress = gearProgress[member.classId] let next: Iwt2PartyEntityState = { ...member, gearLevels: { weapon: classProgress.slots.weapon.level, helmet: classProgress.slots.helmet.level, chest: classProgress.slots.chest.level, legs: classProgress.slots.legs.level, feet: classProgress.slots.feet.level, }, } for (const slotId of IWT2_GEAR_SLOTS) { const level = classProgress.slots[slotId].level if (level <= 0) continue const statId = IWT2_GEAR_SLOT_RECIPES[member.classId][slotId].statId if (statId === 'maxHealth') { const maxHealth = Math.round(next.maxHealth * (1 + level * PER_LEVEL_HEALTH)) next = { ...next, health: Math.min(maxHealth, Math.round(next.health * (maxHealth / next.maxHealth))), maxHealth, } } else if (statId === 'moveSpeed') { next = { ...next, moveSpeed: next.moveSpeed * (1 + level * PER_LEVEL_SPEED) } } else if (statId === 'damage') { next = { ...next, attackDamage: next.attackDamage * (1 + level * PER_LEVEL_OUTPUT) } } else if (statId === 'attackCooldown') { next = { ...next, attackCooldown: next.attackCooldown * (1 - level * PER_LEVEL_COOLDOWN) } } else if (statId === 'projectileSpeed') { next = { ...next, projectileSpeed: next.projectileSpeed * (1 + level * PER_LEVEL_PROJECTILE) } } else if (statId === 'hazardDamageTaken') { next = { ...next, damageTakenScale: next.damageTakenScale * (1 - level * PER_LEVEL_DAMAGE_TAKEN_REDUCTION) } } else if (statId === 'stunResist') { next = { ...next, stunTakenScale: Math.max(0, next.stunTakenScale * (1 - level * PER_LEVEL_STUN_REDUCTION)) } } } return next } function roundOne(value: number): number { return Math.max(0.1, Math.round(value * 10) / 10) }