Android build v1.1.23

This commit is contained in:
Warren H
2026-07-05 22:48:56 -04:00
parent 956c9e32f9
commit 9708ba9e40
106 changed files with 4294 additions and 458 deletions
+41 -16
View File
@@ -13,6 +13,7 @@ import type {
Iwt2PartyEntityId,
Iwt2PartyEntityState,
Iwt2ProjectileEntityState,
Iwt2RoguelikePressureState,
Iwt2StatusState,
Iwt2Vec2,
} from './types'
@@ -22,6 +23,7 @@ import { separateCircles } from './collision'
import { canPartyMemberHitTarget, tickPartyMember } from './partyAi'
import { addFirePuddle, createHazardIndicators, tickGroundHazards } from './hazards'
import { applyPartyDamageInShape } from './mechanics'
import { tickRoguelikePressure } from './roguelikePressure'
import {
addVec2,
clampVec2ToArena,
@@ -62,6 +64,8 @@ export function createInitialIwt2ArenaState(
bossId: Iwt2BossId = 'bulldrome',
bossIds?: Iwt2BossId[],
bossHealthScale = 1,
partyDamageTakenScale = 1,
roguelikePressure?: Iwt2RoguelikePressureState,
): Iwt2ArenaState {
const initialBossIds = bossIds?.length ? bossIds.slice(0, 2) : chooseInitialBossIds(bossId)
const bosses = initialBossIds.map((id, index) => createBossEntity(id, index, bossHealthScale))
@@ -69,11 +73,12 @@ export function createInitialIwt2ArenaState(
schemaVersion: 1,
time: 0,
bounds: { width: DEFAULT_ARENA_WIDTH, height: DEFAULT_ARENA_HEIGHT },
party: INITIAL_PARTY.map(createPartyMember),
party: INITIAL_PARTY.map((member) => createPartyMember(member, partyDamageTakenScale)),
projectiles: [],
hostileAdds: [],
hazards: [],
indicators: [],
roguelikePressure,
boss: bosses[0],
bosses,
nextEventId: 1,
@@ -216,8 +221,13 @@ export function tickIwt2Arena(state: Iwt2ArenaState, input: Iwt2ArenaInput, dt:
party: projectileResult.party,
time: separatedState.time,
})
const pressureResult = tickRoguelikePressure({
party: hazardResult.party,
pressure: separatedState.roguelikePressure,
time: separatedState.time,
})
const damageResult = applyPartyAttacks(
hazardResult.party,
pressureResult.party,
projectileResult.bosses,
projectileResult.hostileAdds,
separatedState.time,
@@ -234,7 +244,7 @@ export function tickIwt2Arena(state: Iwt2ArenaState, input: Iwt2ArenaInput, dt:
finalCollisionState,
)
const nextEvents = assignEventIds(
[...bossResult.events, ...projectileResult.events, ...hazardResult.events, ...damageResult.events, ...hotResult.events],
[...bossResult.events, ...projectileResult.events, ...hazardResult.events, ...pressureResult.events, ...damageResult.events, ...hotResult.events],
state.nextEventId,
)
return {
@@ -242,6 +252,7 @@ export function tickIwt2Arena(state: Iwt2ArenaState, input: Iwt2ArenaInput, dt:
boss: getPrimaryBoss(damageResult.bosses),
bosses: damageResult.bosses,
indicators: [...bossResult.indicators, ...createHazardIndicators(hazardResult.hazards)],
roguelikePressure: pressureResult.pressure,
party: finalParty,
hostileAdds: damageResult.hostileAdds,
hazards: hazardResult.hazards,
@@ -270,7 +281,10 @@ function tickPartyHotEffects(
let nextTickInSeconds = effect.nextTickInSeconds - dt
if (member.health > 0 && nextTickInSeconds <= 0) {
const before = health
health = Math.min(member.maxHealth, health + effect.power)
const healingMultiplier = member.shield > 0
? member.shieldedHotHealingMultiplier ?? 1
: 1
health = Math.min(member.maxHealth, health + effect.power * healingMultiplier)
const healed = health - before
if (healed > 0) {
events.push({
@@ -310,8 +324,9 @@ function regeneratePartyMana(party: Iwt2PartyEntityState[], dt: number): Iwt2Par
})
}
function createPartyMember(initial: InitialPartyMember): Iwt2PartyEntityState {
function createPartyMember(initial: InitialPartyMember, damageTakenScale: number): Iwt2PartyEntityState {
const metadata = IWT2_CLASS_METADATA[initial.classId]
const safeDamageTakenScale = Number.isFinite(damageTakenScale) ? Math.max(0.01, damageTakenScale) : 1
return {
id: initial.id,
kind: 'party',
@@ -321,8 +336,16 @@ function createPartyMember(initial: InitialPartyMember): Iwt2PartyEntityState {
velocity: { x: 0, y: 0 },
facing: { x: 1, y: 0 },
radius: metadata.radius,
moveSpeed: metadata.moveSpeed,
attackRange: metadata.attackRange,
attackDamage: metadata.attackDamage,
attackCooldown: metadata.attackCooldown,
castTime: metadata.castTime,
projectileSpeed: metadata.projectileSpeed,
health: metadata.maxHealth,
maxHealth: metadata.maxHealth,
damageTakenScale: safeDamageTakenScale,
stunTakenScale: 1,
shield: 0,
mana: initial.classId === 'healer' ? 100 : 0,
maxMana: initial.classId === 'healer' ? 100 : 0,
@@ -447,13 +470,15 @@ function separatePartyFromBosses(party: Iwt2PartyEntityState[], state: Iwt2Arena
let position = member.position
for (const boss of state.bosses) {
if (boss.health <= 0) continue
const overlapBeforeSeparation = circleOverlapAmount({ position, radius: member.radius }, boss)
const separated = separateCircles(
{ position, radius: member.radius },
{ position: boss.position, radius: boss.radius },
state.bounds,
)
position = wallClearance(separated, member, state) <= 2
|| circleOverlapAmount({ position: separated, radius: member.radius }, boss) > 0.25
const overlapAfterSeparation = circleOverlapAmount({ position: separated, radius: member.radius }, boss)
position = overlapBeforeSeparation > 0.25
&& (wallClearance(separated, member, state) <= 2 || overlapAfterSeparation > 0.25)
? separateFromBossTowardCenter(member, boss, state)
: separated
}
@@ -802,7 +827,7 @@ function applyPartyAttacks(
if (!target) return member
if (!canPartyMemberHitTarget(member, target.position, target.radius)) return member
const metadata = IWT2_CLASS_METADATA[member.classId]
if (metadata.projectileSpeed > 0) {
if (member.projectileSpeed > 0) {
if (!member.attackReady) return member
const direction = normalizeVec2(subtractVec2(target.position, member.position))
projectiles.push({
@@ -810,29 +835,29 @@ function applyPartyAttacks(
sourceId: member.id,
owner: 'party',
classId: member.classId,
projectileKind: member.classId === 'mage' ? 'magic' : 'arrow',
projectileKind: member.classId === 'mage' ? 'fireball' : 'arrow',
color: metadata.accentColor,
position: {
x: member.position.x + direction.x * (member.radius + 4),
y: member.position.y + direction.y * (member.radius + 4),
},
velocity: scaleVec2(direction, metadata.projectileSpeed),
velocity: scaleVec2(direction, member.projectileSpeed),
radius: member.classId === 'mage' ? 7 : 4,
damage: metadata.attackDamage,
damage: member.attackDamage,
remainingSeconds: 1.2,
})
projectileId += 1
return {
...member,
attackCooldownRemaining: metadata.attackCooldown,
attackCooldownRemaining: member.attackCooldown,
attackReady: false,
}
}
if (member.attackCooldownRemaining > 0) return member
let damage = Math.min(metadata.attackDamage, target.health)
let damage = Math.min(member.attackDamage, target.health)
let defeated = target.health - damage <= 0
if (target.kind === 'boss') {
const damageResult = applyDamageToBoss(target, metadata.attackDamage)
const damageResult = applyDamageToBoss(target, member.attackDamage)
damage = damageResult.healthDamage
defeated = damageResult.boss.health <= 0
nextBosses = nextBosses.map((boss) => boss.id === target.id
@@ -862,13 +887,13 @@ function applyPartyAttacks(
return {
...member,
damageDone: member.damageDone + damage,
attackCooldownRemaining: metadata.attackCooldown,
attackCooldownRemaining: member.attackCooldown,
}
}
return {
...member,
damageDone: member.damageDone + damage,
attackCooldownRemaining: metadata.attackCooldown,
attackCooldownRemaining: member.attackCooldown,
}
})
return {