Android build v1.1.16

This commit is contained in:
Warren H
2026-07-04 17:41:45 -04:00
parent 99e97e5208
commit 0d269a6041
20 changed files with 2304 additions and 307 deletions
+93 -1
View File
@@ -1,6 +1,6 @@
import { IWT2_BALANCE_OVERRIDES } from './balanceOverrides'
export type Iwt2BossId = 'bulldrome' | 'yian-kut-ku'
export type Iwt2BossId = 'bulldrome' | 'yian-kut-ku' | 'great-jaggi' | 'khezu'
export type Iwt2BalanceOverrides = {
bosses?: Partial<Record<Iwt2BossId, Partial<Pick<Iwt2BossMetadata, 'maxHealth' | 'birdHealth'>>>>
@@ -46,6 +46,24 @@ export type Iwt2BossMetadata = {
birdRadius?: number
birdContactDamage?: number
birdStunSeconds?: number
packHowlCooldown?: number
packHowlWindup?: number
packLaneDamage?: number
packLaneStunSeconds?: number
packLaneWidth?: number
packLaneCount?: number
thunderRingCooldown?: number
thunderRingWindup?: number
thunderRingInnerRadius?: number
thunderRingOuterRadius?: number
thunderRingDamage?: number
thunderRingStunSeconds?: number
lightningStrikeCooldown?: number
lightningStrikeWindup?: number
lightningStrikeRadius?: number
lightningStrikeDamage?: number
lightningStrikeStunSeconds?: number
lightningStrikeCount?: number
}
const DEFAULT_BULLDROME_BOSS_METADATA: Iwt2BossMetadata = {
@@ -116,6 +134,76 @@ const DEFAULT_YIAN_KUT_KU_BOSS_METADATA: Iwt2BossMetadata = {
birdStunSeconds: 0.75,
}
const DEFAULT_GREAT_JAGGI_BOSS_METADATA: Iwt2BossMetadata = {
id: 'great-jaggi',
name: 'Great Jaggi',
icon: 'J',
color: '#3f8f73',
accentColor: '#b8f0aa',
maxHealth: 620,
radius: 27,
moveSpeed: 146,
meleeRange: 52,
meleeDamage: 7,
meleeCooldown: 0.82,
chargeCooldown: 0,
chargeWindup: 0,
chargeSpeed: 0,
chargeDamage: 0,
chargeStunSeconds: 0,
chargeLength: 0,
chargeOvershoot: 0,
chargeWidth: 0,
slamWindup: 0,
slamRadius: 0,
slamDamage: 0,
slamStunSeconds: 0,
packHowlCooldown: 5.8,
packHowlWindup: 0.95,
packLaneDamage: 15,
packLaneStunSeconds: 0.45,
packLaneWidth: 24,
packLaneCount: 3,
}
const DEFAULT_KHEZU_BOSS_METADATA: Iwt2BossMetadata = {
id: 'khezu',
name: 'Khezu',
icon: 'K',
color: '#d8d7c9',
accentColor: '#77d9ff',
maxHealth: 760,
radius: 30,
moveSpeed: 92,
meleeRange: 58,
meleeDamage: 10,
meleeCooldown: 1.15,
chargeCooldown: 0,
chargeWindup: 0,
chargeSpeed: 0,
chargeDamage: 0,
chargeStunSeconds: 0,
chargeLength: 0,
chargeOvershoot: 0,
chargeWidth: 0,
slamWindup: 0,
slamRadius: 0,
slamDamage: 0,
slamStunSeconds: 0,
thunderRingCooldown: 7.2,
thunderRingWindup: 0.9,
thunderRingInnerRadius: 70,
thunderRingOuterRadius: 172,
thunderRingDamage: 22,
thunderRingStunSeconds: 0.7,
lightningStrikeCooldown: 4.2,
lightningStrikeWindup: 0.78,
lightningStrikeRadius: 46,
lightningStrikeDamage: 17,
lightningStrikeStunSeconds: 0.55,
lightningStrikeCount: 2,
}
function applyBossOverrides(metadata: Iwt2BossMetadata): Iwt2BossMetadata {
const override = IWT2_BALANCE_OVERRIDES.bosses?.[metadata.id]
if (!override) return metadata
@@ -127,8 +215,12 @@ function applyBossOverrides(metadata: Iwt2BossMetadata): Iwt2BossMetadata {
export const BULLDROME_BOSS_METADATA: Iwt2BossMetadata = applyBossOverrides(DEFAULT_BULLDROME_BOSS_METADATA)
export const YIAN_KUT_KU_BOSS_METADATA: Iwt2BossMetadata = applyBossOverrides(DEFAULT_YIAN_KUT_KU_BOSS_METADATA)
export const GREAT_JAGGI_BOSS_METADATA: Iwt2BossMetadata = applyBossOverrides(DEFAULT_GREAT_JAGGI_BOSS_METADATA)
export const KHEZU_BOSS_METADATA: Iwt2BossMetadata = applyBossOverrides(DEFAULT_KHEZU_BOSS_METADATA)
export const IWT2_BOSS_METADATA: Record<Iwt2BossId, Iwt2BossMetadata> = {
bulldrome: BULLDROME_BOSS_METADATA,
'yian-kut-ku': YIAN_KUT_KU_BOSS_METADATA,
'great-jaggi': GREAT_JAGGI_BOSS_METADATA,
khezu: KHEZU_BOSS_METADATA,
}
+91 -100
View File
@@ -1,12 +1,18 @@
import type { Spell } from '../../../game'
import type { Ability } from '../../../profile'
import { toCombatSpell } from '../../../combat/rules'
export type Iwt2AbilityTarget = 'party-member' | 'self' | 'ground'
export type Iwt2HealerId = 'dawnweaver' | 'lifebinder' | 'runesage'
export type Iwt2HealerMetadata = {
id: Iwt2HealerId
name: string
icon: string
description: string
}
export type Iwt2HealerAbility = {
id: string
healerId: string
healerId: Iwt2HealerId
slot: number
name: string
icon: string
@@ -16,110 +22,95 @@ export type Iwt2HealerAbility = {
manaCost: number
cooldownSeconds: number
target: Iwt2AbilityTarget
extraTargets?: number
}
const IWT1_DAWNWEAVER_ABILITIES: Ability[] = [
{
id: 1,
classId: 1,
slug: 'mend',
name: 'Mend',
spellType: 'direct_heal',
cost: 5,
cooldown: 0.5,
power: 30,
unlockLevel: 1,
glyph: '+',
description: 'A fast, efficient single-target heal.',
export const IWT2_HEALER_METADATA: Record<Iwt2HealerId, Iwt2HealerMetadata> = {
dawnweaver: {
id: 'dawnweaver',
name: 'Dawnweaver',
icon: '+',
description: 'Direct heals, radiant group recovery, shields, and cleanse.',
},
{
id: 2,
classId: 1,
slug: 'renew',
name: 'Renew',
spellType: 'heal_over_time',
cost: 7,
cooldown: 0.5,
power: 12,
unlockLevel: 1,
glyph: '~',
description: 'Heals now and continues healing over time.',
lifebinder: {
id: 'lifebinder',
name: 'Lifebinder',
icon: '~',
description: 'Verdant healing over time, barkskin shielding, and steady group recovery.',
},
{
id: 3,
classId: 1,
slug: 'radiance',
name: 'Radiance',
spellType: 'party_heal',
cost: 12,
cooldown: 8,
power: 18,
unlockLevel: 1,
glyph: '*',
description: 'Restores health to up to 4 injured party members.',
runesage: {
id: 'runesage',
name: 'Runesage',
icon: 'R',
description: 'Rune-scripted mends, concordance healing, aegis shielding, and unraveling cleanse.',
},
{
id: 4,
classId: 1,
slug: 'sun-ward',
name: 'Sun Ward',
spellType: 'absorb',
cost: 8,
cooldown: 7,
power: 36,
unlockLevel: 1,
glyph: 'O',
description: 'Places a damage-absorbing shield on your target.',
},
{
id: 5,
classId: 1,
slug: 'purify',
name: 'Purify',
spellType: 'cleanse',
cost: 5,
cooldown: 5,
power: 10,
unlockLevel: 1,
glyph: 'x',
description: 'Removes a harmful effect and restores health.',
},
{
id: 6,
classId: 1,
slug: 'dawn-burst',
name: 'Dawn Burst',
spellType: 'party_heal',
cost: 16,
cooldown: 12,
power: 28,
unlockLevel: 5,
glyph: 'D',
description: 'A brilliant wave of healing for up to 4 injured allies.',
},
]
}
function toIwt2Ability(ability: Ability, index: number): Iwt2HealerAbility {
const spell = toCombatSpell(ability, String(index + 1))
export const IWT2_HEALER_ORDER: Iwt2HealerId[] = ['dawnweaver', 'lifebinder', 'runesage']
export const IWT2_HEALER_ABILITIES: Record<Iwt2HealerId, Iwt2HealerAbility[]> = {
dawnweaver: [
createAbility('dawnweaver', 1, 'mend', 'Mend', '+', 'direct', 30, 5, 0.5),
createAbility('dawnweaver', 2, 'renew', 'Renew', '~', 'hot', 12, 7, 0.5, 'heal_over_time'),
createAbility('dawnweaver', 3, 'radiance', 'Radiance', '*', 'group', 18, 12, 8),
createAbility('dawnweaver', 4, 'sun-ward', 'Sun Ward', 'O', 'shield', 36, 8, 7, 'shield'),
createAbility('dawnweaver', 5, 'purify', 'Purify', 'x', 'cleanse', 10, 5, 5, 'cleanse'),
createAbility('dawnweaver', 6, 'dawn-burst', 'Dawn Burst', 'D', 'group', 28, 16, 12),
],
lifebinder: [
createAbility('lifebinder', 1, 'verdant-touch', 'Verdant Touch', '+', 'direct', 24, 4, 0.55),
createAbility('lifebinder', 2, 'seed-of-life', 'Seed of Life', '~', 'hot', 16, 9, 1, 'heal_over_time'),
createAbility('lifebinder', 3, 'wild-growth', 'Wild Growth', '*', 'group', 15, 11, 7.5),
createAbility('lifebinder', 4, 'barkskin', 'Barkskin', 'O', 'shield', 46, 10, 5.5, 'shield'),
createAbility('lifebinder', 5, 'purging-sap', 'Purging Sap', 'x', 'cleanse', 12, 6, 4.5, 'cleanse'),
createAbility('lifebinder', 6, 'ancient-grove', 'Ancient Grove', 'G', 'shield', 72, 18, 12, 'shield'),
],
runesage: [
createAbility('runesage', 1, 'etched-mend', 'Etched Mend', '+', 'direct', 22, 3, 0.35),
createAbility('runesage', 2, 'mending-rune', 'Mending Rune', '~', 'hot', 10, 5, 0.45, 'heal_over_time'),
createAbility('runesage', 3, 'concordance', 'Concordance', '*', 'group', 16, 9, 5.5),
createAbility('runesage', 4, 'aegis-script', 'Aegis Script', 'O', 'shield', 28, 6, 4.5, 'shield'),
createAbility('runesage', 5, 'unravel', 'Unravel', 'x', 'cleanse', 8, 4, 3.5, 'cleanse'),
createAbility('runesage', 6, 'grand-design', 'Grand Design', 'R', 'group', 22, 13, 8.5),
],
}
export function abilitiesForHealer(healerId: Iwt2HealerId | string) {
return IWT2_HEALER_ABILITIES[asIwt2HealerId(healerId)]
}
export function asIwt2HealerId(value: unknown): Iwt2HealerId {
if (value === 'field_medic') return 'dawnweaver'
if (value === 'ward_sage') return 'lifebinder'
if (value === 'storm_chanter') return 'runesage'
return IWT2_HEALER_ORDER.includes(value as Iwt2HealerId)
? value as Iwt2HealerId
: 'dawnweaver'
}
function createAbility(
healerId: Iwt2HealerId,
slot: number,
slug: string,
name: string,
icon: string,
kind: Spell['kind'],
power: number,
manaCost: number,
cooldownSeconds: number,
effectType?: string,
): Iwt2HealerAbility {
return {
id: spell.id,
healerId: 'field_medic',
slot: index + 1,
name: spell.name,
icon: spell.glyph,
kind: spell.kind,
power: spell.power,
effectType: spell.effectType,
manaCost: spell.cost,
cooldownSeconds: spell.cooldown,
id: `${healerId}-${slug}`,
healerId,
slot,
name,
icon,
kind,
power,
effectType,
manaCost,
cooldownSeconds,
target: 'party-member',
}
}
export const IWT2_HEALER_ABILITIES: Record<string, Iwt2HealerAbility[]> = {
field_medic: IWT1_DAWNWEAVER_ABILITIES.map(toIwt2Ability),
}
export function abilitiesForHealer(healerId: string) {
return IWT2_HEALER_ABILITIES[healerId] ?? IWT2_HEALER_ABILITIES.field_medic
}
+65
View File
@@ -0,0 +1,65 @@
import {
buildOpponentSlotDebuffChoices,
buildSelfSlotUpgradeChoices,
} from '../../../combat/roguelikeUpgrades'
import type { Spell } from '../../../game'
import { type Iwt2HealerAbility } from './healerAbilities'
export type Iwt2RoguelikeVariant = 'pve' | 'pvp'
export type Iwt2RoguelikeContentType = 'dungeon' | 'raid' | 'stadium'
export type Iwt2RoguelikeSlot = '1' | '2' | '3' | '4' | '5'
export type Iwt2RoguelikeSelfBuffId =
| 'revive-party-members'
| `slot${Iwt2RoguelikeSlot}-extra-target`
| `slot${Iwt2RoguelikeSlot}-cost-down`
| `slot${Iwt2RoguelikeSlot}-cooldown-down`
export type Iwt2RoguelikeOpponentDebuffId =
| `opp-slot${Iwt2RoguelikeSlot}-cost-up`
| `opp-slot${Iwt2RoguelikeSlot}-cooldown-up`
export type Iwt2RoguelikeChoice<T extends string> = {
id: T
name: string
description: string
}
export const IWT2_ROGUELIKE_SLOTS: readonly Iwt2RoguelikeSlot[] = ['1', '2', '3', '4', '5']
export const IWT2_REVIVE_PARTY_CHOICE: Iwt2RoguelikeChoice<Iwt2RoguelikeSelfBuffId> = {
id: 'revive-party-members',
name: 'Revive Party Members',
description: 'Revive fallen party members before the next IWT2 arena.',
}
export function iwt2AbilityToSpell(ability: Iwt2HealerAbility): Spell {
return {
id: ability.id,
key: String(ability.slot),
name: ability.name,
description: ability.name,
cost: ability.manaCost,
cooldown: ability.cooldownSeconds,
power: ability.power,
glyph: ability.icon,
kind: ability.kind,
effectType: ability.effectType,
}
}
export function buildIwt2SelfBuffChoices(abilities: Iwt2HealerAbility[]) {
return buildSelfSlotUpgradeChoices<Iwt2RoguelikeSelfBuffId>({
slots: IWT2_ROGUELIKE_SLOTS,
spells: abilities.map(iwt2AbilityToSpell),
labelMode: 'ability',
})
}
export function buildIwt2OpponentDebuffChoices(abilities: Iwt2HealerAbility[]) {
return buildOpponentSlotDebuffChoices<Iwt2RoguelikeOpponentDebuffId>({
slots: IWT2_ROGUELIKE_SLOTS,
spells: abilities.map(iwt2AbilityToSpell),
labelMode: 'ability',
})
}