153 lines
3.9 KiB
TypeScript
153 lines
3.9 KiB
TypeScript
import { IWT2_BOSS_METADATA, type Iwt2BossId } from '../content/bosses'
|
|
import { IWT2_CLASS_METADATA } from '../content/classes'
|
|
import {
|
|
createInitialIwt2ArenaState as createCoreIwt2ArenaState,
|
|
tickIwt2Arena as tickCoreIwt2Arena,
|
|
} from './arena'
|
|
import type {
|
|
Iwt2ArenaIndicator,
|
|
Iwt2ArenaInput,
|
|
Iwt2ArenaState as Iwt2CoreArenaState,
|
|
Iwt2BossEntityState,
|
|
Iwt2HostileAddState,
|
|
Iwt2PartyEntityState,
|
|
} from './types'
|
|
|
|
export type Iwt2ArenaEntityKind = 'player' | 'party' | 'boss' | 'projectile' | 'hostileAdd'
|
|
|
|
export type Iwt2ArenaEntity = {
|
|
id: string
|
|
kind: Iwt2ArenaEntityKind
|
|
icon: string
|
|
color: string
|
|
x: number
|
|
y: number
|
|
radius: number
|
|
health: number
|
|
maxHealth: number
|
|
stunnedFor: number
|
|
}
|
|
|
|
export type Iwt2ArenaTelegraph =
|
|
| {
|
|
kind: 'charge'
|
|
x: number
|
|
y: number
|
|
width: number
|
|
height: number
|
|
active: boolean
|
|
}
|
|
| {
|
|
kind: 'slam'
|
|
x: number
|
|
y: number
|
|
radius: number
|
|
active: boolean
|
|
}
|
|
|
|
export type Iwt2ArenaState = Iwt2CoreArenaState & {
|
|
arena: {
|
|
width: number
|
|
height: number
|
|
}
|
|
entities: Iwt2ArenaEntity[]
|
|
telegraphs: Iwt2ArenaTelegraph[]
|
|
}
|
|
|
|
export function createInitialIwt2ArenaState(
|
|
bossId?: Iwt2BossId,
|
|
bossIds?: Iwt2BossId[],
|
|
bossHealthScale?: number,
|
|
): Iwt2ArenaState {
|
|
return decorateArenaState(createCoreIwt2ArenaState(bossId, bossIds, bossHealthScale))
|
|
}
|
|
|
|
export function tickIwt2Arena(state: Iwt2ArenaState, input: Iwt2ArenaInput, dt: number): Iwt2ArenaState {
|
|
return decorateArenaState(tickCoreIwt2Arena(state, input, dt))
|
|
}
|
|
|
|
export function decorateArenaState(state: Iwt2CoreArenaState): Iwt2ArenaState {
|
|
return {
|
|
...state,
|
|
arena: { ...state.bounds },
|
|
entities: [
|
|
...state.party.map(toArenaEntity),
|
|
...state.hostileAdds.map(toHostileAddArenaEntity),
|
|
...state.bosses.map(toBossArenaEntity),
|
|
],
|
|
telegraphs: createTelegraphs(state.indicators),
|
|
}
|
|
}
|
|
|
|
function toHostileAddArenaEntity(add: Iwt2HostileAddState): Iwt2ArenaEntity {
|
|
return {
|
|
id: add.id,
|
|
kind: 'hostileAdd',
|
|
icon: 'v',
|
|
color: '#f0b84f',
|
|
x: add.position.x,
|
|
y: add.position.y,
|
|
radius: add.radius,
|
|
health: add.health,
|
|
maxHealth: add.maxHealth,
|
|
stunnedFor: 0,
|
|
}
|
|
}
|
|
|
|
function toArenaEntity(member: Iwt2PartyEntityState): Iwt2ArenaEntity {
|
|
const metadata = IWT2_CLASS_METADATA[member.classId]
|
|
return {
|
|
id: member.id,
|
|
kind: member.aiRole === 'player' ? 'player' : 'party',
|
|
icon: metadata.icon,
|
|
color: metadata.color,
|
|
x: member.position.x,
|
|
y: member.position.y,
|
|
radius: member.radius,
|
|
health: member.health,
|
|
maxHealth: member.maxHealth,
|
|
stunnedFor: Math.max(member.status.stunnedSeconds, member.status.knockedDownSeconds),
|
|
}
|
|
}
|
|
|
|
function toBossArenaEntity(boss: Iwt2BossEntityState): Iwt2ArenaEntity {
|
|
const metadata = IWT2_BOSS_METADATA[boss.bossId]
|
|
return {
|
|
id: boss.id,
|
|
kind: 'boss',
|
|
icon: metadata.icon,
|
|
color: metadata.color,
|
|
x: boss.position.x,
|
|
y: boss.position.y,
|
|
radius: boss.radius,
|
|
health: boss.health,
|
|
maxHealth: boss.maxHealth,
|
|
stunnedFor: 0,
|
|
}
|
|
}
|
|
|
|
function createTelegraphs(indicators: Iwt2ArenaIndicator[]): Iwt2ArenaTelegraph[] {
|
|
return indicators.flatMap((indicator): Iwt2ArenaTelegraph[] => {
|
|
if (indicator.kind === 'lane') {
|
|
return [{
|
|
active: indicator.phase === 'active',
|
|
height: indicator.width * 2,
|
|
kind: 'charge' as const,
|
|
width: Math.max(16, Math.hypot(indicator.end.x - indicator.start.x, indicator.end.y - indicator.start.y)),
|
|
x: Math.min(indicator.start.x, indicator.end.x),
|
|
y: Math.min(indicator.start.y, indicator.end.y) - indicator.width,
|
|
}]
|
|
}
|
|
if (indicator.kind === 'circle') {
|
|
return [{
|
|
active: indicator.phase === 'active',
|
|
kind: 'slam' as const,
|
|
radius: indicator.radius,
|
|
x: indicator.position.x,
|
|
y: indicator.position.y,
|
|
}]
|
|
}
|
|
return []
|
|
})
|
|
}
|