paavohtl Claude Opus 4.6 (1M context) commited on
Commit
b189527
·
1 Parent(s): 78418c5

feat: port Girder weapon to pure Rust

Browse files

Port FireWeapon__Girder (0x51E350) — allocates CTaskFireBall (0xA4 bytes),
copies 7 DWORDs from fire_params, calls CTaskFireBall::Constructor via
naked asm bridge (usercall EAX=parent + 9 stack params, RET 0x24).

Same allocation+constructor pattern as CreateWeaponProjectile and CreateArrow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-wormkit/src/replacements/weapon.rs CHANGED
@@ -361,8 +361,8 @@ unsafe fn fire_weapon_special(
361
  1 => (*worm).set_state(WormState::Blowtorch),
362
  // Pneumatic Drill (pure Rust)
363
  2 => fire_drill(worm, local_struct),
364
- // Girder
365
- 3 => call_fire_stdcall3(worm, params_38_ptr, local_struct, rb(0x51E350)),
366
  // Baseball Bat
367
  4 => (*worm).set_state(WormState::BaseballBat),
368
  // Fire Punch
@@ -555,6 +555,74 @@ unsafe fn fire_mail_mine_mole(worm: *mut CTaskWorm) {
555
  entry.turn_action_counter_Maybe += 7;
556
  }
557
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  /// Freeze (subtype 17) — pure Rust port of 0x51E920.
559
  ///
560
  /// Plays a freeze sound, optionally creates a freeze visual overlay on the
 
361
  1 => (*worm).set_state(WormState::Blowtorch),
362
  // Pneumatic Drill (pure Rust)
363
  2 => fire_drill(worm, local_struct),
364
+ // Girder (pure Rust)
365
+ 3 => fire_girder(worm, params_38_ptr, local_struct),
366
  // Baseball Bat
367
  4 => (*worm).set_state(WormState::BaseballBat),
368
  // Fire Punch
 
555
  entry.turn_action_counter_Maybe += 7;
556
  }
557
 
558
+ /// Girder (type 4 subtype 3) — pure Rust port of 0x51E350.
559
+ ///
560
+ /// Allocates a CTaskFireBall (0xA4 bytes), copies 7 DWORDs from fire_params
561
+ /// as constructor arguments, and calls the constructor. The constructor handles
562
+ /// actually placing the girder on the landscape.
563
+ ///
564
+ /// Convention: stdcall(worm, fire_params, local_struct), RET 0xC.
565
+ unsafe fn fire_girder(
566
+ worm: *mut CTaskWorm, fire_params: *const WeaponFireParams, local_struct: *const u8,
567
+ ) {
568
+ use openwa_core::rebase::rb;
569
+ use openwa_core::task::{SharedDataTable, Task};
570
+ use openwa_core::wa_alloc::wa_malloc;
571
+
572
+ // Look up parent task via SharedData (same key as CreateWeaponProjectile)
573
+ let table = SharedDataTable::from_task((*worm).as_task_ptr());
574
+ let parent = table.lookup(0, 0x19);
575
+
576
+ // Allocate CTaskFireBall (0xA4 bytes), zero first 0x84
577
+ let buffer = wa_malloc(0xA4);
578
+ if buffer.is_null() {
579
+ return;
580
+ }
581
+ core::ptr::write_bytes(buffer, 0, 0x84);
582
+
583
+ // CTaskFireBall::Constructor — usercall(EAX=parent) +
584
+ // stdcall(this, 7×fire_param DWORDs, local_struct), RET 0x24.
585
+ // Copy 7 DWORDs from fire_params onto the stack via the naked bridge.
586
+ call_fireball_ctor(buffer, parent as *mut u8, fire_params, local_struct, rb(0x550890));
587
+ }
588
+
589
+ /// Bridge: CTaskFireBall::Constructor — usercall(EAX=parent) +
590
+ /// stdcall(this, 7 DWORDs from fire_params, local_struct), RET 0x24.
591
+ ///
592
+ /// The original copies 7 DWORDs from fire_params onto the stack via REP MOVSD.
593
+ /// We replicate this by pushing them individually in reverse order.
594
+ #[unsafe(naked)]
595
+ unsafe extern "C" fn call_fireball_ctor(
596
+ _this: *mut u8, _parent: *mut u8, _fire_params: *const WeaponFireParams,
597
+ _local_struct: *const u8, _addr: u32,
598
+ ) {
599
+ core::arch::naked_asm!(
600
+ "push ebx",
601
+ "push esi",
602
+ "push edi",
603
+ // Stack: 3 saves(12) + ret(4) = 16 to first arg
604
+ // Args: this=+16, parent=+20, fire_params=+24, local_struct=+28, addr=+32
605
+ "mov esi, [esp+24]", // ESI = fire_params
606
+ "mov eax, [esp+20]", // EAX = parent (usercall)
607
+ "mov ebx, [esp+32]", // EBX = ctor address
608
+ // Push constructor args in reverse: local_struct, 7 DWORDs, this
609
+ "push [esp+28]", // local_struct (B+28 → stack pos 1)
610
+ "push [esi+24]", // fire_params[6]
611
+ "push [esi+20]", // fire_params[5]
612
+ "push [esi+16]", // fire_params[4]
613
+ "push [esi+12]", // fire_params[3]
614
+ "push [esi+8]", // fire_params[2]
615
+ "push [esi+4]", // fire_params[1]
616
+ "push [esi]", // fire_params[0] (B-28 → stack pos 8)
617
+ "push [esp+48]", // this: B-32+48 = B+16 ✓
618
+ "call ebx", // RET 0x24 cleans 9 params
619
+ "pop edi",
620
+ "pop esi",
621
+ "pop ebx",
622
+ "ret",
623
+ );
624
+ }
625
+
626
  /// Freeze (subtype 17) — pure Rust port of 0x51E920.
627
  ///
628
  /// Plays a freeze sound, optionally creates a freeze visual overlay on the