wormz-a / crates /openwa-core /src /engine /team_ops.rs
paavohtl's picture
Claude Opus 4.6 (1M context)
refactor: rename TeamArenaState to TeamArena, use raw pointers for soundness
e80ee6b
Raw History Blame Contribute Delete
6.7 kB
//! Team and worm state accessor functions.
//!
//! Pure Rust reimplementations of WA.exe functions that access the TeamArena
//! area (DDGame + 0x4628). Called from hook trampolines in openwa-dll.
//!
//! Original WA functions:
//! - CountTeamsByAlliance (0x522030)
//! - GetTeamTotalHealth (0x5224D0)
//! - IsWormInSpecialState (0x5226B0)
//! - GetWormPosition (0x522700)
//! - CheckWormState0x64 (0x5228D0)
//! - CheckTeamWormState0x64 (0x522930)
//! - CheckAnyWormState0x8b (0x522970)
//! - SetActiveWorm_Maybe (0x522500)
use crate::engine::team_arena::worm;
use super::team_arena::TeamArena;
/// Count teams by alliance membership β€” port of 0x522030.
///
/// Updates `state.active_team_count`, `same_alliance_count`, `enemy_team_count`.
/// Uses Pattern B header fields (+0x70/+0x74) for alliance and active_worm.
pub unsafe fn count_teams_by_alliance(arena: *mut TeamArena, alliance_id: i32) {
(*arena).current_alliance = alliance_id;
(*arena).active_team_count = 0;
(*arena).same_alliance_count = 0;
(*arena).enemy_team_count = 0;
for i in 0..(*arena).team_count as usize {
let header = TeamArena::team_header_b(arena, i);
let team_alliance = (*header).alliance;
let active_worm = (*header).active_worm;
if active_worm != 0 && team_alliance >= 0 {
(*arena).active_team_count += 1;
if team_alliance == alliance_id {
(*arena).same_alliance_count += 1;
} else {
(*arena).enemy_team_count += 1;
}
}
}
}
/// Sum worm health for a team β€” port of 0x5224D0.
///
/// Returns 0 if the team is eliminated.
pub unsafe fn get_team_total_health(team_index: u32, arena: *const TeamArena) -> i32 {
let header = TeamArena::team_header(arena, team_index as usize);
if (*header).eliminated != 0 {
return 0;
}
let worm_count = (*header).worm_count;
let mut total = 0i32;
for w in 1..=worm_count as usize {
total += (*TeamArena::team_worm(arena, team_index as usize, w)).health;
}
total
}
/// Check worm state flag β€” port of 0x5226B0.
///
/// Returns 1 if the worm is in a "special" state, 0 otherwise.
pub unsafe fn is_worm_in_special_state(
team_index: u32,
worm_index: u32,
arena: *const TeamArena,
) -> u32 {
if worm::is_special_state(
(*TeamArena::team_worm(arena, team_index as usize, worm_index as usize)).state,
) {
1
} else {
0
}
}
/// Read worm X,Y coordinates β€” port of 0x522700.
///
/// Reads pos_x/pos_y from WormEntry._unknown_90[0..4] / [4..8].
/// These values appear transient β€” actual worm positions live in CGameTask objects.
pub unsafe fn get_worm_position(
team_index: u32,
worm_index: u32,
arena: *const TeamArena,
out_x: *mut i32,
out_y: *mut i32,
) {
let worm = TeamArena::team_worm(arena, team_index as usize, worm_index as usize);
*out_x = *((*worm)._unknown_90.as_ptr() as *const i32);
*out_y = *((*worm)._unknown_90.as_ptr().add(4) as *const i32);
}
/// Check if any worm on any team has state 0x64 β€” port of 0x5228D0.
///
/// Iterates real teams (1-indexed). 11 xrefs in gameplay code.
pub unsafe fn check_worm_state_0x64(arena: *const TeamArena) -> u32 {
for i in 1..=(*arena).team_count as usize {
let header = TeamArena::team_header(arena, i);
if (*header).eliminated != 0 {
continue;
}
let worm_count = (*header).worm_count;
for w in 1..=worm_count as usize {
if (*TeamArena::team_worm(arena, i, w)).state == 0x64 {
return 1;
}
}
}
0
}
/// Check if any worm on a specific team has state 0x64 β€” port of 0x522930.
///
/// Per-team version. 1 xref (FUN_00556ad0).
pub unsafe fn check_team_worm_state_0x64(arena: *const TeamArena, team_idx: u32) -> u32 {
let header = TeamArena::team_header(arena, team_idx as usize);
if (*header).eliminated != 0 {
return 0;
}
let worm_count = (*header).worm_count;
for w in 1..=worm_count as usize {
if (*TeamArena::team_worm(arena, team_idx as usize, w)).state == 0x64 {
return 1;
}
}
0
}
/// Scan all teams for state 0x8b β€” port of 0x522970.
///
/// 1 xref (FUN_00557310).
pub unsafe fn check_any_worm_state_0x8b(arena: *const TeamArena) -> u32 {
for i in 1..=(*arena).team_count as usize {
let header = TeamArena::team_header(arena, i);
if (*header).eliminated != 0 {
continue;
}
let worm_count = (*header).worm_count;
for w in 1..=worm_count as usize {
if (*TeamArena::team_worm(arena, i, w)).state == 0x8b {
return 1;
}
}
}
0
}
/// Set the active worm for a team β€” port of 0x522500.
///
/// `worm_index=0` deactivates the team. `worm_index=N` sets worm N as active.
/// Called on turn transitions and worm selection (Tab).
/// Updates active counters and records last_active_team/alliance.
/// Uses Pattern B headers (0-indexed from `team_idx - 1`).
pub unsafe fn set_active_worm(arena: *mut TeamArena, team_idx: u32, worm_index: i32) {
// Pattern B header: team_idx is 1-indexed, team_header_b is 0-indexed
// team_idx=1 β†’ team_header_b(0) β†’ blocks[2].header
let header = TeamArena::team_header_b_mut(arena, team_idx as usize - 1);
if worm_index == 0 {
// Deactivate team
if (*header).active_worm != 0 {
(*arena).active_worm_count -= 1;
let alliance = (*header).alliance;
if alliance >= 0 {
(*arena).active_team_count -= 1;
if alliance == (*arena).current_alliance {
(*arena).same_alliance_count -= 1;
} else {
(*arena).enemy_team_count -= 1;
}
}
(*header).active_worm = 0;
}
} else {
// Activate team β€” only update counters if not already active
if (*header).active_worm == 0 {
(*arena).active_worm_count += 1;
(*arena).last_active_team = team_idx as i32;
let alliance = (*header).alliance;
(*arena).last_active_alliance = alliance;
if alliance >= 0 {
(*arena).active_team_count += 1;
if alliance == (*arena).current_alliance {
(*arena).same_alliance_count += 1;
} else {
(*arena).enemy_team_count += 1;
}
}
}
// Always write worm_index (original writes ESI unconditionally)
(*header).active_worm = worm_index;
}
}