210 lines
6.4 KiB
TypeScript
210 lines
6.4 KiB
TypeScript
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)
|
|
}
|