chore: Add new DDGame & GameInfo fields based on game state init functions
Browse files
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -218,8 +218,16 @@ pub struct DDGame {
|
|
| 218 |
pub rng_state_1: u32,
|
| 219 |
/// 0x72F0: RNG state word 2 (changes every frame).
|
| 220 |
pub rng_state_2: u32,
|
| 221 |
-
/// 0x72F4
|
| 222 |
-
pub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
/// 0x7308: Sprite/gfx dimension data (passed to GFX_DIR_LOAD_SPRITES).
|
| 224 |
pub gfx_sprite_data: [u8; 0x730C - 0x7308],
|
| 225 |
/// 0x730C-0x732F: GfxDir color table (9 entries).
|
|
@@ -232,8 +240,22 @@ pub struct DDGame {
|
|
| 232 |
pub _unknown_7330: [u8; 8],
|
| 233 |
/// 0x7338: Fill pixel value
|
| 234 |
pub fill_pixel: u32,
|
| 235 |
-
/// 0x733C-
|
| 236 |
-
pub _unknown_733c: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
|
| 238 |
/// 0x7380: Viewport width (Fixed-point, e.g. 960.0 = 0x03C00000).
|
| 239 |
pub viewport_width: i32,
|
|
@@ -274,8 +296,16 @@ pub struct DDGame {
|
|
| 274 |
pub level_width_raw: u32,
|
| 275 |
/// 0x7780: Level height output (written by PCLandscape constructor param 11).
|
| 276 |
pub level_height_raw: u32,
|
| 277 |
-
/// 0x7784
|
| 278 |
-
pub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
/// 0x779C: Level bound min X (Fixed16.16, negative = off-screen left).
|
| 281 |
pub level_bound_min_x: Fixed,
|
|
@@ -300,15 +330,43 @@ pub struct DDGame {
|
|
| 300 |
/// 0x77C8: Total pixels (width × height).
|
| 301 |
pub level_total_pixels: u32,
|
| 302 |
|
| 303 |
-
/// 0x77CC-
|
| 304 |
-
pub _unknown_77cc: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
|
| 306 |
/// 0x77E4: Speech slot table. Maps (team, speech_line_id) → DSSound buffer index.
|
| 307 |
/// Cleared by DSSound_LoadAllSpeechBanks (0x571A70), filled by DDGameWrapper__LoadSpeechWAV (0x571530).
|
| 308 |
pub speech_slot_table: SpeechSlotTable,
|
| 309 |
|
| 310 |
-
/// 0x7D84
|
| 311 |
-
pub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
/// 0x7E25: SuperSheep/AquaSheep restriction active flag (byte).
|
| 313 |
/// When nonzero, the SuperSheep/AquaSheep weapon check is applied.
|
| 314 |
pub supersheep_restricted: u8,
|
|
@@ -326,8 +384,20 @@ pub struct DDGame {
|
|
| 326 |
/// 0x7E40: Game logic version byte 4 (set by InitVersionFlags).
|
| 327 |
/// Used in FireWeapon__MailMineMole to gate vtable call behavior by version.
|
| 328 |
pub version_flag_4: u8,
|
| 329 |
-
/// 0x7E41
|
| 330 |
-
pub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
/// 0x7EA0: Flag/counter (value 4 at runtime — likely team count).
|
| 333 |
pub field_7ea0: u32,
|
|
@@ -355,15 +425,29 @@ pub struct DDGame {
|
|
| 355 |
/// 0x8140: Number of entries currently in the sound queue (0–16).
|
| 356 |
pub sound_queue_count: i32,
|
| 357 |
|
| 358 |
-
/// 0x8144-
|
| 359 |
-
pub _unknown_8144: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 360 |
/// 0x8150: Parallax/camera scale factor (Fixed-point multiplier).
|
| 361 |
/// Used by DrawCrosshairLine (multiplied by 0x140000) and
|
| 362 |
/// CTaskCloud render (parallax X offset = wind_speed * this >> 16).
|
| 363 |
pub parallax_scale: i32,
|
| 364 |
|
| 365 |
-
/// 0x8154-
|
| 366 |
-
pub _unknown_8154: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 367 |
|
| 368 |
/// 0x818C: Turn status text buffer (null-terminated ASCII).
|
| 369 |
/// Shows on screen during gameplay, e.g. "Cheesy harkitseee siirtoaan"
|
|
|
|
| 218 |
pub rng_state_1: u32,
|
| 219 |
/// 0x72F0: RNG state word 2 (changes every frame).
|
| 220 |
pub rng_state_2: u32,
|
| 221 |
+
/// 0x72F4: Unknown (zeroed by InitTurnState).
|
| 222 |
+
pub _field_72f4: u32,
|
| 223 |
+
/// 0x72F8: Unknown (zeroed by InitTurnState).
|
| 224 |
+
pub _field_72f8: u32,
|
| 225 |
+
/// 0x72FC: Unknown (zeroed by InitTurnState).
|
| 226 |
+
pub _field_72fc: u32,
|
| 227 |
+
/// 0x7300: Unknown (zeroed by InitTurnState).
|
| 228 |
+
pub _field_7300: u32,
|
| 229 |
+
/// 0x7304: Unknown (zeroed by InitTurnState).
|
| 230 |
+
pub _field_7304: u32,
|
| 231 |
/// 0x7308: Sprite/gfx dimension data (passed to GFX_DIR_LOAD_SPRITES).
|
| 232 |
pub gfx_sprite_data: [u8; 0x730C - 0x7308],
|
| 233 |
/// 0x730C-0x732F: GfxDir color table (9 entries).
|
|
|
|
| 240 |
pub _unknown_7330: [u8; 8],
|
| 241 |
/// 0x7338: Fill pixel value
|
| 242 |
pub fill_pixel: u32,
|
| 243 |
+
/// 0x733C-0x733F: Unknown
|
| 244 |
+
pub _unknown_733c: [u8; 4],
|
| 245 |
+
/// 0x7340: Landscape dimension param (passed to PCLandscape vtable slot 6).
|
| 246 |
+
pub _field_7340: u32,
|
| 247 |
+
/// 0x7344-0x734B: Unknown
|
| 248 |
+
pub _unknown_7344: [u8; 8],
|
| 249 |
+
/// 0x734C: Landscape dimension param (passed to PCLandscape vtable slot 6).
|
| 250 |
+
pub _field_734c: u32,
|
| 251 |
+
/// 0x7350-0x7373: Unknown
|
| 252 |
+
pub _unknown_7350: [u8; 36],
|
| 253 |
+
/// 0x7374: Unknown (zeroed by InitTurnState).
|
| 254 |
+
pub _field_7374: u32,
|
| 255 |
+
/// 0x7378: Unknown (zeroed by InitTurnState).
|
| 256 |
+
pub _field_7378: u32,
|
| 257 |
+
/// 0x737C: Unknown (zeroed by InitTurnState).
|
| 258 |
+
pub _field_737c: u32,
|
| 259 |
|
| 260 |
/// 0x7380: Viewport width (Fixed-point, e.g. 960.0 = 0x03C00000).
|
| 261 |
pub viewport_width: i32,
|
|
|
|
| 296 |
pub level_width_raw: u32,
|
| 297 |
/// 0x7780: Level height output (written by PCLandscape constructor param 11).
|
| 298 |
pub level_height_raw: u32,
|
| 299 |
+
/// 0x7784: Unknown (zeroed by InitTurnState).
|
| 300 |
+
pub _field_7784: u32,
|
| 301 |
+
/// 0x7788: Unknown (set from GameInfo+0xF362 during InitTurnState).
|
| 302 |
+
pub _field_7788: u32,
|
| 303 |
+
/// 0x778C: Unknown (set to Fixed 1.0 = 0x10000 by InitTurnState).
|
| 304 |
+
pub _field_778c: u32,
|
| 305 |
+
/// 0x7790: Unknown (zeroed by InitTurnState).
|
| 306 |
+
pub _field_7790: u32,
|
| 307 |
+
/// 0x7794-0x779B: Unknown
|
| 308 |
+
pub _unknown_7794: [u8; 8],
|
| 309 |
|
| 310 |
/// 0x779C: Level bound min X (Fixed16.16, negative = off-screen left).
|
| 311 |
pub level_bound_min_x: Fixed,
|
|
|
|
| 330 |
/// 0x77C8: Total pixels (width × height).
|
| 331 |
pub level_total_pixels: u32,
|
| 332 |
|
| 333 |
+
/// 0x77CC-0x77D3: Unknown
|
| 334 |
+
pub _unknown_77cc: [u8; 8],
|
| 335 |
+
/// 0x77D4: Unknown (zeroed by InitTurnState).
|
| 336 |
+
pub _field_77d4: u32,
|
| 337 |
+
/// 0x77D8: Unknown (zeroed by InitTurnState).
|
| 338 |
+
pub _field_77d8: u32,
|
| 339 |
+
/// 0x77DC: Unknown (zeroed by InitTurnState).
|
| 340 |
+
pub _field_77dc: u32,
|
| 341 |
+
/// 0x77E0: Unknown (zeroed by InitTurnState).
|
| 342 |
+
pub _field_77e0: u32,
|
| 343 |
|
| 344 |
/// 0x77E4: Speech slot table. Maps (team, speech_line_id) → DSSound buffer index.
|
| 345 |
/// Cleared by DSSound_LoadAllSpeechBanks (0x571A70), filled by DDGameWrapper__LoadSpeechWAV (0x571530).
|
| 346 |
pub speech_slot_table: SpeechSlotTable,
|
| 347 |
|
| 348 |
+
/// 0x7D84: Unknown (zeroed by InitTurnState).
|
| 349 |
+
pub _field_7d84: u32,
|
| 350 |
+
/// 0x7D88: Per-team u32 flags (13 entries, zeroed per team by InitTurnState).
|
| 351 |
+
pub _field_7d88: [u32; 13],
|
| 352 |
+
/// 0x7DBC: Per-team byte flags array 1 (13 entries, set to 1 by InitTurnState).
|
| 353 |
+
pub _field_7dbc: [u8; 13],
|
| 354 |
+
/// 0x7DC9: Per-team byte flags array 2 (13 entries, set to 1 by InitTurnState).
|
| 355 |
+
pub _field_7dc9: [u8; 13],
|
| 356 |
+
/// 0x7DD6: Per-team byte flags array 3 (13 entries, zeroed by InitTurnState).
|
| 357 |
+
pub _field_7dd6: [u8; 13],
|
| 358 |
+
/// 0x7DE3: Per-team byte flags array 4 (13 entries, zeroed by InitTurnState).
|
| 359 |
+
pub _field_7de3: [u8; 13],
|
| 360 |
+
/// 0x7DF0: Per-team byte flags array 5 (13 entries, zeroed by InitTurnState).
|
| 361 |
+
pub _field_7df0: [u8; 13],
|
| 362 |
+
/// 0x7DFD-0x7E02: Unknown
|
| 363 |
+
pub _unknown_7dfd: [u8; 6],
|
| 364 |
+
/// 0x7E03: Unknown byte (zeroed by InitTurnState).
|
| 365 |
+
pub _field_7e03: u8,
|
| 366 |
+
/// 0x7E04: Unknown byte (zeroed by InitTurnState).
|
| 367 |
+
pub _field_7e04: u8,
|
| 368 |
+
/// 0x7E05-0x7E24: Unknown
|
| 369 |
+
pub _unknown_7e05: [u8; 32],
|
| 370 |
/// 0x7E25: SuperSheep/AquaSheep restriction active flag (byte).
|
| 371 |
/// When nonzero, the SuperSheep/AquaSheep weapon check is applied.
|
| 372 |
pub supersheep_restricted: u8,
|
|
|
|
| 384 |
/// 0x7E40: Game logic version byte 4 (set by InitVersionFlags).
|
| 385 |
/// Used in FireWeapon__MailMineMole to gate vtable call behavior by version.
|
| 386 |
pub version_flag_4: u8,
|
| 387 |
+
/// 0x7E41: Unknown byte (zeroed by InitTurnState).
|
| 388 |
+
pub _field_7e41: u8,
|
| 389 |
+
/// 0x7E42-0x7E4B: Unknown
|
| 390 |
+
pub _unknown_7e42: [u8; 10],
|
| 391 |
+
/// 0x7E4C: Unknown (zeroed by InitTurnState).
|
| 392 |
+
pub _field_7e4c: u32,
|
| 393 |
+
/// 0x7E50-0x7E6F: Unknown fields (all zeroed by InitTurnState).
|
| 394 |
+
pub _fields_7e50: [u32; 8],
|
| 395 |
+
/// 0x7E70: Per-team scoring flags (6 entries, written by InitAllianceData).
|
| 396 |
+
pub team_scoring_flags: [u32; 6],
|
| 397 |
+
/// 0x7E88-0x7E9B: Unknown fields (all zeroed by InitTurnState).
|
| 398 |
+
pub _fields_7e88: [u32; 5],
|
| 399 |
+
/// 0x7E9C-0x7E9F: Unknown
|
| 400 |
+
pub _unknown_7e9c: [u8; 4],
|
| 401 |
|
| 402 |
/// 0x7EA0: Flag/counter (value 4 at runtime — likely team count).
|
| 403 |
pub field_7ea0: u32,
|
|
|
|
| 425 |
/// 0x8140: Number of entries currently in the sound queue (0–16).
|
| 426 |
pub sound_queue_count: i32,
|
| 427 |
|
| 428 |
+
/// 0x8144-0x8147: Unknown
|
| 429 |
+
pub _unknown_8144: [u8; 4],
|
| 430 |
+
/// 0x8148: Unknown (set to 1 by InitTurnState).
|
| 431 |
+
pub _field_8148: u32,
|
| 432 |
+
/// 0x814C-0x814F: Unknown
|
| 433 |
+
pub _unknown_814c: [u8; 4],
|
| 434 |
/// 0x8150: Parallax/camera scale factor (Fixed-point multiplier).
|
| 435 |
/// Used by DrawCrosshairLine (multiplied by 0x140000) and
|
| 436 |
/// CTaskCloud render (parallax X offset = wind_speed * this >> 16).
|
| 437 |
pub parallax_scale: i32,
|
| 438 |
|
| 439 |
+
/// 0x8154-0x8157: Unknown
|
| 440 |
+
pub _unknown_8154: [u8; 4],
|
| 441 |
+
/// 0x8158: Unknown (zeroed by InitTurnState).
|
| 442 |
+
pub _field_8158: u32,
|
| 443 |
+
/// 0x815C: Unknown (zeroed by InitTurnState).
|
| 444 |
+
pub _field_815c: u32,
|
| 445 |
+
/// 0x8160: Unknown (zeroed by InitTurnState).
|
| 446 |
+
pub _field_8160: u32,
|
| 447 |
+
/// 0x8164: Unknown (zeroed by InitTurnState).
|
| 448 |
+
pub _field_8164: u32,
|
| 449 |
+
/// 0x8168-0x818B: Unknown
|
| 450 |
+
pub _unknown_8168: [u8; 36],
|
| 451 |
|
| 452 |
/// 0x818C: Turn status text buffer (null-terminated ASCII).
|
| 453 |
/// Shows on screen during gameplay, e.g. "Cheesy harkitseee siirtoaan"
|
crates/openwa-core/src/engine/game_info.rs
CHANGED
|
@@ -19,16 +19,24 @@ pub struct GameInfo {
|
|
| 19 |
/// Used by DSSound_LoadAllSpeechBanks to iterate teams.
|
| 20 |
pub speech_team_count: u8,
|
| 21 |
|
| 22 |
-
/// 0x044D-
|
| 23 |
-
pub _unknown_044d: [u8;
|
|
|
|
|
|
|
| 24 |
|
| 25 |
/// 0xD778: Game version/mode. Compared against various thresholds:
|
| 26 |
/// -2 = network game, -3..0 = different game modes, 8+ = new versions.
|
| 27 |
/// Used by DDGame constructor for conditional initialization.
|
| 28 |
pub game_version: i32,
|
| 29 |
|
| 30 |
-
/// 0xD77C-
|
| 31 |
-
pub _unknown_d77c: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
/// 0xD932: DoubleTurnTime availability threshold (u16).
|
| 33 |
/// If game_version > 0xD1 and this > 0x7FFF, DoubleTurnTime is disabled.
|
| 34 |
pub double_turn_time_threshold: u16,
|
|
@@ -47,8 +55,10 @@ pub struct GameInfo {
|
|
| 47 |
/// 0xD946: Network config byte 2 (copied to network object+0x29).
|
| 48 |
pub net_config_2: u8,
|
| 49 |
|
| 50 |
-
/// 0xD947-
|
| 51 |
-
pub _unknown_d947: [u8;
|
|
|
|
|
|
|
| 52 |
/// 0xD94C: Donkey (weapon 0x36) disable flag.
|
| 53 |
pub donkey_disabled: u8,
|
| 54 |
/// 0xD94D-0xD955: Unknown
|
|
@@ -69,8 +79,14 @@ pub struct GameInfo {
|
|
| 69 |
/// 0xD9A2: Network weapon exception flag. When net_config_2 != 0,
|
| 70 |
/// weapons 10/0x37/0x38 are only disabled if this is also 0.
|
| 71 |
pub net_weapon_exception: u8,
|
| 72 |
-
/// 0xD9A3-
|
| 73 |
-
pub _unknown_d9a3: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
/// 0xD9E0: Streaming audio config data (path config passed to streaming audio ctor).
|
| 76 |
/// Address of this field is passed as a pointer parameter.
|
|
@@ -138,8 +154,12 @@ pub struct GameInfo {
|
|
| 138 |
/// Checked by IsSoundSuppressed, DispatchGlobalSound, PlaySoundPooled_Direct.
|
| 139 |
pub sound_mute: u8,
|
| 140 |
|
| 141 |
-
/// 0xF349-
|
| 142 |
-
pub _unknown_f349: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
/// 0xF374: Display flags passed to DDDisplay::Init.
|
| 145 |
pub display_flags: u32,
|
|
|
|
| 19 |
/// Used by DSSound_LoadAllSpeechBanks to iterate teams.
|
| 20 |
pub speech_team_count: u8,
|
| 21 |
|
| 22 |
+
/// 0x044D-0xD773: Unknown
|
| 23 |
+
pub _unknown_044d: [u8; 0xD774 - 0x44D],
|
| 24 |
+
/// 0xD774: Initial RNG seed from scheme options.
|
| 25 |
+
pub rng_seed: u32,
|
| 26 |
|
| 27 |
/// 0xD778: Game version/mode. Compared against various thresholds:
|
| 28 |
/// -2 = network game, -3..0 = different game modes, 8+ = new versions.
|
| 29 |
/// Used by DDGame constructor for conditional initialization.
|
| 30 |
pub game_version: i32,
|
| 31 |
|
| 32 |
+
/// 0xD77C-0xD787: Unknown
|
| 33 |
+
pub _unknown_d77c: [u8; 0xD788 - 0xD77C],
|
| 34 |
+
/// 0xD788: Scoring parameter A (multiplied by 50 for initial score).
|
| 35 |
+
pub scoring_param_a: u16,
|
| 36 |
+
/// 0xD78A: Scoring parameter B (multiplied by 50 for initial score).
|
| 37 |
+
pub scoring_param_b: u16,
|
| 38 |
+
/// 0xD78C-0xD931: Unknown
|
| 39 |
+
pub _unknown_d78c: [u8; 0xD932 - 0xD78C],
|
| 40 |
/// 0xD932: DoubleTurnTime availability threshold (u16).
|
| 41 |
/// If game_version > 0xD1 and this > 0x7FFF, DoubleTurnTime is disabled.
|
| 42 |
pub double_turn_time_threshold: u16,
|
|
|
|
| 55 |
/// 0xD946: Network config byte 2 (copied to network object+0x29).
|
| 56 |
pub net_config_2: u8,
|
| 57 |
|
| 58 |
+
/// 0xD947-0xD94A: Unknown
|
| 59 |
+
pub _unknown_d947: [u8; 4],
|
| 60 |
+
/// 0xD94B: Landscape scheme flag (nonzero enables terrain features via PCLandscape vtable slot 6).
|
| 61 |
+
pub landscape_scheme_flag: u8,
|
| 62 |
/// 0xD94C: Donkey (weapon 0x36) disable flag.
|
| 63 |
pub donkey_disabled: u8,
|
| 64 |
/// 0xD94D-0xD955: Unknown
|
|
|
|
| 79 |
/// 0xD9A2: Network weapon exception flag. When net_config_2 != 0,
|
| 80 |
/// weapons 10/0x37/0x38 are only disabled if this is also 0.
|
| 81 |
pub net_weapon_exception: u8,
|
| 82 |
+
/// 0xD9A3-0xD9DB: Unknown
|
| 83 |
+
pub _unknown_d9a3: [u8; 0xD9DC - 0xD9A3],
|
| 84 |
+
/// 0xD9DC: Index of the starting team for this round.
|
| 85 |
+
pub starting_team_index: i8,
|
| 86 |
+
/// 0xD9DD: Game mode flag. Negative = training/replay mode. Also used as starting-team index for activity flags in normal mode.
|
| 87 |
+
pub game_mode_flag: i8,
|
| 88 |
+
/// 0xD9DE-0xD9DF: Unknown
|
| 89 |
+
pub _unknown_d9de: [u8; 2],
|
| 90 |
|
| 91 |
/// 0xD9E0: Streaming audio config data (path config passed to streaming audio ctor).
|
| 92 |
/// Address of this field is passed as a pointer parameter.
|
|
|
|
| 154 |
/// Checked by IsSoundSuppressed, DispatchGlobalSound, PlaySoundPooled_Direct.
|
| 155 |
pub sound_mute: u8,
|
| 156 |
|
| 157 |
+
/// 0xF349-0xF361: Unknown
|
| 158 |
+
pub _unknown_f349: [u8; 0xF362 - 0xF349],
|
| 159 |
+
/// 0xF362: Unknown byte copied to DDGame+0x7788 during turn state init.
|
| 160 |
+
pub _field_f362: u8,
|
| 161 |
+
/// 0xF363-0xF373: Unknown
|
| 162 |
+
pub _unknown_f363: [u8; 0xF374 - 0xF363],
|
| 163 |
|
| 164 |
/// 0xF374: Display flags passed to DDDisplay::Init.
|
| 165 |
pub display_flags: u32,
|
crates/openwa-core/src/engine/game_state_init.rs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
| 4 |
//! InitGameState itself is Rust or the original WA code.
|
| 5 |
|
| 6 |
use crate::engine::ddgame::DDGame;
|
|
|
|
| 7 |
use crate::wa_alloc::wa_malloc;
|
| 8 |
|
| 9 |
/// Pure Rust implementation of SpriteGfxTable__Init (0x541620).
|
|
@@ -62,15 +63,15 @@ const TEAM_ENTRY_STRIDE: usize = 3000;
|
|
| 62 |
///
|
| 63 |
/// Chain: wrapper+0x488 → DDGame, DDGame+0x24 → GameInfo.
|
| 64 |
#[inline]
|
| 65 |
-
unsafe fn game_info_from_wrapper(wrapper: *mut u8) -> *mut
|
| 66 |
-
let ddgame = *(wrapper.add(0x488) as *const *mut
|
| 67 |
-
|
| 68 |
}
|
| 69 |
|
| 70 |
/// Helper: read the DDGame pointer from a DDGameWrapper pointer.
|
| 71 |
#[inline]
|
| 72 |
-
unsafe fn ddgame_from_wrapper(wrapper: *mut u8) -> *mut
|
| 73 |
-
*(wrapper.add(0x488) as *const *mut
|
| 74 |
}
|
| 75 |
|
| 76 |
/// Pure Rust implementation of CGameTask_InitTeamScoring_Maybe (0x528510).
|
|
@@ -97,10 +98,8 @@ unsafe fn ddgame_from_wrapper(wrapper: *mut u8) -> *mut u8 {
|
|
| 97 |
pub unsafe fn init_team_scoring(wrapper: *mut u8) {
|
| 98 |
let game_info = game_info_from_wrapper(wrapper);
|
| 99 |
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
let scoring_param_a = *(game_info.add(0xD788) as *const u16) as u32;
|
| 103 |
-
let scoring_param_b = *(game_info.add(0xD78A) as *const u16) as u32;
|
| 104 |
let value_a = scoring_param_a * 50; // uVar1 * 0x32
|
| 105 |
let value_b = scoring_param_b * 50; // iVar4
|
| 106 |
|
|
@@ -119,15 +118,14 @@ pub unsafe fn init_team_scoring(wrapper: *mut u8) {
|
|
| 119 |
}
|
| 120 |
|
| 121 |
// Mark the starting team in array_1: wrapper+0x128[starting_team] = 1
|
| 122 |
-
|
| 123 |
-
let starting_team = *(game_info.add(0xD9DC) as *const i8) as i32;
|
| 124 |
*(base.add(starting_team as usize)) = 1;
|
| 125 |
|
| 126 |
// GameInfo+0xD9DD: mode flag (i8, negative = training/replay mode)
|
| 127 |
-
let mode_flag =
|
| 128 |
|
| 129 |
// GameInfo+0x0000: first byte = total team count (num_teams, u8)
|
| 130 |
-
let num_teams = *game_info as
|
| 131 |
|
| 132 |
// Team activity flags at wrapper+0x22C (one u32 per team)
|
| 133 |
let flags_base = wrapper.add(0x22C) as *mut u32;
|
|
@@ -143,12 +141,13 @@ pub unsafe fn init_team_scoring(wrapper: *mut u8) {
|
|
| 143 |
// For each active team slot in GameInfo, clear the flag to 0
|
| 144 |
// GameInfo+0x44C: team count for alliance data (u8)
|
| 145 |
let game_info2 = game_info_from_wrapper(wrapper);
|
| 146 |
-
let alliance_team_count = *game_info2
|
| 147 |
if alliance_team_count > 0 {
|
| 148 |
let mut offset: usize = 0;
|
| 149 |
for _ in 0..alliance_team_count {
|
| 150 |
// GameInfo+0x450 + team_index*3000: alliance group (i8)
|
| 151 |
-
let alliance_group =
|
|
|
|
| 152 |
if alliance_group >= 0 {
|
| 153 |
*flags_base.add(alliance_group as usize) = 0;
|
| 154 |
}
|
|
@@ -167,14 +166,14 @@ pub unsafe fn init_team_scoring(wrapper: *mut u8) {
|
|
| 167 |
}
|
| 168 |
|
| 169 |
// Set starting team's flag to 1
|
| 170 |
-
// GameInfo+0xD9DD: starting team index for flags (i8, reuse
|
| 171 |
-
let starting_flag_team =
|
| 172 |
*flags_base.add(starting_flag_team as usize) = 1;
|
| 173 |
}
|
| 174 |
|
| 175 |
// Zero CTask sub-object fields for each team's task pointer
|
| 176 |
// wrapper+0x54[i] = CTask pointer, clear offsets +0x08..+0x18 (5 DWORDs)
|
| 177 |
-
let num_teams2 = *game_info_from_wrapper(wrapper) as
|
| 178 |
if num_teams2 > 0 {
|
| 179 |
let task_ptrs = wrapper.add(0x54) as *mut *mut u8;
|
| 180 |
for i in 0..num_teams2 as usize {
|
|
@@ -199,16 +198,16 @@ pub unsafe fn init_team_scoring(wrapper: *mut u8) {
|
|
| 199 |
///
|
| 200 |
/// # Layout:
|
| 201 |
/// - wrapper+0x350..+0x384: 13 × u32 alliance bitmasks (one per alliance group)
|
| 202 |
-
/// - DDGame
|
| 203 |
/// - wrapper+0x3AC..: auxiliary array of non-starting-team alliance IDs (+0x10)
|
| 204 |
///
|
| 205 |
/// # GameInfo fields used:
|
| 206 |
-
/// -
|
| 207 |
-
/// -
|
| 208 |
/// - +0x450 + i*3000: alliance_group (i8) for team i
|
| 209 |
/// - +0x451 + i*3000: alliance_id (u8) for team i
|
| 210 |
-
/// -
|
| 211 |
-
/// -
|
| 212 |
///
|
| 213 |
/// - wrapper+0x490: first byte used as scoring override value
|
| 214 |
pub unsafe fn init_alliance_data(wrapper: *mut u8) {
|
|
@@ -220,19 +219,19 @@ pub unsafe fn init_alliance_data(wrapper: *mut u8) {
|
|
| 220 |
// Build alliance bitmasks: for each team, set bit (alliance_id & 0x1F)
|
| 221 |
// in bitmask[alliance_group]
|
| 222 |
let game_info = game_info_from_wrapper(wrapper);
|
| 223 |
-
let num_teams = *game_info as
|
| 224 |
|
| 225 |
// Global DAT_007a087c+4 = 0 (static variable, offset 0x7A0880)
|
| 226 |
// We skip this — it's a global we don't own. The original sets it but
|
| 227 |
// it appears unused in the init path.
|
| 228 |
|
| 229 |
-
let alliance_team_count = *game_info
|
| 230 |
if num_teams != 0 && alliance_team_count > 0 {
|
| 231 |
let mut offset: usize = 0;
|
| 232 |
for _ in 0..alliance_team_count {
|
| 233 |
let gi = game_info_from_wrapper(wrapper);
|
| 234 |
-
let alliance_group = *(gi.add(0x450 + offset) as *const i8) as i32;
|
| 235 |
-
let alliance_id = *(gi.add(0x451 + offset)
|
| 236 |
|
| 237 |
if alliance_group >= 0 {
|
| 238 |
let bitmask = (wrapper.add(0x350) as *mut u32).add(alliance_group as usize);
|
|
@@ -243,15 +242,13 @@ pub unsafe fn init_alliance_data(wrapper: *mut u8) {
|
|
| 243 |
}
|
| 244 |
}
|
| 245 |
|
| 246 |
-
// Zero 6 per-team scoring flags
|
| 247 |
let ddgame = ddgame_from_wrapper(wrapper);
|
| 248 |
-
|
| 249 |
-
*(ddgame.add(0x7E70) as *mut u32).add(i as usize) = 0;
|
| 250 |
-
}
|
| 251 |
|
| 252 |
// Build scoring flags and auxiliary alliance array
|
| 253 |
let game_info = game_info_from_wrapper(wrapper);
|
| 254 |
-
let alliance_team_count = *game_info
|
| 255 |
|
| 256 |
// Local tracking: which alliance IDs we've already added to auxiliary array
|
| 257 |
let mut seen: [u8; 6] = [0; 6]; // local_8[0..6], indexed by alliance_id
|
|
@@ -262,11 +259,12 @@ pub unsafe fn init_alliance_data(wrapper: *mut u8) {
|
|
| 262 |
|
| 263 |
for team_idx in 0..alliance_team_count {
|
| 264 |
let gi = game_info_from_wrapper(wrapper);
|
| 265 |
-
let alliance_group = *(gi.add(0x450 + team_offset) as *const i8) as i32;
|
| 266 |
-
let alliance_id = *(gi.add(0x451 + team_offset)
|
| 267 |
|
| 268 |
let ddgame = ddgame_from_wrapper(wrapper);
|
| 269 |
-
let scoring_flag =
|
|
|
|
| 270 |
|
| 271 |
if alliance_group < 0 {
|
| 272 |
// No alliance: flag = value from wrapper+0x490 (i8 sign-extended)
|
|
@@ -275,15 +273,15 @@ pub unsafe fn init_alliance_data(wrapper: *mut u8) {
|
|
| 275 |
} else {
|
| 276 |
// Check if this team is allied with the starting team
|
| 277 |
let gi = game_info_from_wrapper(wrapper);
|
| 278 |
-
let num_teams_byte = *gi
|
| 279 |
let override_byte = *(wrapper.add(0x490) as *const u8);
|
| 280 |
|
| 281 |
if num_teams_byte == 0 || override_byte != 0 {
|
| 282 |
// No teams or override set: flag = 1
|
| 283 |
*scoring_flag = 1;
|
| 284 |
} else {
|
| 285 |
-
let game_version =
|
| 286 |
-
let starting_team =
|
| 287 |
|
| 288 |
let team_bitmask: u32 = if game_version < 0x83 {
|
| 289 |
// Old version: use full alliance bitmask
|
|
@@ -305,7 +303,7 @@ pub unsafe fn init_alliance_data(wrapper: *mut u8) {
|
|
| 305 |
|
| 306 |
// Build auxiliary array for non-starting-team alliances
|
| 307 |
let gi2 = game_info_from_wrapper(wrapper);
|
| 308 |
-
let starting_team2 =
|
| 309 |
|
| 310 |
if alliance_group != starting_team2 && seen[alliance_id as usize] == 0 {
|
| 311 |
let starting_bitmask2 =
|
|
@@ -434,7 +432,7 @@ unsafe fn wa_init_feature_flags(wrapper: *mut u8) {
|
|
| 434 |
/// object.
|
| 435 |
pub unsafe fn init_turn_state(wrapper: *mut u8) {
|
| 436 |
let ddgame = ddgame_from_wrapper(wrapper);
|
| 437 |
-
let game_info =
|
| 438 |
|
| 439 |
// wrapper+0x458 = -1, wrapper+0x450 = 0, wrapper+0x454 = 0
|
| 440 |
*(wrapper.add(0x458) as *mut u32) = 0xFFFF_FFFF;
|
|
@@ -442,116 +440,103 @@ pub unsafe fn init_turn_state(wrapper: *mut u8) {
|
|
| 442 |
*(wrapper.add(0x454) as *mut u32) = 0;
|
| 443 |
|
| 444 |
// DDGame+0x72E0/E4 = -1, DDGame+0x72E8 = 0
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
|
| 449 |
-
// Zero
|
| 450 |
{
|
|
|
|
| 451 |
let mut off = 0u32;
|
| 452 |
while off < 0x118 {
|
| 453 |
-
*(
|
| 454 |
off += 0x14;
|
| 455 |
}
|
| 456 |
}
|
| 457 |
|
| 458 |
// More DDGame field zeroing
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
// DDGame
|
| 467 |
-
let rng_seed =
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
// DDGame
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
|
| 483 |
// Camera center: (level_width << 16) / 2, (level_height << 16) / 2
|
| 484 |
-
let level_width =
|
| 485 |
-
let level_height =
|
| 486 |
let cx = (level_width << 16) / 2;
|
| 487 |
let cy = (level_height << 16) / 2;
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
|
| 498 |
// Per-team loop
|
| 499 |
-
let num_teams = *game_info as
|
| 500 |
if num_teams > 0 {
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
*(ddgame.add(0x7DF0 + i as usize) as *mut u8) = 0;
|
| 509 |
-
off += 4;
|
| 510 |
}
|
| 511 |
}
|
| 512 |
|
| 513 |
-
|
| 514 |
-
|
| 515 |
|
| 516 |
// Call DDGame__InitFeatureFlags (600-line feature flag init, bridged)
|
| 517 |
wa_init_feature_flags(wrapper);
|
| 518 |
|
| 519 |
// Post-feature-flag field writes
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
*(ddgame.add(0x7E68) as *mut u32) = 0;
|
| 528 |
-
*(ddgame.add(0x7E6C) as *mut u32) = 0;
|
| 529 |
-
*(ddgame.add(0x7E88) as *mut u32) = 0;
|
| 530 |
-
*(ddgame.add(0x7E8C) as *mut u32) = 0;
|
| 531 |
-
*(ddgame.add(0x7E90) as *mut u32) = 0;
|
| 532 |
-
*(ddgame.add(0x7E94) as *mut u32) = 0;
|
| 533 |
-
*(ddgame.add(0x7E98) as *mut u32) = 0;
|
| 534 |
-
*(ddgame.add(0x7EA0) as *mut u32) = 0;
|
| 535 |
-
*(ddgame.add(0x7EA4) as *mut u32) = 0;
|
| 536 |
-
|
| 537 |
-
*(ddgame.add(0x8148) as *mut u32) = 1;
|
| 538 |
|
| 539 |
let ddgame2 = ddgame_from_wrapper(wrapper);
|
| 540 |
-
|
| 541 |
-
|
| 542 |
let ddgame3 = ddgame_from_wrapper(wrapper);
|
| 543 |
-
|
| 544 |
-
|
| 545 |
|
| 546 |
-
// Vtable dispatch: DDGame
|
| 547 |
-
// param = game_info
|
| 548 |
let ddgame4 = ddgame_from_wrapper(wrapper);
|
| 549 |
-
let landscape =
|
| 550 |
if !landscape.is_null() {
|
| 551 |
let vtable = *(landscape as *const *const usize);
|
| 552 |
let slot1: unsafe extern "thiscall" fn(*mut u8, u32) = core::mem::transmute(*vtable.add(1));
|
| 553 |
-
let
|
| 554 |
-
let param = *game_info2.add(0xD94C) as u32;
|
| 555 |
slot1(landscape, param);
|
| 556 |
}
|
| 557 |
}
|
|
@@ -560,22 +545,22 @@ pub unsafe fn init_turn_state(wrapper: *mut u8) {
|
|
| 560 |
///
|
| 561 |
/// Convention: usercall(EAX=wrapper), plain RET.
|
| 562 |
///
|
| 563 |
-
/// Checks game_info
|
| 564 |
-
/// appropriate parameters, then updates DDGame
|
| 565 |
pub unsafe fn init_landscape_flags(wrapper: *mut u8) {
|
| 566 |
let ddgame = ddgame_from_wrapper(wrapper);
|
| 567 |
-
let game_info =
|
| 568 |
|
| 569 |
-
let scheme_flag =
|
| 570 |
|
| 571 |
// Read the 4 DDGame fields used as params
|
| 572 |
-
let field_7318 =
|
| 573 |
-
let field_730c =
|
| 574 |
-
let field_734c =
|
| 575 |
-
let field_7340 =
|
| 576 |
|
| 577 |
-
// DDGame
|
| 578 |
-
let landscape =
|
| 579 |
let vtable = *(landscape as *const *const usize);
|
| 580 |
// Vtable slot 6 = offset +0x18
|
| 581 |
let slot6: unsafe extern "thiscall" fn(*mut u8, u32, u32, u32, u32, u32, u32, u32, u32) =
|
|
@@ -586,8 +571,8 @@ pub unsafe fn init_landscape_flags(wrapper: *mut u8) {
|
|
| 586 |
slot6(
|
| 587 |
landscape, 1, 1, 1, 1, field_7318, field_730c, field_734c, field_7340,
|
| 588 |
);
|
| 589 |
-
|
| 590 |
-
} else if
|
| 591 |
// Call with (0, 0, 1, 0, field_7318, field_730c, field_734c, field_7340)
|
| 592 |
slot6(
|
| 593 |
landscape, 0, 0, 1, 0, field_7318, field_730c, field_734c, field_7340,
|
|
|
|
| 4 |
//! InitGameState itself is Rust or the original WA code.
|
| 5 |
|
| 6 |
use crate::engine::ddgame::DDGame;
|
| 7 |
+
use crate::engine::game_info::GameInfo;
|
| 8 |
use crate::wa_alloc::wa_malloc;
|
| 9 |
|
| 10 |
/// Pure Rust implementation of SpriteGfxTable__Init (0x541620).
|
|
|
|
| 63 |
///
|
| 64 |
/// Chain: wrapper+0x488 → DDGame, DDGame+0x24 → GameInfo.
|
| 65 |
#[inline]
|
| 66 |
+
unsafe fn game_info_from_wrapper(wrapper: *mut u8) -> *mut GameInfo {
|
| 67 |
+
let ddgame = *(wrapper.add(0x488) as *const *mut DDGame);
|
| 68 |
+
(*ddgame).game_info
|
| 69 |
}
|
| 70 |
|
| 71 |
/// Helper: read the DDGame pointer from a DDGameWrapper pointer.
|
| 72 |
#[inline]
|
| 73 |
+
unsafe fn ddgame_from_wrapper(wrapper: *mut u8) -> *mut DDGame {
|
| 74 |
+
*(wrapper.add(0x488) as *const *mut DDGame)
|
| 75 |
}
|
| 76 |
|
| 77 |
/// Pure Rust implementation of CGameTask_InitTeamScoring_Maybe (0x528510).
|
|
|
|
| 98 |
pub unsafe fn init_team_scoring(wrapper: *mut u8) {
|
| 99 |
let game_info = game_info_from_wrapper(wrapper);
|
| 100 |
|
| 101 |
+
let scoring_param_a = (*game_info).scoring_param_a as u32;
|
| 102 |
+
let scoring_param_b = (*game_info).scoring_param_b as u32;
|
|
|
|
|
|
|
| 103 |
let value_a = scoring_param_a * 50; // uVar1 * 0x32
|
| 104 |
let value_b = scoring_param_b * 50; // iVar4
|
| 105 |
|
|
|
|
| 118 |
}
|
| 119 |
|
| 120 |
// Mark the starting team in array_1: wrapper+0x128[starting_team] = 1
|
| 121 |
+
let starting_team = (*game_info).starting_team_index as i32;
|
|
|
|
| 122 |
*(base.add(starting_team as usize)) = 1;
|
| 123 |
|
| 124 |
// GameInfo+0xD9DD: mode flag (i8, negative = training/replay mode)
|
| 125 |
+
let mode_flag = (*game_info).game_mode_flag;
|
| 126 |
|
| 127 |
// GameInfo+0x0000: first byte = total team count (num_teams, u8)
|
| 128 |
+
let num_teams = (*game_info).num_teams as i32;
|
| 129 |
|
| 130 |
// Team activity flags at wrapper+0x22C (one u32 per team)
|
| 131 |
let flags_base = wrapper.add(0x22C) as *mut u32;
|
|
|
|
| 141 |
// For each active team slot in GameInfo, clear the flag to 0
|
| 142 |
// GameInfo+0x44C: team count for alliance data (u8)
|
| 143 |
let game_info2 = game_info_from_wrapper(wrapper);
|
| 144 |
+
let alliance_team_count = (*game_info2).speech_team_count as i32;
|
| 145 |
if alliance_team_count > 0 {
|
| 146 |
let mut offset: usize = 0;
|
| 147 |
for _ in 0..alliance_team_count {
|
| 148 |
// GameInfo+0x450 + team_index*3000: alliance group (i8)
|
| 149 |
+
let alliance_group =
|
| 150 |
+
*((game_info2 as *const u8).add(0x450 + offset) as *const i8) as i32;
|
| 151 |
if alliance_group >= 0 {
|
| 152 |
*flags_base.add(alliance_group as usize) = 0;
|
| 153 |
}
|
|
|
|
| 166 |
}
|
| 167 |
|
| 168 |
// Set starting team's flag to 1
|
| 169 |
+
// GameInfo+0xD9DD: starting team index for flags (i8, reuse game_mode_flag which is the same field)
|
| 170 |
+
let starting_flag_team = (*game_info_from_wrapper(wrapper)).game_mode_flag as i32;
|
| 171 |
*flags_base.add(starting_flag_team as usize) = 1;
|
| 172 |
}
|
| 173 |
|
| 174 |
// Zero CTask sub-object fields for each team's task pointer
|
| 175 |
// wrapper+0x54[i] = CTask pointer, clear offsets +0x08..+0x18 (5 DWORDs)
|
| 176 |
+
let num_teams2 = (*game_info_from_wrapper(wrapper)).num_teams as i32;
|
| 177 |
if num_teams2 > 0 {
|
| 178 |
let task_ptrs = wrapper.add(0x54) as *mut *mut u8;
|
| 179 |
for i in 0..num_teams2 as usize {
|
|
|
|
| 198 |
///
|
| 199 |
/// # Layout:
|
| 200 |
/// - wrapper+0x350..+0x384: 13 × u32 alliance bitmasks (one per alliance group)
|
| 201 |
+
/// - DDGame.team_scoring_flags: 6 × u32 per-team scoring flags
|
| 202 |
/// - wrapper+0x3AC..: auxiliary array of non-starting-team alliance IDs (+0x10)
|
| 203 |
///
|
| 204 |
/// # GameInfo fields used:
|
| 205 |
+
/// - num_teams
|
| 206 |
+
/// - speech_team_count (alliance_team_count)
|
| 207 |
/// - +0x450 + i*3000: alliance_group (i8) for team i
|
| 208 |
/// - +0x451 + i*3000: alliance_id (u8) for team i
|
| 209 |
+
/// - game_version
|
| 210 |
+
/// - starting_team_index
|
| 211 |
///
|
| 212 |
/// - wrapper+0x490: first byte used as scoring override value
|
| 213 |
pub unsafe fn init_alliance_data(wrapper: *mut u8) {
|
|
|
|
| 219 |
// Build alliance bitmasks: for each team, set bit (alliance_id & 0x1F)
|
| 220 |
// in bitmask[alliance_group]
|
| 221 |
let game_info = game_info_from_wrapper(wrapper);
|
| 222 |
+
let num_teams = (*game_info).num_teams as i32;
|
| 223 |
|
| 224 |
// Global DAT_007a087c+4 = 0 (static variable, offset 0x7A0880)
|
| 225 |
// We skip this — it's a global we don't own. The original sets it but
|
| 226 |
// it appears unused in the init path.
|
| 227 |
|
| 228 |
+
let alliance_team_count = (*game_info).speech_team_count as i32;
|
| 229 |
if num_teams != 0 && alliance_team_count > 0 {
|
| 230 |
let mut offset: usize = 0;
|
| 231 |
for _ in 0..alliance_team_count {
|
| 232 |
let gi = game_info_from_wrapper(wrapper);
|
| 233 |
+
let alliance_group = *((gi as *const u8).add(0x450 + offset) as *const i8) as i32;
|
| 234 |
+
let alliance_id = *((gi as *const u8).add(0x451 + offset));
|
| 235 |
|
| 236 |
if alliance_group >= 0 {
|
| 237 |
let bitmask = (wrapper.add(0x350) as *mut u32).add(alliance_group as usize);
|
|
|
|
| 242 |
}
|
| 243 |
}
|
| 244 |
|
| 245 |
+
// Zero 6 per-team scoring flags (DDGame.team_scoring_flags)
|
| 246 |
let ddgame = ddgame_from_wrapper(wrapper);
|
| 247 |
+
(*ddgame).team_scoring_flags = [0u32; 6];
|
|
|
|
|
|
|
| 248 |
|
| 249 |
// Build scoring flags and auxiliary alliance array
|
| 250 |
let game_info = game_info_from_wrapper(wrapper);
|
| 251 |
+
let alliance_team_count = (*game_info).speech_team_count as i32;
|
| 252 |
|
| 253 |
// Local tracking: which alliance IDs we've already added to auxiliary array
|
| 254 |
let mut seen: [u8; 6] = [0; 6]; // local_8[0..6], indexed by alliance_id
|
|
|
|
| 259 |
|
| 260 |
for team_idx in 0..alliance_team_count {
|
| 261 |
let gi = game_info_from_wrapper(wrapper);
|
| 262 |
+
let alliance_group = *((gi as *const u8).add(0x450 + team_offset) as *const i8) as i32;
|
| 263 |
+
let alliance_id = *((gi as *const u8).add(0x451 + team_offset)) as u32;
|
| 264 |
|
| 265 |
let ddgame = ddgame_from_wrapper(wrapper);
|
| 266 |
+
let scoring_flag =
|
| 267 |
+
core::ptr::addr_of_mut!((*ddgame).team_scoring_flags[team_idx as usize]);
|
| 268 |
|
| 269 |
if alliance_group < 0 {
|
| 270 |
// No alliance: flag = value from wrapper+0x490 (i8 sign-extended)
|
|
|
|
| 273 |
} else {
|
| 274 |
// Check if this team is allied with the starting team
|
| 275 |
let gi = game_info_from_wrapper(wrapper);
|
| 276 |
+
let num_teams_byte = (*gi).num_teams;
|
| 277 |
let override_byte = *(wrapper.add(0x490) as *const u8);
|
| 278 |
|
| 279 |
if num_teams_byte == 0 || override_byte != 0 {
|
| 280 |
// No teams or override set: flag = 1
|
| 281 |
*scoring_flag = 1;
|
| 282 |
} else {
|
| 283 |
+
let game_version = (*gi).game_version;
|
| 284 |
+
let starting_team = (*gi).starting_team_index as i32;
|
| 285 |
|
| 286 |
let team_bitmask: u32 = if game_version < 0x83 {
|
| 287 |
// Old version: use full alliance bitmask
|
|
|
|
| 303 |
|
| 304 |
// Build auxiliary array for non-starting-team alliances
|
| 305 |
let gi2 = game_info_from_wrapper(wrapper);
|
| 306 |
+
let starting_team2 = (*gi2).starting_team_index as i32;
|
| 307 |
|
| 308 |
if alliance_group != starting_team2 && seen[alliance_id as usize] == 0 {
|
| 309 |
let starting_bitmask2 =
|
|
|
|
| 432 |
/// object.
|
| 433 |
pub unsafe fn init_turn_state(wrapper: *mut u8) {
|
| 434 |
let ddgame = ddgame_from_wrapper(wrapper);
|
| 435 |
+
let game_info = (*ddgame).game_info;
|
| 436 |
|
| 437 |
// wrapper+0x458 = -1, wrapper+0x450 = 0, wrapper+0x454 = 0
|
| 438 |
*(wrapper.add(0x458) as *mut u32) = 0xFFFF_FFFF;
|
|
|
|
| 440 |
*(wrapper.add(0x454) as *mut u32) = 0;
|
| 441 |
|
| 442 |
// DDGame+0x72E0/E4 = -1, DDGame+0x72E8 = 0
|
| 443 |
+
(*ddgame)._unknown_72e0 = 0xFFFF_FFFF;
|
| 444 |
+
(*ddgame).render_slot_count = 0xFFFF_FFFF;
|
| 445 |
+
(*ddgame)._unknown_72e8 = 0;
|
| 446 |
|
| 447 |
+
// Zero render entry table first u32 at DDGame+0x73B0, stride 0x14, while offset < 0x118
|
| 448 |
{
|
| 449 |
+
let base = core::ptr::addr_of_mut!((*ddgame).render_entries) as *mut u8;
|
| 450 |
let mut off = 0u32;
|
| 451 |
while off < 0x118 {
|
| 452 |
+
*(base.add(off as usize) as *mut u32) = 0;
|
| 453 |
off += 0x14;
|
| 454 |
}
|
| 455 |
}
|
| 456 |
|
| 457 |
// More DDGame field zeroing
|
| 458 |
+
(*ddgame).render_state_flag = 0;
|
| 459 |
+
(*ddgame)._field_72f4 = 0;
|
| 460 |
+
(*ddgame)._field_72f8 = 0;
|
| 461 |
+
(*ddgame)._field_72fc = 0;
|
| 462 |
+
(*ddgame)._field_7300 = 0;
|
| 463 |
+
(*ddgame)._field_7304 = 0;
|
| 464 |
+
|
| 465 |
+
// DDGame.rng_state_1/2 = game_info.rng_seed (RNG seed from scheme)
|
| 466 |
+
let rng_seed = (*game_info).rng_seed;
|
| 467 |
+
(*ddgame).rng_state_1 = rng_seed;
|
| 468 |
+
(*ddgame).rng_state_2 = rng_seed; // duplicate
|
| 469 |
+
|
| 470 |
+
(*ddgame)._field_7378 = 0;
|
| 471 |
+
(*ddgame)._field_7374 = 0;
|
| 472 |
+
(*ddgame)._field_737c = 0;
|
| 473 |
+
(*ddgame)._field_77dc = 0;
|
| 474 |
+
(*ddgame)._field_77e0 = 0;
|
| 475 |
+
(*ddgame)._field_7784 = 0;
|
| 476 |
+
|
| 477 |
+
// DDGame._field_7788 = game_info._field_f362 (byte → u32)
|
| 478 |
+
(*ddgame)._field_7788 = (*game_info)._field_f362 as u32;
|
| 479 |
+
(*ddgame)._field_778c = 0x10000; // Fixed-point 1.0
|
| 480 |
+
(*ddgame)._field_7790 = 0;
|
| 481 |
|
| 482 |
// Camera center: (level_width << 16) / 2, (level_height << 16) / 2
|
| 483 |
+
let level_width = (*ddgame).level_width as i32;
|
| 484 |
+
let level_height = (*ddgame).level_height as i32;
|
| 485 |
let cx = (level_width << 16) / 2;
|
| 486 |
let cy = (level_height << 16) / 2;
|
| 487 |
+
(*ddgame).viewport_width = cx;
|
| 488 |
+
(*ddgame).viewport_width_2 = cx; // duplicate
|
| 489 |
+
(*ddgame).viewport_height = cy;
|
| 490 |
+
(*ddgame).viewport_height_2 = cy; // duplicate
|
| 491 |
|
| 492 |
+
(*ddgame)._field_7d84 = 0;
|
| 493 |
+
(*ddgame)._field_7e4c = 0;
|
| 494 |
+
(*ddgame)._field_77d4 = 0;
|
| 495 |
+
(*ddgame)._field_77d8 = 0;
|
| 496 |
|
| 497 |
// Per-team loop
|
| 498 |
+
let num_teams = (*game_info).num_teams as i32;
|
| 499 |
if num_teams > 0 {
|
| 500 |
+
for i in 0..num_teams as usize {
|
| 501 |
+
(*ddgame)._field_7d88[i] = 0;
|
| 502 |
+
(*ddgame)._field_7dbc[i] = 1;
|
| 503 |
+
(*ddgame)._field_7dc9[i] = 1;
|
| 504 |
+
(*ddgame)._field_7dd6[i] = 0;
|
| 505 |
+
(*ddgame)._field_7de3[i] = 0;
|
| 506 |
+
(*ddgame)._field_7df0[i] = 0;
|
|
|
|
|
|
|
| 507 |
}
|
| 508 |
}
|
| 509 |
|
| 510 |
+
(*ddgame)._field_7e03 = 0;
|
| 511 |
+
(*ddgame)._field_7e04 = 0;
|
| 512 |
|
| 513 |
// Call DDGame__InitFeatureFlags (600-line feature flag init, bridged)
|
| 514 |
wa_init_feature_flags(wrapper);
|
| 515 |
|
| 516 |
// Post-feature-flag field writes
|
| 517 |
+
(*ddgame)._field_7e41 = 0;
|
| 518 |
+
(*ddgame)._fields_7e50 = [0u32; 8];
|
| 519 |
+
(*ddgame)._fields_7e88 = [0u32; 5];
|
| 520 |
+
(*ddgame).field_7ea0 = 0;
|
| 521 |
+
(*ddgame).field_7ea4 = 0;
|
| 522 |
+
|
| 523 |
+
(*ddgame)._field_8148 = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
|
| 525 |
let ddgame2 = ddgame_from_wrapper(wrapper);
|
| 526 |
+
(*ddgame2)._field_8158 = 0;
|
| 527 |
+
(*ddgame2)._field_815c = 0;
|
| 528 |
let ddgame3 = ddgame_from_wrapper(wrapper);
|
| 529 |
+
(*ddgame3)._field_8160 = 0;
|
| 530 |
+
(*ddgame3)._field_8164 = 0;
|
| 531 |
|
| 532 |
+
// Vtable dispatch: DDGame.landscape → vtable slot 1,
|
| 533 |
+
// param = game_info.donkey_disabled (byte at +0xD94C)
|
| 534 |
let ddgame4 = ddgame_from_wrapper(wrapper);
|
| 535 |
+
let landscape = (*ddgame4).landscape as *mut u8;
|
| 536 |
if !landscape.is_null() {
|
| 537 |
let vtable = *(landscape as *const *const usize);
|
| 538 |
let slot1: unsafe extern "thiscall" fn(*mut u8, u32) = core::mem::transmute(*vtable.add(1));
|
| 539 |
+
let param = (*(*ddgame4).game_info).donkey_disabled as u32;
|
|
|
|
| 540 |
slot1(landscape, param);
|
| 541 |
}
|
| 542 |
}
|
|
|
|
| 545 |
///
|
| 546 |
/// Convention: usercall(EAX=wrapper), plain RET.
|
| 547 |
///
|
| 548 |
+
/// Checks game_info.landscape_scheme_flag and dispatches landscape vtable slot 6 with
|
| 549 |
+
/// appropriate parameters, then updates DDGame.level_width_raw.
|
| 550 |
pub unsafe fn init_landscape_flags(wrapper: *mut u8) {
|
| 551 |
let ddgame = ddgame_from_wrapper(wrapper);
|
| 552 |
+
let game_info = (*ddgame).game_info;
|
| 553 |
|
| 554 |
+
let scheme_flag = (*game_info).landscape_scheme_flag;
|
| 555 |
|
| 556 |
// Read the 4 DDGame fields used as params
|
| 557 |
+
let field_7318 = (*ddgame).gfx_color_table[3];
|
| 558 |
+
let field_730c = (*ddgame).gfx_color_table[0];
|
| 559 |
+
let field_734c = (*ddgame)._field_734c;
|
| 560 |
+
let field_7340 = (*ddgame)._field_7340;
|
| 561 |
|
| 562 |
+
// DDGame.landscape object
|
| 563 |
+
let landscape = (*ddgame).landscape as *mut u8;
|
| 564 |
let vtable = *(landscape as *const *const usize);
|
| 565 |
// Vtable slot 6 = offset +0x18
|
| 566 |
let slot6: unsafe extern "thiscall" fn(*mut u8, u32, u32, u32, u32, u32, u32, u32, u32) =
|
|
|
|
| 571 |
slot6(
|
| 572 |
landscape, 1, 1, 1, 1, field_7318, field_730c, field_734c, field_7340,
|
| 573 |
);
|
| 574 |
+
(*ddgame).level_width_raw = 1;
|
| 575 |
+
} else if (*ddgame).level_width_raw != 0 {
|
| 576 |
// Call with (0, 0, 1, 0, field_7318, field_730c, field_734c, field_7340)
|
| 577 |
slot6(
|
| 578 |
landscape, 0, 0, 1, 0, field_7318, field_730c, field_734c, field_7340,
|