# Weapon System Comprehensive reference for the Worms Armageddon 3.8.1 weapon system internals. ## Overview The weapon system spans multiple subsystems: a **weapon table** defining all 71 weapons, an **ammo/delay tracker** per team/alliance, a **firing dispatch** that routes weapon activation to type-specific handlers, **CTaskMissile** objects for in-flight projectiles, and an **explosion system** for terrain damage. ### Object Relationships ```mermaid graph TD DDGame["DDGame (0x98B8 bytes)"] WT["WeaponTable
DDGame+0x510"] TAS["TeamArenaState
DDGame+0x4628"] WP["WeaponPanel
DDGame+0x548"] CTW["CTaskWorm
(per-live-worm task)"] CTM["CTaskMissile
(per-projectile task)"] EXP["CreateExplosion"] LAND["PCLandscape"] DDGame --> WT DDGame --> TAS DDGame --> WP CTW -->|"FireWeapon"| CTM CTW -->|"GetAmmo/SubtractAmmo"| TAS CTM -->|"impact"| EXP EXP -->|"terrain damage"| LAND WP -->|"reads availability"| WT WP -->|"reads ammo"| TAS ``` ### Firing Sequence ```mermaid sequenceDiagram participant P as Player Input participant W as CTaskWorm participant FR as WeaponRelease participant FW as FireWeapon participant CW as CreateWeaponProjectile participant M as CTaskMissile participant E as CreateExplosion participant L as PCLandscape P->>W: Fire key pressed W->>FR: WeaponRelease(0x51C3D0)
position, angle FR->>FW: FireWeapon(0x51EE60)
weapon type dispatch FW->>CW: CreateWeaponProjectile(0x51E0F0) CW->>M: CTaskMissile__Constructor(0x507D10)
allocate 0x40C bytes loop Every frame M->>M: PhysicsUpdate(0x508C90)
gravity, wind, collision end M->>E: CreateExplosion(0x548080) E->>L: ApplyExplosion(0x57C820)
terrain bitmap damage ``` ## WeaponEntry & WeaponTable ### WeaponTable (DDGame+0x510) Allocated by `InitWeaponTable` (0x53CAB0, stdcall(wrapper), RET 0x4). Called once during `CTaskGameState__vmethod_7` at game start. ``` +0x00 [WeaponEntry; 71] entries Standard weapons (0..70) Total: 71 × 0x1D0 = 0x80B0 bytes (no header) ``` ### WeaponEntry (0x1D0 = 464 bytes per entry) Source: `wkJellyWorm/src/CustomWeapons.h` (WeaponStruct) + InitWeaponTable analysis. ``` +0x00 *const c_char name1 Primary weapon name string +0x04 *const c_char name2 Secondary weapon name string +0x08 i32 panel_state Panel state (init: 0xFFFFFFFF). wkJellyWorm: panelRow +0x0C i32 unknown_0c Unknown +0x10 i32 defined Nonzero = weapon exists. Checked by CheckWeaponAvail. +0x14 [u8; 0x10] unknown_14 Unknown +0x24 i32 availability Init: 0xFFFFFFFF, then 0 or 1. None/SkipGo/Surrender = 0. +0x28 i32 enabled Init: 1 +0x2C [u8; 0x1A4] unknown_2c Remaining fields (opaque) ``` **InitWeaponTable per-entry initialization:** - +0x08 = 0xFFFFFFFF (panel_state) - +0x24 = 0xFFFFFFFF (availability, then overwritten per-weapon) - +0x28 = 1 (enabled) - Weapon 0 (None), 57 (SkipGo), 58 (Surrender): availability forced to 0 **Rust types:** `WeaponEntry`, `WeaponTable` in `openwa-core/src/game/weapon.rs`. ### Weapon Enum (71 entries) Defined in `openwa-core/src/game/weapon.rs`. Key weapon IDs: | ID | Hex | Name | Category | |----|-----|------|----------| | 0 | 0x00 | None | - | | 1 | 0x01 | Bazooka | Projectile | | 2 | 0x02 | HomingMissile | Projectile (homing) | | 6 | 0x06 | Grenade | Thrown | | 10 | 0x0A | Earthquake | Special (net-disabled) | | 24 | 0x18 | SuperSheep | Animal (restricted) | | 25 | 0x19 | AquaSheep | Animal (restricted) | | 37 | 0x25 | NinjaRope | Rope | | 40 | 0x28 | Teleport | Utility (immune to sudden death) | | 54 | 0x36 | Donkey | Special (disable flag) | | 55 | 0x37 | NuclearTest | Special (net-disabled) | | 56 | 0x38 | Armageddon | Special (net-disabled) | | 59 | 0x3B | SelectWorm | Action (needs >1 alive worm) | | 66 | 0x42 | Invisibility | Power-up (mode flag) | | 69 | 0x45 | DoubleTurnTime | Power-up (threshold) | ## Ammunition & Delay System ### Storage: TeamArenaState (DDGame+0x4628) Ammo and delay are stored in a flat array indexed by alliance and weapon ID. ``` Flat index = alliance_id × 142 + weapon_id Ammo: TeamArenaState + 0x1EB4 + flat_index × 4 Delay: TeamArenaState + 0x1EB4 + (flat_index + 71) × 4 Values: -1 = unlimited, 0 = none, >0 = count Delay: 0 = available, >0 = turns until available ``` 6 alliances × 142 entries (71 ammo + 71 delay) = 852 total slots. Alliance ID is read from the team's sentinel worm field at offset +0x80 (`sentinel_weapon_alliance`). ### Functions | Function | Address | Convention | Parameters | |----------|---------|------------|------------| | GetAmmo | 0x5225E0 | usercall | EAX=team_index, ESI=arena_base, EDX=weapon_id → EAX=ammo | | AddAmmo | 0x522640 | usercall | EAX=team_index, EDX=amount, stack(base, weapon_id), RET 0x8 | | SubtractAmmo | 0x522680 | usercall | EAX=team_index, ECX=arena_base, stack(weapon_id), RET 0x4 | | SubtractAmmo_v2 | 0x558E80 | fastcall | ECX=param1, EDX=CTaskTeam* | | CountAliveWorms | 0x5225A0 | usercall | EAX=team_index, ECX=arena_base → EAX=count | **GetAmmo special rules:** - Returns 0 if weapon delay > 0 (weapon on cooldown) - Sudden death (game_phase ≥ 484): returns 0 for all except Teleport (0x28) - SelectWorm (0x3B): returns 0 if team has ≤1 alive worm - Game mode flag at TeamArenaState+0x2C0C can override delay checks **SubtractAmmo call sites (verified):** - `CTaskWorm__HandleMessage` (0x512C27): subtracts Teleport (0x28) ammo - `CTaskTeam_vt2__vmethod_2` (0x558443): subtracts the team's selected weapon - `FUN_0055f8c0` (3 sites): subtracts DamageX2 (0x43), CrateSpy (0x44), and others Our hook reportedly never triggered — this was because the test replay (bots) doesn't use Teleport, DamageX2, or CrateSpy. The hook implementation is correct. The second variant at 0x558E80 (fastcall) has 3 xrefs from `CTaskTeam_vt2__vmethod_2`. **Port status:** All 4 functions ported in `replacements/weapon.rs`. ## Weapon Availability ### DDGame__CheckWeaponAvail (0x53FFC0) Convention: fastcall(DDGame), ESI=weapon_id. Ported as `check_weapon_avail` in `openwa-core/src/engine/game_state_init.rs`. **Per-weapon disable rules:** | Weapon(s) | Condition | Result | |-----------|-----------|--------| | Earthquake (10), NuclearTest (55), Armageddon (56) | `net_config_2 != 0 && net_weapon_exception == 0` | Disabled in network games | | Donkey (54) | `donkey_disabled != 0` | GameInfo+0xD94C flag | | Invisibility (66) | `invisibility_mode == 0 && network_ecx == 0` | Needs network or mode flag | | DoubleTurnTime (69) | `game_version > 0xD1 && threshold > 0x7FFF` | GameInfo+0xD932 threshold | **General availability path:** 1. Check weapon table entry: name1 non-null = weapon defined 2. If `level_width_raw == 0` OR weapon defined: check super weapon rules 3. `IsSuperWeapon` (0x565960): if super and `super_weapon_allowed == 0`, disable (unless game_version < 0x2A) 4. `supersheep_restricted` (DDGame+0x7E25): gates SuperSheep/AquaSheep check 5. `aquasheep_is_supersheep` (GameInfo+0xD956): when set, AquaSheep slot becomes SuperSheep ### DDGame__IsSuperWeapon (0x565960) Convention: fastcall(weapon_id). Returns 1 for super weapons: Super weapon IDs: 16, 19, 29-31, 36, 41-42, 45-46, 49-51, 54-56, 60-61 ## FireWeapon Dispatch (0x51EE60) Convention: thiscall. `in_EAX` = weapon context structure where: - +0x30: weapon type (1-4) - +0x34: subtype for type 3 and type 4 - +0x38: subtype for type 1 and type 2 - +0x3C: parameters base (passed to sub-functions) `param_2` = DDGameWrapper pointer. Sets `param_2[0xF] = 1` on completion. ### Type 1: Projectile Weapons | Subtype | Function | Address | Weapons | |---------|----------|---------|---------| | 1 | FireWeapon__PlacedExplosive | 0x51EC80 | Dynamite, Mine, Petrol Bomb | | 2 | FireWeapon__Projectile | 0x51DFB0 | Bazooka, Mortar, Longbow | | 3 | CreateWeaponProjectile | 0x51E0F0 | Homing Missile, Homing Pigeon | | 4 | FireWeapon__Shotgun | 0x51ED90 | Shotgun, Handgun, Uzi, Minigun | ### Type 2: Rope Weapons | Subtype | Function | Address | Weapons | |---------|----------|---------|---------| | 1 | FireWeapon__RopeType1 | 0x51E1C0 | Ninja Rope (attach) | | 2 | CreateWeaponProjectile | 0x51E0F0 | Ninja Rope (projectile mode) | | 3 | FireWeapon__RopeType3 | 0x51E240 | Bungee | ### Type 3: Thrown Weapons | Subtype | Function | Address | Weapons | |---------|----------|---------|---------| | (all) | FireWeapon__GrenadeMortar | 0x51E2C0 | Grenade, Cluster Bomb, Banana Bomb, Holy Grenade | ### Type 4: Special Weapons | Subtype | Function | Address | Description | |---------|----------|---------|-------------| | 1 | vtable[0xE](0x6C) | (indirect) | Blowtorch | | 2 | FireWeapon__Special_Type2 | 0x51E3E0 | Pneumatic Drill | | 3 | FireWeapon__Special_Type3 | 0x51E350 | Girder | | 4 | vtable[0xE](0x6D) | (indirect) | Baseball Bat | | 5 | vtable[0xE](0x75) | (indirect) | Fire Punch | | 6 | vtable[0xE](0x70) | (indirect) | Dragon Ball | | 8 | vtable[0xE](0x6E) | (indirect) | Kamikaze | | 9 | FireWeapon__Special_Type9 | 0x51E480 | Prod | | 10 | FireWeapon__Special_Type10 | 0x51E710 | Air Strike | | 11 | vtable[0xE](0x71) | (indirect) | Scales of Justice | | 13 | FireWeapon__Special_Type13 | 0x51E5C0 | Napalm Strike | | 14 | FireWeapon__Special_Type14 | 0x51E670 | Mail/Mine/Mole Strike | | 16 | FUN_0051EB00 (conditional) | 0x51EB00 | Teleport (checks FUN_516930 first) | | 17 | FireWeapon__Special_Type17 | 0x51E920 | Freeze | | 18 | vtable[0xE](0x72) | (indirect) | Suicide Bomber | | 19 | FireWeapon__Special_Type19 | 0x51E8C0 | Skip Go | | 20 | FireWeapon__Special_Type20 | 0x51E600 | Surrender | | 21 | FireWeapon__Special_Type21 | 0x51EBE0 | Select Worm | | 22 | FireWeapon__Special_Type22 | 0x51EC30 | Jet Pack | | 23 | vtable[0xE](0x78) | (indirect) | Magic Bullet | | 24 | FireWeapon__Special_Type24 | 0x51EA60 | Low Gravity / Fast Walk | ## CTaskMissile (0x40C bytes) Extends CGameTask (0xFC base). Represents an in-flight projectile. ``` Constructor: 0x507D10 (stdcall, 4 params) Vtable: 0x664438 Class type: 0x0B Allocation: 0x40C bytes via wa_malloc ``` ### Structure Layout ``` +0x000 CGameTask base (0xFC bytes) +0x084 speed_x Fixed16.16 horizontal velocity +0x088 speed_y Fixed16.16 vertical velocity +0x090 pos_x Fixed16.16 world X +0x094 pos_y Fixed16.16 world Y +0x128 u32 launch_seed Position-derived seed +0x12C u32 slot_id Object pool slot index +0x130 spawn_params [11 DWORDs = 44 bytes] [0] owner_id Team that fired [2-3] spawn_x/y Fixed16.16 launch position [4-5] speed_x/y Fixed16.16 initial velocity [8] pellet_index Cluster volley index +0x15C weapon_data [94 DWORDs = 376 bytes] [0x0F] gravity_pct 100 = 1.0× gravity [0x10] bounce_pct Bounce coefficient [0x12] friction_pct Friction coefficient [0x1A] missile_type 2=Standard, 3=Homing, 4=Sheep, 5=Cluster [0x1C] render_timer Fuse countdown +0x2D4 render_data [42 DWORDs = 168 bytes] Physics parameters (shifted copy of weapon_data) +0x3C8 i32 direction -1 (left) or +1 (right) ``` ### Missile Type Enum | Value | Type | Behavior | |-------|------|----------| | 2 | Standard | Straight trajectory with gravity | | 3 | Homing | Tracks target position | | 4 | Sheep | Walking behavior on terrain | | 5 | Cluster | Splits into sub-munitions on impact | ### Key Methods | Method | Address | Description | |--------|---------|-------------| | Constructor | 0x507D10 | Initialize physics, copy weapon data from scheme | | Init | 0x508640 | Reset physics state | | PhysicsUpdate | 0x508C90 | Per-frame trajectory, gravity, wind, collision | | HandleMessage | 0x50B400 | Event processing (detonation triggers, etc.) | | Free | 0x508330 | Cleanup and pool return | | vt12 | 0x508C70 | Vtable slot 12 | | vt18_GetField130 | 0x50BFA0 | Returns spawn_params pointer | ### Missile Count Limit DDGame+0x72A4 tracks active missile count. `CreateWeaponProjectile` checks: - If count + 7 < 0x2BD (701): create missile normally - If exceeded and game_version < 0x3C: set error code 6 at DDGame+0x4624, load string resource 0x70F as error message at DDGame+0x7EF4 - Otherwise: silently fail ## Projectile Creation (0x51E0F0) `CreateWeaponProjectile`: thiscall(this=context, param_2=missile_params, param_3=launch_params). 1. Read DDGame via `this+0x2C` (CTask.ddgame field) 2. Check missile count limit (DDGame+0x72A4 + 7 < 0x2BD) 3. Call `FUN_004FDF90(this)` — pool slot allocator 4. `wa_malloc(0x40C)` + `memset(0, 0x3EC)` 5. `CTaskMissile__Constructor(slot, missile_params, launch_params)` 6. Return allocated missile pointer ## Explosion & Terrain Damage | Function | Address | Convention | Description | |----------|---------|------------|-------------| | CreateExplosion | 0x548080 | usercall(ESI=context) | Spawns explosion task via pool allocator + vtable dispatch | | PCLandscape_ApplyExplosion | 0x57C820 | (unknown) | Modifies terrain bitmap, creates crater | CreateExplosion is a thin wrapper: allocates via `FUN_004FDF90`, then calls `vtable[2]()` on the allocated object. The actual explosion logic (radius, damage, terrain removal) lives in the explosion task's ProcessFrame. ## Weapon Panel UI | Function | Address | Description | |----------|---------|-------------| | DDGame__InitWeaponPanel_Maybe | 0x567770 | Initialize panel structures and availability grid | | PrepareWeaponPanel | 0x568220 | Main panel renderer (~2000 lines). Iterates weapons by row, checks availability, renders tiles | | WeaponPanelDrawTile | 0x567AA0 | Render individual weapon tile | | WeaponPanelDescription | 0x567F80 | Show ammo/delay info on hover | | WeaponPanelCheckClick | 0x5690B0 | Handle weapon selection clicks | **Panel geometry:** 5 columns × 13 rows. Each tile is 29px wide. Custom weapons (wkJellyWorm): dynamic columns up to 256 weapons. ## Weapon Crate System - `CTaskCrate__Constructor` (0x502490): creates a weapon crate in the game world - Crate probability per weapon defined in scheme (`WeaponSettings.crate_probability`, 0-100%) - Crate drops award random weapons weighted by probability ## Scheme Integration Weapon configuration flows from `.wsc` scheme files into GameInfo, then into the weapon table and ammo arrays at game start. ### WeaponSettings (4 bytes per weapon) ``` +0x00 u8 ammo 0=none, 1-10=count, 0x80+=infinite +0x01 u8 power 0-20 (max varies per weapon) +0x02 u8 delay Turn cooldown before available +0x03 u8 crate_probability 0-100 percentage ``` ### Scheme Formats | Version | Payload | Weapons | |---------|---------|---------| | V1 | 0xD8 bytes | 45 standard weapons | | V2 | 0x124 bytes | V1 + 19 super weapons | | V3 | 0x192 bytes | V2 + 110 extended options (physics tweaks) | **Port status:** Scheme parsing fully ported in `openwa-core/src/game/scheme.rs` (10 hooks in `replacements/scheme.rs`). ## Complete Function Address Table ### Weapon Table & Initialization | Function | Address | Convention | Port Status | |----------|---------|------------|-------------| | InitWeaponTable | 0x53CAB0 | stdcall(wrapper), RET 0x4 | Hooked (passthrough) | | DDGame__CheckWeaponAvail | 0x53FFC0 | fastcall(DDGame) | **Ported** | | DDGame__IsSuperWeapon | 0x565960 | fastcall(weapon_id) | **Ported** | | DDGame__LoadHudAndWeaponSprites | 0x53D0E0 | stdcall(wrapper, ...) | Bridge call | ### Ammo System | Function | Address | Convention | Port Status | |----------|---------|------------|-------------| | GetAmmo | 0x5225E0 | usercall(EAX,ESI,EDX) | **Ported** | | AddAmmo | 0x522640 | usercall(EAX,EDX,stack), RET 0x8 | **Ported** | | SubtractAmmo | 0x522680 | usercall(EAX,ECX,stack), RET 0x4 | **Ported** (may not trigger) | | SubtractAmmo_v2 | 0x558E80 | fastcall(ECX,EDX) | Unported | | CountAliveWorms | 0x5225A0 | usercall(EAX,ECX) | **Ported** | ### Firing & Projectiles | Function | Address | Convention | Port Status | |----------|---------|------------|-------------| | WeaponRelease | 0x51C3D0 | thiscall(CTaskWorm*, ...) | Unported | | FireWeapon | 0x51EE60 | thiscall, EAX=weapon_ctx | Unported | | CreateWeaponProjectile | 0x51E0F0 | thiscall(context, params, launch) | Unported | | CTaskMissile__Constructor | 0x507D10 | stdcall, 4 params | Unported | | CTaskMissile__PhysicsUpdate | 0x508C90 | (unknown) | Unported | | CTaskMissile__HandleMessage | 0x50B400 | thiscall | Unported | | CreateExplosion | 0x548080 | usercall(ESI=context) | Unported | | PCLandscape_ApplyExplosion | 0x57C820 | (unknown) | Unported | ### Fire Dispatch Targets | Function | Address | Weapon Category | |----------|---------|-----------------| | FireWeapon__Projectile | 0x51DFB0 | Bazooka-class | | FireWeapon__PlacedExplosive | 0x51EC80 | Dynamite/Mine/Petrol | | FireWeapon__Shotgun | 0x51ED90 | Shotgun/Handgun/Uzi | | FireWeapon__RopeType1 | 0x51E1C0 | Ninja Rope attach | | FireWeapon__RopeType3 | 0x51E240 | Bungee | | FireWeapon__GrenadeMortar | 0x51E2C0 | Grenade/Cluster/Banana | | FireWeapon__Special_Type2 | 0x51E3E0 | Pneumatic Drill | | FireWeapon__Special_Type3 | 0x51E350 | Girder | | FireWeapon__Special_Type9 | 0x51E480 | Prod | | FireWeapon__Special_Type10 | 0x51E710 | Air Strike | | FireWeapon__Special_Type13 | 0x51E5C0 | Napalm Strike | | FireWeapon__Special_Type14 | 0x51E670 | Mail/Mine/Mole Strike | ### Weapon Panel | Function | Address | Convention | Port Status | |----------|---------|------------|-------------| | DDGame__InitWeaponPanel_Maybe | 0x567770 | thiscall | Unported | | PrepareWeaponPanel | 0x568220 | cdecl | Unported | | WeaponPanelDrawTile | 0x567AA0 | (unknown) | Unported | | WeaponPanelDescription | 0x567F80 | (unknown) | Unported | | WeaponPanelCheckClick | 0x5690B0 | (unknown) | Unported | ### Scheme | Function | Address | Convention | Port Status | |----------|---------|------------|-------------| | Scheme__CheckWeaponLimits | 0x4D50E0 | stdcall | **Ported** | | Scheme__ValidateExtendedOptions | 0x4D5110 | stdcall | **Ported** | | Scheme__ReadFile | 0x4D3890 | stdcall | **Ported** | ## Port Recommendations ### Already Done - Weapon enum and struct types (`WeaponEntry`, `WeaponTable`) - Ammo system (4 hooks: GetAmmo, AddAmmo, SubtractAmmo, CountAliveWorms) - Availability checks (2 hooks: CheckWeaponAvail, IsSuperWeapon) - Scheme parsing (10 hooks covering all scheme I/O) - Team/worm state queries (8 hooks in replacements/team.rs) ### Next Priority 1. **InitWeaponTable internals** — populate WeaponEntry fields from scheme data 2. **SubtractAmmo investigation** — verify hook triggers, check v2 variant 3. **Weapon panel init** — DDGame__InitWeaponPanel_Maybe (self-contained) ### Medium Term 4. **FireWeapon dispatch** — the type/subtype routing logic 5. **CreateWeaponProjectile** — missile allocation and pool management 6. **WeaponRelease** — coordinate transform and launch logic ### Long Term 7. **CTaskMissile physics** — gravity, wind, collision, homing AI 8. **Individual weapon behaviors** — each Fire__* handler 9. **Explosion system** — terrain damage, damage calculation 10. **Weapon panel rendering** — PrepareWeaponPanel (~2000 lines)