export type Role = 'Tank' | 'Healer' | 'Damage' export type PartyMember = { id: string name: string role: Role health: number maxHealth: number shield: number hotTicks: number hotEffects?: Array<{ id: string spellId: string label: string ticks: number power: number }> bounceHeals?: Array<{ id: string label: string charges: number power: number }> damageReductionTicks?: number debuff?: string debuffTicks?: number poisonStacks?: number maxHealthPenaltyTicks?: number healingReductionTicks?: number } export type Spell = { id: string key: string name: string description: string cost: number cooldown: number power: number glyph: string kind: 'direct' | 'hot' | 'group' | 'shield' | 'cleanse' | 'damage_reduction' | 'bounce_heal' effectType?: string } export type Encounter = { id: string enemyName: string description: string maxHealth: number damage: number tankDamage: number partyDamage: number isBoss: boolean } export type CombatLogEntry = { id: number text: string tone: 'system' | 'heal' | 'danger' | 'loot' } export const TANKLESS_DAMAGE_MULTIPLIER = 1.35 export const DEFAULT_GROUP_HEAL_TARGETS = 4 export const INITIAL_PARTY: PartyMember[] = [ { id: 'brann', name: 'Brann', role: 'Tank', health: 150, maxHealth: 150, shield: 0, hotTicks: 0 }, { id: 'mira', name: 'Mira', role: 'Healer', health: 100, maxHealth: 100, shield: 0, hotTicks: 0 }, { id: 'kael', name: 'Kael', role: 'Damage', health: 105, maxHealth: 105, shield: 0, hotTicks: 0 }, { id: 'ves', name: 'Ves', role: 'Damage', health: 95, maxHealth: 95, shield: 0, hotTicks: 0 }, { id: 'orin', name: 'Orin', role: 'Damage', health: 110, maxHealth: 110, shield: 0, hotTicks: 0 }, { id: 'lyra', name: 'Lyra', role: 'Damage', health: 98, maxHealth: 98, shield: 0, hotTicks: 0 }, ] export const RAID_PARTY: PartyMember[] = [ { id: 'brann', name: 'Brann', role: 'Tank', health: 165, maxHealth: 165, shield: 0, hotTicks: 0 }, { id: 'tala', name: 'Tala', role: 'Tank', health: 155, maxHealth: 155, shield: 0, hotTicks: 0 }, { id: 'mira', name: 'Mira', role: 'Healer', health: 100, maxHealth: 100, shield: 0, hotTicks: 0 }, { id: 'seren', name: 'Seren', role: 'Healer', health: 102, maxHealth: 102, shield: 0, hotTicks: 0 }, { id: 'kael', name: 'Kael', role: 'Damage', health: 105, maxHealth: 105, shield: 0, hotTicks: 0 }, { id: 'ves', name: 'Ves', role: 'Damage', health: 95, maxHealth: 95, shield: 0, hotTicks: 0 }, { id: 'orin', name: 'Orin', role: 'Damage', health: 110, maxHealth: 110, shield: 0, hotTicks: 0 }, { id: 'lyra', name: 'Lyra', role: 'Damage', health: 98, maxHealth: 98, shield: 0, hotTicks: 0 }, { id: 'dax', name: 'Dax', role: 'Damage', health: 108, maxHealth: 108, shield: 0, hotTicks: 0 }, { id: 'nyx', name: 'Nyx', role: 'Damage', health: 100, maxHealth: 100, shield: 0, hotTicks: 0 }, { id: 'ren', name: 'Ren', role: 'Damage', health: 104, maxHealth: 104, shield: 0, hotTicks: 0 }, { id: 'iona', name: 'Iona', role: 'Damage', health: 96, maxHealth: 96, shield: 0, hotTicks: 0 }, { id: 'sol', name: 'Sol', role: 'Damage', health: 106, maxHealth: 106, shield: 0, hotTicks: 0 }, { id: 'nari', name: 'Nari', role: 'Damage', health: 99, maxHealth: 99, shield: 0, hotTicks: 0 }, { id: 'joss', name: 'Joss', role: 'Damage', health: 103, maxHealth: 103, shield: 0, hotTicks: 0 }, { id: 'eira', name: 'Eira', role: 'Damage', health: 97, maxHealth: 97, shield: 0, hotTicks: 0 }, { id: 'cato', name: 'Cato', role: 'Damage', health: 107, maxHealth: 107, shield: 0, hotTicks: 0 }, { id: 'rhea', name: 'Rhea', role: 'Damage', health: 101, maxHealth: 101, shield: 0, hotTicks: 0 }, ] export const SPELLS: Spell[] = [ { id: 'mend', key: '1', name: 'Mend', description: 'A fast, efficient single-target heal.', cost: 5, cooldown: 0.5, power: 30, glyph: '+', kind: 'direct', }, { id: 'renew', key: '2', name: 'Renew', description: 'Heals now and continues healing over time.', cost: 7, cooldown: 0.5, power: 12, glyph: '~', kind: 'hot', }, { id: 'radiance', key: '3', name: 'Radiance', description: 'Restores health to up to 4 injured party members.', cost: 12, cooldown: 8, power: 18, glyph: '*', kind: 'group', }, { id: 'ward', key: '4', name: 'Sun Ward', description: 'Places a damage-absorbing shield on your target.', cost: 8, cooldown: 7, power: 36, glyph: 'O', kind: 'shield', }, { id: 'purify', key: '5', name: 'Purify', description: 'Removes a harmful effect and restores a little health.', cost: 5, cooldown: 5, power: 10, glyph: 'x', kind: 'cleanse', }, ] export const ENCOUNTERS: Encounter[] = [ { id: 'ashfang-pack', enemyName: 'Ashfang Pack', description: 'Three beasts snap at random party members.', maxHealth: 390, damage: 13, tankDamage: 7, partyDamage: 24, isBoss: false, }, { id: 'cinder-adepts', enemyName: 'Cinder Adepts', description: 'Cultists pressure the tank while throwing embers into the group.', maxHealth: 470, damage: 16, tankDamage: 10, partyDamage: 25, isBoss: false, }, { id: 'warden-vhal', enemyName: 'Warden Vhal', description: 'Boss: cleanse Searing Mark and prepare group healing for Cinder Pulse.', maxHealth: 820, damage: 18, tankDamage: 13, partyDamage: 27, isBoss: true, }, ] export function partyDamageOutput(party: PartyMember[], baseDamage: number) { const livingCount = party.filter((member) => member.health > 0).length return Math.round(baseDamage * (livingCount / Math.max(1, party.length))) } export function tankPressureTargets(party: PartyMember[]) { const living = party.filter((member) => member.health > 0) const tanks = living.filter((member) => member.role === 'Tank') if (tanks.length > 0) return { targets: tanks, multiplier: 1 } const damageDealer = living .filter((member) => member.role === 'Damage') .sort((left, right) => right.health - left.health)[0] return { targets: damageDealer ? [damageDealer] : [], multiplier: TANKLESS_DAMAGE_MULTIPLIER, } } export function groupHealTargets(party: PartyMember[], targetCount = DEFAULT_GROUP_HEAL_TARGETS) { return party .filter((member) => member.health > 0) .sort((left, right) => (left.health / left.maxHealth) - (right.health / right.maxHealth)) .slice(0, targetCount) }