From f55a0d6d41c1edd28c1a7543820681a391d993ca Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Sun, 4 Aug 2024 14:00:18 +0200 Subject: [PATCH 01/10] Custom item support Required mains supported for Missiles/PB Unlimited Missiles/PBs are supported now Spring Ball has its own item now --- ppcasm/ppcasm_macro/src/lib.rs | 6 + schema/randomprime.schema.json | 229 +++++++----- src/add_modify_obj_patches.rs | 17 +- src/custom_assets.rs | 18 +- src/patch_config.rs | 61 ++- src/patches.rs | 517 ++++++++++++++++++-------- src/pickup_meta.rs | 42 ++- src/pickup_meta.rs.in | 5 + src/starting_items.rs | 47 +++ structs/src/scly_props/spawn_point.rs | 4 +- 10 files changed, 657 insertions(+), 289 deletions(-) diff --git a/ppcasm/ppcasm_macro/src/lib.rs b/ppcasm/ppcasm_macro/src/lib.rs index b4b0ad2c..35176e40 100644 --- a/ppcasm/ppcasm_macro/src/lib.rs +++ b/ppcasm/ppcasm_macro/src/lib.rs @@ -372,6 +372,7 @@ decl_instrs! { addc[o][.], (r:d), (r:a), (r:b) => (6;31) | d | a | b | (?o) | (9;10) | (?.); addi, (r:d), (r:a), (i:imm) => (6;14) | d | a | (16;imm); addic[.], (r:d), (r:a), (i:imm) => (5;6) | (?.) | d | a | (16;imm); + and[.], (r:d), (r:a), (r:b) => (6;31) | d | a | b | (10;28) | (?.); andi, (r:d), (r:a), (i:imm) => (6;28) | d | a | (16;imm); andis, (r:d), (r:a), (i:imm) => (6;29) | d | a | (16;imm); b[l][a], (l:li) => (6;18) | (24;li) | (?a) | (?l); @@ -409,11 +410,16 @@ decl_instrs! { mr, (r:a), (r:s) => (6;31) | s | a | s | (10;444) | (1;0); mtlr, (r:d) => (6;31) | d | (10;0x100) | (10;467) | (1;0); mullw[o][.],(r:d), (r:a), (r:b) => (6;31) | d | a | b | (?o) | (9;235) | (?.); + neg, (r:d), (r:a) => (6;31) | d | a | (16;208); nop => (32;0x60000000); + nor[.], (r:d), (r:a), (r:b) => (6;31) | d | a | b | (10;124) | (?.); + not, (r:a), (r:s) => (6;31) | s | a | s | (10;124) | (1;0); or[.], (r:d), (r:a), (r:b) => (6;31) | d | a | b | (10;444) | (?.); ori, (r:d), (r:a), (i:imm) => (6;24) | d | a | (16;imm); oris, (r:d), (r:a), (i:imm) => (6;25) | d | a | (16;imm); + slw[.], (r:d), (r:a), (r:b) => (6;31) | d | a | b | (10;24) | (?.); slwi, (r:a), (r:s), (i:n) => (6;21) | s | a | (5;{#n}) | (5;0) |(5;{31 - #n}) | (1;0); + srw[.], (r:d), (r:a), (r:b) => (6;31) | d | a | b | (10;536) | (?.); srwi, (r:a), (r:s), (i:n) => (6;21) | s | a | (5;{32 - #n}) | (5;n) |(5;31) | (1;0); rlwimi[.], (r:a), (r:s), (i:sh), (i:mb), (i:me) => (6;20) | s | a | (5;sh) | (5;mb) |(5;me) | (?.); diff --git a/schema/randomprime.schema.json b/schema/randomprime.schema.json index 27eebd13..6fa54ccd 100644 --- a/schema/randomprime.schema.json +++ b/schema/randomprime.schema.json @@ -327,9 +327,22 @@ "type": "string" }, "springBall": { - "description": "Restores the Spring Ball feature from Metroid Prime Trilogy. Use C-Stick Up while being morphed to use Spring Ball. NOTE: You need Morph Ball Bombs to use Spring Ball just like in Metroid Prime Trilogy.", + "description": "[Deprecated] Restores the Spring Ball feature from Metroid Prime Trilogy. Use C-Stick Up while being morphed to use Spring Ball. NOTE: You need Morph Ball Bombs to use Spring Ball just like in Metroid Prime Trilogy.", "type": "boolean", - "default": false + "default": false, + "deprecated": true + }, + "springBallItem": { + "description": "Spring Ball will require this item to be obtained before being able to use it.", + "$ref": "#/$defs/pickupType", + "not": { + "enum": [ + "Nothing", + "Floaty Jump", + "Ice Trap" + ] + }, + "default": "Spring Ball" }, "warpToStart": { "description": "Refusing to save at any Save Station while holding L + R will warp you to the starting location (by default, Samus' Ship in Tallon Overworld: Landing Site)", @@ -470,9 +483,10 @@ "default": false }, "enableIceTraps": { - "description": "Set to true if Ice Traps items are used in this layout.", + "description": "[Deprecated] Does not do anything anymore", "type": "boolean", - "default": false + "default": false, + "deprecated": true }, "missileStationPbRefill": { "description": "If enabled, Missile Stations also refill Power Bomb ammunition.", @@ -2492,6 +2506,12 @@ "type": "boolean", "default": false }, + "powerSuit": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "default": 0 + }, "variaSuit": { "type": "boolean", "default": false @@ -2535,6 +2555,32 @@ "flamethrower": { "type": "boolean", "default": false + }, + "unknownItem1": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "default": 0 + }, + "unlimitedMissiles": { + "type": "boolean", + "default": false + }, + "unlimitedPowerBombs": { + "type": "boolean", + "default": false + }, + "missileLauncher": { + "type": "boolean", + "default": true + }, + "powerBombLauncher": { + "type": "boolean", + "default": true + }, + "springBall": { + "type": "boolean", + "default": false } }, "required": [ @@ -2552,6 +2598,7 @@ "bombs", "spiderBall", "boostBall", + "powerSuit", "variaSuit", "gravitySuit", "phazonSuit", @@ -2562,7 +2609,13 @@ "superMissile", "wavebuster", "iceSpreader", - "flamethrower" + "flamethrower", + "unknownItem1", + "unlimitedMissiles", + "unlimitedPowerBombs", + "missileLauncher", + "powerBombLauncher", + "springBall" ], "default": { "combatVisor": true, @@ -2579,6 +2632,7 @@ "bombs": false, "spiderBall": false, "boostBall": false, + "powerSuit": 0, "variaSuit": false, "gravitySuit": false, "phazonSuit": false, @@ -2589,7 +2643,13 @@ "superMissile": false, "wavebuster": false, "iceSpreader": false, - "flamethrower": false + "flamethrower": false, + "unknownItem1": 0, + "unlimitedMissiles": false, + "unlimitedPowerBombs": false, + "missileLauncher": true, + "powerBombLauncher": true, + "springBall": false }, "additionalProperties": false }, @@ -2693,53 +2753,7 @@ }, "type": { "description": "Defines what is acquired when the pickup is obtained.", - "type": "string", - "enum": [ - "Power Beam", - "Ice Beam", - "Wave Beam", - "Plasma Beam", - "Missile", - "Scan Visor", - "Morph Ball Bomb", - "Power Bomb", - "Flamethrower", - "Thermal Visor", - "Charge Beam", - "Super Missile", - "Grapple Beam", - "X-Ray Visor", - "Ice Spreader", - "Space Jump Boots", - "Morph Ball", - "Combat Visor", - "Boost Ball", - "Spider Ball", - "Power Suit", - "Gravity Suit", - "Varia Suit", - "Phazon Suit", - "Energy Tank", - "Unknown Item 1", - "Health Refill", - "Unknown Item 2", - "Wavebuster", - "Artifact of Truth", - "Artifact of Strength", - "Artifact of Elder", - "Artifact of Wild", - "Artifact of Lifegiver", - "Artifact of Warrior", - "Artifact of Chozo", - "Artifact of Nature", - "Artifact of Sun", - "Artifact of World", - "Artifact of Spirit", - "Artifact of Newborn", - "Nothing", - "Floaty Jump", - "Ice Trap" - ], + "$ref": "#/$defs/pickupType", "default": "Nothing" }, "scanText": { @@ -3918,51 +3932,16 @@ "default": 0 }, "itemId": { - "type": "string", - "enum": [ - "PowerBeam", - "IceBeam", - "WaveBeam", - "PlasmaBeam", - "Missile", - "ScanVisor", - "MorphBallBomb", - "PowerBomb", - "Flamethrower", - "ThermalVisor", - "ChargeBeam", - "SuperMissile", - "GrappleBeam", - "XRayVisor", - "IceSpreader", - "SpaceJumpBoots", - "MorphBall", - "CombatVisor", - "BoostBall", - "SpiderBall", - "PowerSuit", - "GravitySuit", - "VariaSuit", - "PhazonSuit", - "EnergyTank", - "UnknownItem1", - "HealthRefill", - "UnknownItem2", - "Wavebuster", - "ArtifactOfTruth", - "ArtifactOfStrength", - "ArtifactOfElder", - "ArtifactOfWild", - "ArtifactOfLifegiver", - "ArtifactOfWarrior", - "ArtifactOfChozo", - "ArtifactOfNature", - "ArtifactOfSun", - "ArtifactOfWorld", - "ArtifactOfSpirit", - "ArtifactOfNewborn" - ], - "default": "PowerBeam" + "description": "Any custom items will be stored in UnknownItem2", + "$ref": "#/$defs/pickupType", + "not": { + "enum": [ + "Nothing", + "Floaty Jump", + "Ice Trap" + ] + }, + "default": "Power Beam" }, "active": { "type": "boolean", @@ -5820,6 +5799,60 @@ "Disabled", "Enemy" ] + }, + "pickupType": { + "type": "string", + "enum": [ + "Power Beam", + "Ice Beam", + "Wave Beam", + "Plasma Beam", + "Missile", + "Scan Visor", + "Morph Ball Bomb", + "Power Bomb", + "Flamethrower", + "Thermal Visor", + "Charge Beam", + "Super Missile", + "Grapple Beam", + "X-Ray Visor", + "Ice Spreader", + "Space Jump Boots", + "Morph Ball", + "Combat Visor", + "Boost Ball", + "Spider Ball", + "Power Suit", + "Gravity Suit", + "Varia Suit", + "Phazon Suit", + "Energy Tank", + "Unknown Item 1", + "Health Refill", + "Unknown Item 2", + "Wavebuster", + "Artifact of Truth", + "Artifact of Strength", + "Artifact of Elder", + "Artifact of Wild", + "Artifact of Lifegiver", + "Artifact of Warrior", + "Artifact of Chozo", + "Artifact of Nature", + "Artifact of Sun", + "Artifact of World", + "Artifact of Spirit", + "Artifact of Newborn", + "Unlimited Missiles", + "Unlimited Power Bombs", + "Missile Launcher", + "Main Power Bomb", + "Spring Ball", + "Nothing", + "Floaty Jump", + "Ice Trap" + ] } } } \ No newline at end of file diff --git a/src/add_modify_obj_patches.rs b/src/add_modify_obj_patches.rs index 1558b605..80528e43 100644 --- a/src/add_modify_obj_patches.rs +++ b/src/add_modify_obj_patches.rs @@ -492,9 +492,9 @@ pub fn patch_add_spawn_point( varia_suit: 0, phazon_suit: 0, energy_tanks: 0, - unknown0: 0, + unknown_item_1: 0, health_refill: 0, - unknown1: 0, + unknown_item_2: 0, wavebuster: 0, default_spawn: config.default_spawn.unwrap_or(false) as u8, active: config.active.unwrap_or(true) as u8, @@ -613,9 +613,12 @@ pub fn patch_add_special_fn( area: &mut mlvl_wrapper::MlvlArea, config: SpecialFunctionConfig, ) -> Result<(), String> { - let default = "".to_string(); - let unknown0 = config.unknown1.as_ref().unwrap_or(&default); + let default_unknown0 = "".to_string(); + let unknown0 = config.unknown1.as_ref().unwrap_or(&default_unknown0); let unknown0 = string_to_cstr(unknown0.clone()); + let default_item_id = "Power Beam".to_string(); + let item_id = config.item_id.as_ref().unwrap_or(&default_item_id); + let item_id = PickupType::from_str(&item_id[..]) as u32; macro_rules! new { () => { @@ -630,7 +633,7 @@ pub fn patch_add_special_fn( unknown3: config.unknown4.unwrap_or_default(), layer_change_room_id: config.layer_change_room_id.unwrap_or(0xFFFFFFFF), layer_change_layer_id: config.layer_change_layer_id.unwrap_or(0xFFFFFFFF), - item_id: config.item_id.unwrap_or(PickupType::PowerBeam) as u32, + item_id, unknown4: config.active.unwrap_or(true) as u8, // active unknown5: config.unknown6.unwrap_or_default(), unknown6: config.spinner1.unwrap_or(0xFFFFFFFF), @@ -668,8 +671,8 @@ pub fn patch_add_special_fn( property_data.layer_change_layer_id = layer_change_layer_id } if let Some(item_id) = config.item_id { - property_data.item_id = item_id as u32 - } + property_data.item_id = PickupType::from_str(&item_id) as u32; + }; if let Some(active) = config.active { property_data.unknown4 = active as u8 } diff --git a/src/custom_assets.rs b/src/custom_assets.rs index ba8fc84a..e007112b 100644 --- a/src/custom_assets.rs +++ b/src/custom_assets.rs @@ -1367,16 +1367,14 @@ pub fn collect_game_resources<'r>( ]; looking_for.extend(custom_scan_point_deps); - if config.enable_ice_traps { - let player_freeze_deps: Vec<(u32, FourCC)> = vec![ - resource_info!("breakFreezeVisor.PART").into(), - resource_info!("Frost1TXTR.TXTR").into(), - resource_info!("75DAC95C.PART").into(), - resource_info!("zorch1_snow3.TXTR").into(), - resource_info!("C28C7348.PART").into(), - ]; - looking_for.extend(player_freeze_deps); - } + let player_freeze_deps: Vec<(u32, FourCC)> = vec![ + resource_info!("breakFreezeVisor.PART").into(), + resource_info!("Frost1TXTR.TXTR").into(), + resource_info!("75DAC95C.PART").into(), + resource_info!("zorch1_snow3.TXTR").into(), + resource_info!("C28C7348.PART").into(), + ]; + looking_for.extend(player_freeze_deps); // Dependencies read from paks and custom assets will go here // let mut found = HashMap::with_capacity(looking_for.len()); diff --git a/src/patch_config.rs b/src/patch_config.rs index 08e762ed..74ba023f 100644 --- a/src/patch_config.rs +++ b/src/patch_config.rs @@ -460,7 +460,7 @@ pub struct SpecialFunctionConfig { pub layer_change_room_id: Option, pub layer_change_layer_id: Option, - pub item_id: Option, + pub item_id: Option, pub active: Option, pub unknown6: Option, @@ -1439,7 +1439,7 @@ pub struct PatchConfig { pub starting_visor: Visor, pub starting_beam: Beam, pub escape_sequence_counts_up: bool, - pub enable_ice_traps: bool, + pub enable_ice_traps: bool, // deprecated pub missile_station_pb_refill: bool, pub door_open_mode: DoorOpenMode, @@ -2335,8 +2335,16 @@ impl PatchConfigPrivate { if !item_max_capacity.contains_key(&PickupType::EnergyTank) && !force_vanilla_layout { item_max_capacity.insert(PickupType::EnergyTank, 200); } + if !item_max_capacity.contains_key(&PickupType::UnknownItem2) { + item_max_capacity.insert(PickupType::UnknownItem2, 2147483647); + } - if item_max_capacity.contains_key(&PickupType::Nothing) + if item_max_capacity.contains_key(&PickupType::UnlimitedMissiles) + || item_max_capacity.contains_key(&PickupType::UnlimitedPowerBombs) + || item_max_capacity.contains_key(&PickupType::MissileLauncher) + || item_max_capacity.contains_key(&PickupType::PowerBombLauncher) + || item_max_capacity.contains_key(&PickupType::SpringBall) + || item_max_capacity.contains_key(&PickupType::Nothing) || item_max_capacity.contains_key(&PickupType::FloatyJump) || item_max_capacity.contains_key(&PickupType::IceTrap) { @@ -2400,9 +2408,43 @@ impl PatchConfigPrivate { Some(items) => items.clone(), None => { if force_vanilla_layout { - StartingItems::from_u64(2188378143) + // from u64 2188378143 + StartingItems { + power_beam: true, + combat_visor: true, + scan_visor: true, + missiles: 15, + energy_tanks: 0, + power_bombs: 0, + wave: false, + ice: false, + plasma: false, + charge: true, + morph_ball: true, + bombs: true, + spider_ball: false, + boost_ball: false, + power_suit: 0, + varia_suit: true, + gravity_suit: false, + phazon_suit: false, + thermal_visor: false, + xray: false, + space_jump: false, + grapple: true, + super_missile: false, + wavebuster: false, + ice_spreader: false, + flamethrower: false, + unknown_item_1: 0, + unlimited_missiles: false, + unlimited_power_bombs: false, + missile_launcher: true, + power_bomb_launcher: true, + spring_ball: false, + } } else { - StartingItems::from_u64(1) + StartingItems::default() } } } @@ -2535,7 +2577,12 @@ impl PatchConfigPrivate { let spring_ball_item = { match self.game_config.spring_ball_item.as_deref() { Some(s) => PickupType::from_str(s), - None => PickupType::MorphBallBomb, + None => PickupType::from_str({ + match spring_ball { + false => "Spring Ball", + true => "Morph Ball Bomb", + } + }), } }; @@ -2674,7 +2721,7 @@ impl PatchConfigPrivate { .unwrap_or_else(|| StartingItems::from_u64(1)), disable_item_loss: self.game_config.disable_item_loss.unwrap_or(true), escape_sequence_counts_up: self.game_config.escape_sequence_counts_up.unwrap_or(false), - enable_ice_traps: self.game_config.enable_ice_traps.unwrap_or(false), + enable_ice_traps: self.game_config.enable_ice_traps.unwrap_or(true), missile_station_pb_refill: self.game_config.missile_station_pb_refill.unwrap_or(false), door_open_mode: self .game_config diff --git a/src/patches.rs b/src/patches.rs index 5ddd1d99..bfceb52c 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -4194,7 +4194,6 @@ fn modify_pickups_in_mrea<'r>( let mut special_fn_artifact_layer_change_id = 0; let mut trigger_id = 0; let mut floaty_contraption_id = [0, 0, 0, 0]; - let mut special_fn_ice_trap_id = 0; let pickup_kind = pickup_type.kind(); if (29..=40).contains(&pickup_kind) { @@ -4215,10 +4214,6 @@ fn modify_pickups_in_mrea<'r>( ]; } - if pickup_type == PickupType::IceTrap { - special_fn_ice_trap_id = area.new_object_id_from_layer_id(0); - } - let four_ids = [ area.new_object_id_from_layer_id(0), area.new_object_id_from_layer_id(0), @@ -4374,27 +4369,6 @@ fn modify_pickups_in_mrea<'r>( }); } - // If this is an ice trap, insert a special function to freeze the player on picking up - // Extra dependencies for the freeze effect - // steamTxtr -> "Frost1TXTR.TXTR" - // iceTxtr -> "breakFreezeVisor.PART" - if pickup_type == PickupType::IceTrap { - let function = structs::SclyObject { - instance_id: special_fn_ice_trap_id, - property_data: structs::SpecialFunction::ice_trap_fn( - b"Ice Trap Special Function\0".as_cstr(), - ) - .into(), - connections: vec![].into(), - }; - layers[0].objects.as_mut_vec().push(function); - additional_connections.push(structs::Connection { - state: structs::ConnectionState::ARRIVED, - message: structs::ConnectionMsg::ACTION, - target_object_id: special_fn_ice_trap_id, - }); - } - if respawn || mrea_id == 0x40C548E9 { if auto_respawn_timer_id != 0 { let timer = structs::SclyObject { @@ -9828,7 +9802,6 @@ fn patch_dol( smoother_teleports: bool, skip_splash_screens: bool, escape_sequence_counts_up: bool, - enable_ice_traps: bool, uuid: Option<[u8; 16]>, shoot_in_grapple: bool, ) -> Result<(), String> { @@ -11166,8 +11139,9 @@ fn patch_dol( } // TO-DO : - // Disable spring ball on Trilogy if config.spring_ball is set to false - if config.spring_ball { + // Set spring ball item on Trilogy + if [Version::NtscJTrilogy, Version::NtscUTrilogy, Version::PalTrilogy].contains(&version) { + } else { // call compute spring ball movement #[rustfmt::skip] let call_compute_spring_ball_movement_patch = ppcasm!( @@ -11464,120 +11438,348 @@ fn patch_dol( new_text_section.extend(spring_ball_cooldown_reset_on_morph_patch.encoded_bytes()); } - if enable_ice_traps { - let switch_case_ice_trap_patch = ppcasm!(symbol_addr!("Case0_Switch_AcceptScriptMsg_CScriptSpecialFunction", version) + 33 * 4, { - .long new_text_section_end; + // custom item support + let first_custom_item_idx = -1 * (PickupType::ArtifactOfNewborn.kind() + 1) as i32; + let custom_item_initialize_power_up_hook = ppcasm!(symbol_addr!("InitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei", version) + 0x1c, { + b { new_text_section_end }; + }); + dol_patcher.ppcasm_patch(&custom_item_initialize_power_up_hook)?; + + let custom_item_initialize_power_up_patch = ppcasm!(new_text_section_end, { + mr r29, r4; + mr r14, r5; + lis r15, { symbol_addr!("CPlayerState_PowerUpMaxValues", version) }@h; + addi r15, r15, { symbol_addr!("CPlayerState_PowerUpMaxValues", version) }@l; + + // add to item total if pickup isn't disappearing + lwz r3, 0x14(r1); + lwz r3, 0x26c(r3); + cmpwi r3, 0; + bne check_custom_item; + li r3, { PickupType::PowerSuit.kind() }; + rlwinm r0, r3, 0x3, 0x0, 0x1c; + add r3, r31, r0; + addi r3, r3, 0x28; + lwz r4, 0x4(r3); + addi r4, r4, 1; + stw r4, 0x4(r3); + + // add/remove custom item to unknown item 2 + check_custom_item: + cmpwi r29, { PickupType::ArtifactOfNewborn.kind() }; + ble continue_init_power_up; + li r3, { PickupType::UnknownItem2.kind() }; + rlwinm r0, r3, 0x3, 0x0, 0x1c; + add r3, r31, r0; + addi r3, r3, 0x2c; + li r4, { first_custom_item_idx }; + add r4, r4, r29; + li r0, 1; + slw r0, r4, r4; + lwz r0, 0x0(r3); + cmpwi r14, 0; + blt remove_custom_item; + or r0, r4, r4; + b set_custom_item; + remove_custom_item: + not r4, r4; + and r0, r4, r4; + set_custom_item: + stw r4, 0x0(r3); + + // check if it is missile launcher + cmpwi r29, { PickupType::MissileLauncher.kind() }; + bne check_power_bomb; + li r3, { PickupType::Missile.kind() }; + lwz r0, { PickupType::Missile.kind() * 4 }(r15); + b incr_capacity; + + // check if it is power bomb launcher + check_power_bomb: + cmpwi r29, { PickupType::PowerBombLauncher.kind() }; + bne check_ice_trap; + li r3, { PickupType::PowerBomb.kind() }; + lwz r0, { PickupType::PowerBomb.kind() * 4 }(r15); + b incr_capacity; + + // check if it is ice trap + check_ice_trap: + cmpwi r29, { PickupType::IceTrap.kind() }; + bne continue_init_power_up; + mr r16, r5; + lwz r3, 0x84c(r25); + mr r4, r25; + lis r5, 0x6FC0; + ori r5, r5, 0x3D46; + li r6, 0xC34; + lis r7, 0x2B75; + ori r7, r7, 0x7945; + bl { symbol_addr!("Freeze__7CPlayerFR13CStateManagerUiUsUi", version) }; + lis r5, data@h; + addi r5, r5, data@l; + lfs f14, 0x0(r5); + lwz r5, 0x8b8(r25); + lwz r5, 0x0(r5); + lfs f15, 0x0c(r5); + fsubs f15, f15, f14; + stfs f15, 0x0c(r5); + fcmpu cr0, f15, f28; + bgt not_dead_from_ice_trap; + lwz r4, 0x0(r5); + andis r4, r4, 0x7fff; + stw r4, 0x0(r5); + not_dead_from_ice_trap: + b end_init_power_up; + + // check for max capacity + incr_capacity: + rlwinm r0, r3, 0x3, 0x0, 0x1c; + add r3, r31, r0; + addi r3, r3, 0x28; + lwz r4, 0x4(r3); + add r4, r4, r14; + cmpw r4, r0; + ble incr_capacity_check_for_negative; + mr r4, r0; + b incr_capacity_set_capacity; + incr_capacity_check_for_negative: + cmpwi r4, 0; + bge incr_capacity_set_capacity; + li r4, 0; + incr_capacity_set_capacity: + stw r4, 0x4(r3); + + // check for max amount + lwz r4, 0x0(r3); + add r4, r4, r14; + lwz r0, 0x4(r3); + cmpw r4, r0; + ble incr_amount_check_for_negative; + mr r4, r0; + b incr_amount_set_amount; + incr_amount_check_for_negative: + cmpwi r4, 0; + bge incr_amount_set_amount; + li r4, 0; + incr_amount_set_amount: + stw r4, 0x0(r3); + + end_init_power_up: + mr r5, r14; + andi r14, r14, 0; + andi r15, r15, 0; + andi r16, r16, 0; + fmr f14, f28; + fmr f15, f28; + b { symbol_addr!("InitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei", version) + 0x108 }; + + // restore previous context + continue_init_power_up: + mr r5, r14; + andi r14, r14, 0; + andi r15, r15, 0; + andi r16, r16, 0; + fmr f14, f28; + fmr f15, f28; + cmpwi r29, 0; + b { symbol_addr!("InitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei", version) + 0x20 }; + data: + .float 75.0; }); - dol_patcher.ppcasm_patch(&switch_case_ice_trap_patch)?; - - if version == Version::NtscJ || version == Version::Pal { - let ice_trap_special_func_patch = ppcasm!(new_text_section_end, { - // backup return to case 0 - lwz r16, 0x0(r3); - // if message not Action then return - cmpwi r29, 19; - bne { new_text_section_end + 0x8c }; - // backup "this" pointer - mr r14, r3; - mr r15, r5; - - // function body - lwz r3, 0x84c(r25); - mr r4, r25; - lis r5, 0x6FC0; - ori r5, r5, 0x3D46; - li r6, 0xC34; - lis r7, 0x2B75; - ori r7, r7, 0x7945; - bl { symbol_addr!("Freeze__7CPlayerFR13CStateManagerUiUsUi", version) }; - lis r5, data@h; - addi r5, r5, data@l; - lfs f14, 0x0(r5); - lwz r5, 0x8b8(r25); - lwz r5, 0x0(r5); - lfs f15, 0x0c(r5); - fsubs f15, f15, f14; - stfs f15, 0x0c(r5); - fcmpu cr0, f15, f28; - bgt { new_text_section_end + 0x68 }; - lwz r4, 0x0(r5); - andis r4, r4, 0x7fff; - stw r4, 0x0(r5); - - // restore registers - fmr f14, f28; - fmr f15, f28; - mr r3, r14; - andi r14, r14, 0; - mr r4, r28; - mr r5, r15; - andi r15, r15, 0; - mr r6, r25; - mr r7, r25; - mtlr r16; - andi r16, r16, 0; - blr; - data: - .float 75.0; - }); - new_text_section_end += ice_trap_special_func_patch.encoded_bytes().len() as u32; - new_text_section.extend(ice_trap_special_func_patch.encoded_bytes()); - } else { - let ice_trap_special_func_patch = ppcasm!(new_text_section_end, { - // backup return to case 0 - lwz r16, 0x0(r3); - // if message not Action then return - cmpwi r28, 19; - bne { new_text_section_end + 0x8c }; - // backup "this" pointer - mr r14, r3; - mr r15, r5; - - // function body - lwz r3, 0x84c(r25); - mr r4, r25; - lis r5, 0x6FC0; - ori r5, r5, 0x3D46; - li r6, 0xC34; - lis r7, 0x2B75; - ori r7, r7, 0x7945; - bl { symbol_addr!("Freeze__7CPlayerFR13CStateManagerUiUsUi", version) }; - lis r5, data@h; - addi r5, r5, data@l; - lfs f14, 0x0(r5); - lwz r5, 0x8b8(r25); - lwz r5, 0x0(r5); - lfs f15, 0x0c(r5); - fsubs f15, f15, f14; - stfs f15, 0x0c(r5); - fcmpu cr0, f15, f28; - bgt { new_text_section_end + 0x68 }; - lwz r4, 0x0(r5); - andis r4, r4, 0x7fff; - stw r4, 0x0(r5); - - // restore registers - fmr f14, f28; - fmr f15, f28; - mr r3, r14; - andi r14, r14, 0; - mr r4, r28; - mr r5, r15; - andi r15, r15, 0; - mr r6, r25; - mr r7, r25; - mtlr r16; - andi r16, r16, 0; - blr; - data: - .float 75.0; - }); + new_text_section_end += custom_item_initialize_power_up_patch.encoded_bytes().len() as u32; + new_text_section.extend(custom_item_initialize_power_up_patch.encoded_bytes()); - new_text_section_end += ice_trap_special_func_patch.encoded_bytes().len() as u32; - new_text_section.extend(ice_trap_special_func_patch.encoded_bytes()); - } - } + let custom_item_has_power_up_hook = ppcasm!(symbol_addr!("HasPowerUp__12CPlayerStateCFQ212CPlayerState9EItemType", version), { + b { new_text_section_end }; + }); + dol_patcher.ppcasm_patch(&custom_item_has_power_up_hook)?; + let custom_item_has_power_up_patch = ppcasm!(new_text_section_end, { + // check custom item in unknown item 2 + cmpwi r4, { PickupType::ArtifactOfNewborn.kind() }; + ble { new_text_section_end + 0x34 }; + li r15, { PickupType::UnknownItem2.kind() }; + rlwinm r0, r15, 0x3, 0x0, 0x1c; + add r15, r3, r0; + addi r15, r15, 0x2c; + li r3, { first_custom_item_idx }; + add r3, r3, r4; + lwz r0, 0x0(r15); + srw r0, r3, r3; + andi r3, r3, 1; + andi r15, r15, 0; + blr; + + // restore previous context + andi r15, r15, 0; + cmpwi r4, 0; + b { symbol_addr!("HasPowerUp__12CPlayerStateCFQ212CPlayerState9EItemType", version) + 0x4 }; + }); + + new_text_section_end += custom_item_has_power_up_patch.encoded_bytes().len() as u32; + new_text_section.extend(custom_item_has_power_up_patch.encoded_bytes()); + + let custom_item_get_item_amount_hook = ppcasm!(symbol_addr!("GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType", version), { + b { new_text_section_end }; + }); + dol_patcher.ppcasm_patch(&custom_item_get_item_amount_hook)?; + let custom_item_get_item_amount_patch = ppcasm!(new_text_section_end, { + // backup arguments + mr r14, r3; + + // preload unknown item 2 for future checks in the function + li r15, { PickupType::UnknownItem2.kind() }; + rlwinm r0, r15, 0x3, 0x0, 0x1c; + add r15, r3, r0; + addi r15, r15, 0x2c; + lwz r15, 0x0(r15); + + cmpwi r4, { PickupType::Missile.kind() }; + bne { new_text_section_end + 0x40 }; + // check for missile launcher + andi r15, r3, { PickupType::MissileLauncher.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x68 }; + // check for unlimited missiles + andi r15, r3, { PickupType::UnlimitedMissiles.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x78 }; + li r3, 255; + b { new_text_section_end + 0x6c }; + + cmpwi r4, { PickupType::PowerBomb.kind() }; + bne { new_text_section_end + 0x78 }; + // check for power bomb launcher + andi r15, r3, { PickupType::PowerBombLauncher.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x68 }; + // check for unlimited power bombs + andi r15, r3, { PickupType::UnlimitedPowerBombs.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x78 }; + li r3, 8; + b { new_text_section_end + 0x6c }; + + li r3, 0; + + andi r14, r14, 0; + andi r15, r15, 0; + blr; + + // restore previous context + mr r3, r14; + andi r14, r14, 0; + andi r15, r15, 0; + cmpwi r4, 0; + b { symbol_addr!("GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType", version) + 0x4 }; + }); + + new_text_section_end += custom_item_get_item_amount_patch.encoded_bytes().len() as u32; + new_text_section.extend(custom_item_get_item_amount_patch.encoded_bytes()); + + let custom_item_get_item_capacity_hook = ppcasm!(symbol_addr!("GetItemCapacity__12CPlayerStateCFQ212CPlayerState9EItemType", version), { + b { new_text_section_end }; + }); + dol_patcher.ppcasm_patch(&custom_item_get_item_capacity_hook)?; + let custom_item_get_item_capacity_patch = ppcasm!(new_text_section_end, { + // backup arguments + mr r14, r3; + + // preload unknown item 2 for future checks in the function + li r15, { PickupType::UnknownItem2.kind() }; + rlwinm r0, r15, 0x3, 0x0, 0x1c; + add r15, r3, r0; + addi r15, r15, 0x2c; + lwz r15, 0x0(r15); + + cmpwi r4, { PickupType::Missile.kind() }; + bne { new_text_section_end + 0x40 }; + // check for missile launcher + andi r15, r3, { PickupType::MissileLauncher.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x68 }; + // check for unlimited missiles + andi r15, r3, { PickupType::UnlimitedMissiles.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x78 }; + li r3, 255; + b { new_text_section_end + 0x6c }; + + cmpwi r4, { PickupType::PowerBomb.kind() }; + bne { new_text_section_end + 0x78 }; + // check for power bomb launcher + andi r15, r3, { PickupType::PowerBombLauncher.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x68 }; + // check for unlimited power bombs + andi r15, r3, { PickupType::UnlimitedPowerBombs.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x78 }; + li r3, 8; + b { new_text_section_end + 0x6c }; + + li r3, 0; + + andi r14, r14, 0; + andi r15, r15, 0; + blr; + + // restore previous context + mr r3, r14; + andi r14, r14, 0; + andi r15, r15, 0; + cmpwi r4, 0; + b { symbol_addr!("GetItemCapacity__12CPlayerStateCFQ212CPlayerState9EItemType", version) + 0x4 }; + }); + + new_text_section_end += custom_item_get_item_capacity_patch.encoded_bytes().len() as u32; + new_text_section.extend(custom_item_get_item_capacity_patch.encoded_bytes()); + + let custom_item_decr_pickup_hook = ppcasm!(symbol_addr!("DecrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei", version), { + b { new_text_section_end }; + }); + dol_patcher.ppcasm_patch(&custom_item_decr_pickup_hook)?; + let custom_item_decr_pickup_patch = ppcasm!(new_text_section_end, { + // backup arguments + mr r14, r3; + + // preload unknown item 2 for future checks in the function + li r15, { PickupType::UnknownItem2.kind() }; + rlwinm r0, r15, 0x3, 0x0, 0x1c; + add r15, r3, r0; + addi r15, r15, 0x2c; + lwz r15, 0x0(r15); + + cmpwi r4, { PickupType::Missile.kind() }; + bne { new_text_section_end + 0x30 }; + // check for unlimited missiles + andi r15, r3, { PickupType::UnlimitedMissiles.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x54 }; + b { new_text_section_end + 0x48 }; + + cmpwi r4, { PickupType::PowerBomb.kind() }; + bne { new_text_section_end + 0x54 }; + // check for unlimited power bombs + andi r15, r3, { PickupType::UnlimitedPowerBombs.custom_item_value() }; + cmpwi r3, 0; + beq { new_text_section_end + 0x54 }; + b { new_text_section_end + 0x48 }; + + andi r14, r14, 0; + andi r15, r15, 0; + blr; + + // restore previous context + mr r3, r14; + andi r14, r14, 0; + andi r15, r15, 0; + cmpwi r4, 0; + b { symbol_addr!("DecrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei", version) + 0x4 }; + }); + + new_text_section_end += custom_item_decr_pickup_patch.encoded_bytes().len() as u32; + new_text_section.extend(custom_item_decr_pickup_patch.encoded_bytes()); // restore chest vulnerability to missile and charged shot, also wavebuster cheese works too if [Version::Pal, Version::NtscJ].contains(&version) { @@ -12472,9 +12674,9 @@ fn patch_final_boss_permadeath<'r>( varia_suit: 0, phazon_suit: 0, energy_tanks: 0, - unknown0: 0, + unknown_item_1: 0, health_refill: 0, - unknown1: 0, + unknown_item_2: 0, wavebuster: 0, default_spawn: 0, active: 1, @@ -13398,9 +13600,9 @@ fn patch_add_dock_teleport<'r>( varia_suit: 0, phazon_suit: 0, energy_tanks: 0, - unknown0: 0, + unknown_item_1: 0, health_refill: 0, - unknown1: 0, + unknown_item_2: 0, wavebuster: 0, default_spawn: 0, active: 1, @@ -16160,11 +16362,9 @@ fn build_and_run_patches<'r>( let mut patcher = PrimePatcher::new(); // Add the freeze effect assets required by CPlayer::Freeze() - if config.enable_ice_traps { - patcher.add_file_patch(b"GGuiSys.pak", |file| { - add_player_freeze_assets(file, game_resources) - }); - } + patcher.add_file_patch(b"GGuiSys.pak", |file| { + add_player_freeze_assets(file, game_resources) + }); // Add the pickup icon patcher.add_file_patch(b"GGuiSys.pak", |file| add_map_pickup_icon_txtr(file)); @@ -17114,6 +17314,11 @@ fn build_and_run_patches<'r>( pickups[idx].clone() // TODO: cloning is suboptimal } }; + + if pickup.pickup_type == "Unknown Item 2" { + panic!("Unknown Item 2 is no more possible to be used directly. If you wish to use custom items then specify their type instead!"); + } + let show_icon = pickup.show_icon.unwrap_or(false); let key = PickupHashKey { @@ -17141,12 +17346,6 @@ fn build_and_run_patches<'r>( } }; - if !config.enable_ice_traps - && PickupType::from_str(&pickup.pickup_type) == PickupType::IceTrap - { - panic!("EnableIceTraps must be true if you are placing Ice Trap pickups"); - } - // modify pickup, connections, hudmemo etc. patcher.add_scly_patch( (pak_name.as_bytes(), room_info.room_id.to_u32()), @@ -17225,12 +17424,6 @@ fn build_and_run_patches<'r>( } }; - if !config.enable_ice_traps - && PickupType::from_str(&pickup.pickup_type) == PickupType::IceTrap - { - panic!("EnableIceTraps must be true if you are placing Ice Trap pickups"); - } - patcher.add_scly_patch( (pak_name.as_bytes(), room_info.room_id.to_u32()), move |_ps, area| { @@ -17707,7 +17900,6 @@ fn build_and_run_patches<'r>( true, config.skip_splash_screens, config.escape_sequence_counts_up, - config.enable_ice_traps, config.uuid, config.shoot_in_grapple, ) @@ -17728,7 +17920,6 @@ fn build_and_run_patches<'r>( false, config.skip_splash_screens, config.escape_sequence_counts_up, - config.enable_ice_traps, config.uuid, config.shoot_in_grapple, ) diff --git a/src/pickup_meta.rs b/src/pickup_meta.rs index 27046717..1f39df84 100644 --- a/src/pickup_meta.rs +++ b/src/pickup_meta.rs @@ -38,7 +38,7 @@ pub enum PickupType { EnergyTank, UnknownItem1, HealthRefill, - UnknownItem2, + UnknownItem2, // now used for custom items Wavebuster, ArtifactOfTruth, ArtifactOfStrength, @@ -52,6 +52,11 @@ pub enum PickupType { ArtifactOfWorld, ArtifactOfSpirit, ArtifactOfNewborn, + UnlimitedMissiles, + UnlimitedPowerBombs, + MissileLauncher, + PowerBombLauncher, + SpringBall, Nothing, FloatyJump, IceTrap, @@ -101,6 +106,11 @@ impl PickupType { PickupType::ArtifactOfWorld => "Artifact Of World", PickupType::ArtifactOfSpirit => "Artifact Of Spirit", PickupType::ArtifactOfNewborn => "Artifact Of Newborn", + PickupType::UnlimitedMissiles => "Unlimited Missiles", + PickupType::UnlimitedPowerBombs => "Unlimited Power Bombs", + PickupType::MissileLauncher => "Missile Launcher", + PickupType::PowerBombLauncher => "Main Power Bomb", + PickupType::SpringBall => "Spring Ball", PickupType::Nothing => "Nothing", PickupType::FloatyJump => "Floaty Jump", PickupType::IceTrap => "Ice Trap", @@ -150,6 +160,11 @@ impl PickupType { PickupType::ArtifactOfWorld, PickupType::ArtifactOfSpirit, PickupType::ArtifactOfNewborn, + PickupType::UnlimitedMissiles, + PickupType::UnlimitedPowerBombs, + PickupType::MissileLauncher, + PickupType::PowerBombLauncher, + PickupType::SpringBall, PickupType::Nothing, PickupType::FloatyJump, PickupType::IceTrap, @@ -161,11 +176,19 @@ impl PickupType { pub fn kind(&self) -> u32 { match self { PickupType::FloatyJump => PickupType::Nothing.kind(), - PickupType::IceTrap => PickupType::Nothing.kind(), _ => *self as u32, } } + pub fn custom_item_value(&self) -> i32 + { + if self.kind() <= PickupType::ArtifactOfNewborn.kind() || self.kind() >= PickupType::Nothing.kind() { + panic!("PickupType needs to be a custom item to return a custom item value"); + } + + 2_i32.pow(self.kind() - PickupType::ArtifactOfNewborn.kind() - 1) as i32 + } + #[allow(clippy::should_implement_trait)] pub fn from_str(string: &str) -> Self { let string = string.to_lowercase(); @@ -261,6 +284,11 @@ pub fn pickup_type_for_pickup(pickup: &structs::Pickup) -> Option { 36 => Some(PickupType::ArtifactOfNature), 30 => Some(PickupType::ArtifactOfStrength), 26 if pickup.curr_increase == 20 => Some(PickupType::HealthRefill), + 41 => Some(PickupType::UnlimitedMissiles), + 42 => Some(PickupType::UnlimitedPowerBombs), + 43 => Some(PickupType::MissileLauncher), + 44 => Some(PickupType::PowerBombLauncher), + 47 => Some(PickupType::IceTrap), _ => None, } } @@ -306,6 +334,11 @@ pub fn pickup_model_for_pickup(pickup: &structs::Pickup) -> Option 36 => Some(PickupModel::ArtifactOfNature), 30 => Some(PickupModel::ArtifactOfStrength), 26 if pickup.curr_increase == 20 => Some(PickupModel::HealthRefill), + 41 => Some(PickupModel::MissileRefill), + 42 => Some(PickupModel::PowerBombRefill), + 43 => Some(PickupModel::Missile), + 44 => Some(PickupModel::PowerBomb), + 47 => Some(PickupModel::IceTrap), _ => None, } } @@ -569,6 +602,11 @@ impl PickupModel { PickupType::ArtifactOfWorld => PickupModel::ArtifactOfWorld, PickupType::ArtifactOfSpirit => PickupModel::ArtifactOfSpirit, PickupType::ArtifactOfNewborn => PickupModel::ArtifactOfNewborn, + PickupType::UnlimitedMissiles => PickupModel::MissileRefill, + PickupType::UnlimitedPowerBombs => PickupModel::PowerBombRefill, + PickupType::MissileLauncher => PickupModel::Missile, + PickupType::PowerBombLauncher => PickupModel::PowerBomb, + PickupType::SpringBall => PickupModel::RandovaniaGamecube, PickupType::Nothing => PickupModel::Nothing, PickupType::FloatyJump => PickupModel::RandovaniaGamecube, PickupType::IceTrap => PickupModel::IceTrap, diff --git a/src/pickup_meta.rs.in b/src/pickup_meta.rs.in index 4c1adef0..adc7e9ff 100644 --- a/src/pickup_meta.rs.in +++ b/src/pickup_meta.rs.in @@ -10359,6 +10359,11 @@ impl PickupType PickupType::ArtifactOfWorld => "audio/jin_artifact.dsp\0", PickupType::ArtifactOfSpirit => "audio/jin_artifact.dsp\0", PickupType::ArtifactOfNewborn => "audio/jin_artifact.dsp\0", + PickupType::UnlimitedMissiles => "/audio/itm_x_short_02.dsp\0", + PickupType::UnlimitedPowerBombs => "/audio/itm_x_short_02.dsp\0", + PickupType::MissileLauncher => "audio/jin_itemattain.dsp\0", + PickupType::PowerBombLauncher => "audio/jin_itemattain.dsp\0", + PickupType::SpringBall => "audio/jin_itemattain.dsp\0", PickupType::Nothing => "/audio/itm_x_short_02.dsp\0", PickupType::FloatyJump => "/audio/itm_x_short_02.dsp\0", PickupType::IceTrap => "/audio/evt_x_event_00.dsp\0", // being a bit trolly here diff --git a/src/starting_items.rs b/src/starting_items.rs index a15f6c7d..a9176f93 100644 --- a/src/starting_items.rs +++ b/src/starting_items.rs @@ -1,3 +1,5 @@ +use crate::pickup_meta::PickupType; + use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -17,6 +19,7 @@ pub struct StartingItems { pub bombs: bool, pub spider_ball: bool, pub boost_ball: bool, + pub power_suit: u32, pub varia_suit: bool, pub gravity_suit: bool, pub phazon_suit: bool, @@ -28,6 +31,12 @@ pub struct StartingItems { pub wavebuster: bool, pub ice_spreader: bool, pub flamethrower: bool, + pub unknown_item_1: u32, + pub unlimited_missiles: bool, + pub unlimited_power_bombs: bool, + pub missile_launcher: bool, + pub power_bomb_launcher: bool, + pub spring_ball: bool, } impl StartingItems { @@ -53,6 +62,7 @@ impl StartingItems { bombs: fetch_bits(1) == 1, spider_ball: fetch_bits(1) == 1, boost_ball: fetch_bits(1) == 1, + power_suit: 0, varia_suit: fetch_bits(1) == 1, gravity_suit: fetch_bits(1) == 1, phazon_suit: fetch_bits(1) == 1, @@ -64,6 +74,12 @@ impl StartingItems { wavebuster: fetch_bits(1) == 1, ice_spreader: fetch_bits(1) == 1, flamethrower: fetch_bits(1) == 1, + unknown_item_1: 0, + unlimited_missiles: false, + unlimited_power_bombs: false, + missile_launcher: true, + power_bomb_launcher: true, + spring_ball: false, } } @@ -82,6 +98,7 @@ impl StartingItems { spawn_point.bombs = self.bombs as u32; spawn_point.spider_ball = self.spider_ball as u32; spawn_point.boost_ball = self.boost_ball as u32; + spawn_point.power_suit = 0; spawn_point.varia_suit = self.varia_suit as u32; spawn_point.gravity_suit = self.gravity_suit as u32; spawn_point.phazon_suit = self.phazon_suit as u32; @@ -93,6 +110,24 @@ impl StartingItems { spawn_point.wavebuster = self.wavebuster as u32; spawn_point.ice_spreader = self.ice_spreader as u32; spawn_point.flamethrower = self.flamethrower as u32; + spawn_point.unknown_item_1 = self.unknown_item_1 as u32; + let mut unknown_item_2 = 0; + if self.unlimited_missiles { + unknown_item_2 |= PickupType::UnlimitedMissiles.custom_item_value(); + } + if self.unlimited_power_bombs { + unknown_item_2 |= PickupType::UnlimitedPowerBombs.custom_item_value(); + } + if self.missile_launcher { + unknown_item_2 |= PickupType::MissileLauncher.custom_item_value(); + } + if self.power_bomb_launcher { + unknown_item_2 |= PickupType::PowerBombLauncher.custom_item_value(); + } + if self.spring_ball { + unknown_item_2 |= PickupType::SpringBall.custom_item_value(); + } + spawn_point.unknown_item_2 = unknown_item_2 as u32; } /// Custom deserializataion function that accepts an int as well as the usual struct/object @@ -140,6 +175,11 @@ impl StartingItems { && !self.wavebuster && !self.ice_spreader && !self.flamethrower + && !self.unlimited_missiles + && !self.unlimited_power_bombs + && !self.missile_launcher + && !self.power_bomb_launcher + && !self.spring_ball } } @@ -160,6 +200,7 @@ impl Default for StartingItems { bombs: false, spider_ball: false, boost_ball: false, + power_suit: 0, varia_suit: false, gravity_suit: false, phazon_suit: false, @@ -171,6 +212,12 @@ impl Default for StartingItems { wavebuster: false, ice_spreader: false, flamethrower: false, + unknown_item_1: 0, + unlimited_missiles: false, + unlimited_power_bombs: false, + missile_launcher: true, + power_bomb_launcher: true, + spring_ball: false } } } diff --git a/structs/src/scly_props/spawn_point.rs b/structs/src/scly_props/spawn_point.rs index b52df7b6..d14d8ded 100644 --- a/structs/src/scly_props/spawn_point.rs +++ b/structs/src/scly_props/spawn_point.rs @@ -40,9 +40,9 @@ pub struct SpawnPoint<'r> { pub varia_suit: u32, pub phazon_suit: u32, pub energy_tanks: u32, - pub unknown0: u32, + pub unknown_item_1: u32, pub health_refill: u32, - pub unknown1: u32, + pub unknown_item_2: u32, pub wavebuster: u32, pub default_spawn: u8, From 40ec34eaa3b3ab6d47854bea0a54ad4689f8519f Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Thu, 5 Sep 2024 22:16:05 +0200 Subject: [PATCH 02/10] Do not update Unknown Item 2 if pickup is temporary (> Nothing) --- src/patches.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/patches.rs b/src/patches.rs index bfceb52c..3b9f6c4c 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -11468,6 +11468,8 @@ fn patch_dol( check_custom_item: cmpwi r29, { PickupType::ArtifactOfNewborn.kind() }; ble continue_init_power_up; + cmpwi r29, { PickupType::Nothing.kind() }; + bge check_missile_launcher; li r3, { PickupType::UnknownItem2.kind() }; rlwinm r0, r3, 0x3, 0x0, 0x1c; add r3, r31, r0; @@ -11487,6 +11489,7 @@ fn patch_dol( set_custom_item: stw r4, 0x0(r3); + check_missile_launcher: // check if it is missile launcher cmpwi r29, { PickupType::MissileLauncher.kind() }; bne check_power_bomb; From fd2e6a1276a6072812ea263b6414c9ff24939f9e Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Thu, 5 Sep 2024 23:23:44 +0200 Subject: [PATCH 03/10] Floaty Jump can be received in multiworld now --- src/patches.rs | 210 ++++++------------------------------------------- 1 file changed, 22 insertions(+), 188 deletions(-) diff --git a/src/patches.rs b/src/patches.rs index 3b9f6c4c..ef96b89e 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -2384,16 +2384,6 @@ fn patch_add_item<'r>( // Pickup to use for game functionality // let pickup_type = PickupType::from_str(&pickup_config.pickup_type); - if pickup_type == PickupType::FloatyJump { - let deps = WaterType::Normal.dependencies(); - let deps_iter = deps.iter().map(|&(file_id, fourcc)| structs::Dependency { - asset_id: file_id, - asset_type: fourcc, - }); - - area.add_dependencies(game_resources, 0, deps_iter); - } - let extern_model = if pickup_config.model.is_some() { extern_models.get(pickup_config.model.as_ref().unwrap()) } else { @@ -2813,17 +2803,8 @@ fn patch_add_item<'r>( // ); // generate object IDs before borrowing scly section as mutable - let mut floaty_contraption_id = [0, 0, 0, 0]; let mut poi_id = 0; let mut special_fn_artifact_layer_change_id = 0; - if pickup_type == PickupType::FloatyJump { - floaty_contraption_id = [ - area.new_object_id_from_layer_id(new_layer_idx), - area.new_object_id_from_layer_id(new_layer_idx), - area.new_object_id_from_layer_id(new_layer_idx), - area.new_object_id_from_layer_id(new_layer_idx), - ]; - } let special_function_id = area.new_object_id_from_layer_id(new_layer_idx); let four_ids = [ area.new_object_id_from_layer_id(new_layer_idx), @@ -2844,26 +2825,6 @@ fn patch_add_item<'r>( let scly = area.mrea().scly_section_mut(); let layers = scly.layers.as_mut_vec(); - if pickup_type == PickupType::FloatyJump { - place_floaty_contraption( - layers[0].objects.as_mut_vec(), - floaty_contraption_id[0], - floaty_contraption_id[1], - floaty_contraption_id[2], - floaty_contraption_id[3], - pickup_position, - ); - - pickup_obj - .connections - .as_mut_vec() - .push(structs::Connection { - state: structs::ConnectionState::ARRIVED, - message: structs::ConnectionMsg::RESET_AND_START, - target_object_id: floaty_contraption_id[0], - }); - } - if shuffle_position || *pickup_config.jumbo_scan.as_ref().unwrap_or(&false) { layers[new_layer_idx] .objects @@ -4064,16 +4025,6 @@ fn modify_pickups_in_mrea<'r>( // Pickup to use for game functionality // let pickup_type = PickupType::from_str(&pickup_config.pickup_type); - if pickup_type == PickupType::FloatyJump { - let deps = WaterType::Normal.dependencies(); - let deps_iter = deps.iter().map(|&(file_id, fourcc)| structs::Dependency { - asset_id: file_id, - asset_type: fourcc, - }); - - area.add_dependencies(game_resources, 0, deps_iter); - } - let extern_model = if pickup_config.model.is_some() { extern_models.get(pickup_config.model.as_ref().unwrap()) } else { @@ -4193,7 +4144,6 @@ fn modify_pickups_in_mrea<'r>( let post_pickup_relay_id = area.new_object_id_from_layer_name("Default"); let mut special_fn_artifact_layer_change_id = 0; let mut trigger_id = 0; - let mut floaty_contraption_id = [0, 0, 0, 0]; let pickup_kind = pickup_type.kind(); if (29..=40).contains(&pickup_kind) { @@ -4205,15 +4155,6 @@ fn modify_pickups_in_mrea<'r>( trigger_id = area.new_object_id_from_layer_name("Default"); } - if pickup_type == PickupType::FloatyJump { - floaty_contraption_id = [ - area.new_object_id_from_layer_id(0), - area.new_object_id_from_layer_id(0), - area.new_object_id_from_layer_id(0), - area.new_object_id_from_layer_id(0), - ]; - } - let four_ids = [ area.new_object_id_from_layer_id(0), area.new_object_id_from_layer_id(0), @@ -4509,14 +4450,6 @@ fn modify_pickups_in_mrea<'r>( }); }*/ - if pickup_type == PickupType::FloatyJump { - additional_connections.push(structs::Connection { - state: structs::ConnectionState::ARRIVED, - message: structs::ConnectionMsg::RESET_AND_START, - target_object_id: floaty_contraption_id[0], - }); - } - if jumbo_poi { layers[0].objects.as_mut_vec().push(structs::SclyObject { instance_id: jumbo_poi_special_function_id, @@ -4598,17 +4531,6 @@ fn modify_pickups_in_mrea<'r>( } } - if pickup_type == PickupType::FloatyJump { - place_floaty_contraption( - layers[0].objects.as_mut_vec(), - floaty_contraption_id[0], - floaty_contraption_id[1], - floaty_contraption_id[2], - floaty_contraption_id[3], - position, - ); - } - if jumbo_poi { layers[jumbo_poi_layer_idx] .objects @@ -4696,115 +4618,6 @@ fn modify_pickups_in_mrea<'r>( Ok(()) } -fn place_floaty_contraption( - objects: &mut Vec, - timer1_id: u32, // send RESET_AND_START to this ID to give floaty - timer2_id: u32, - water_id: u32, - camera_id: u32, - position: [f32; 3], -) { - if timer1_id == 0 - || timer2_id == 0 - || water_id == 0 - || camera_id == 0 - || timer1_id == timer2_id - || timer1_id == water_id - || timer1_id == camera_id - || timer2_id == water_id - || timer2_id == camera_id - || water_id == camera_id - { - panic!("something went wrong making floaty contraption"); - } - - let mut water_obj = WaterType::Normal.to_obj(); - water_obj.instance_id = water_id; - - let water = water_obj.property_data.as_water_mut().unwrap(); - water.position = position.into(); - water.scale = [5.0, 5.0, 5.0].into(); - water.active = 0; - - objects.push(water_obj); - - objects.push(structs::SclyObject { - instance_id: timer1_id, - property_data: structs::Timer { - name: b"floaty timer1\0".as_cstr(), - - start_time: 1.0 / 60.0, - max_random_add: 0.0, - looping: 0, - start_immediately: 0, - active: 1, - } - .into(), - connections: vec![ - structs::Connection { - state: structs::ConnectionState::ZERO, - message: structs::ConnectionMsg::ACTIVATE, - target_object_id: water_id, - }, - structs::Connection { - state: structs::ConnectionState::ZERO, - message: structs::ConnectionMsg::RESET_AND_START, - target_object_id: timer2_id, - }, - structs::Connection { - state: structs::ConnectionState::ZERO, - message: structs::ConnectionMsg::ACTIVATE, - target_object_id: camera_id, - }, - ] - .into(), - }); - - objects.push(structs::SclyObject { - instance_id: timer2_id, - property_data: structs::Timer { - name: b"floaty timer2\0".as_cstr(), - - start_time: 1.0 / 60.0, - max_random_add: 0.0, - looping: 0, - start_immediately: 0, - active: 1, - } - .into(), - connections: vec![structs::Connection { - state: structs::ConnectionState::ZERO, - message: structs::ConnectionMsg::DEACTIVATE, - target_object_id: water_id, - }] - .into(), - }); - - objects.push(structs::SclyObject { - instance_id: camera_id, - property_data: structs::Camera { - name: b"floaty camera\0".as_cstr(), - - position: [position[0], position[1], position[2] + 5.0].into(), - rotation: [0.0, 0.0, 0.0].into(), - active: 0, - shot_duration: 2.0 / 60.0, - look_at_player: 0, - out_of_player_eye: 0, - into_player_eye: 0, - draw_player: 0, - disable_input: 1, - unknown: 0, - finish_cine_skip: 0, - field_of_view: 140.0, - check_failsafe: 1, - disable_out_of_into: 0, - } - .into(), - connections: vec![].into(), - }); -} - fn update_pickup( pickup_obj: &mut structs::SclyObject, pickup_type: PickupType, @@ -11508,7 +11321,7 @@ fn patch_dol( // check if it is ice trap check_ice_trap: cmpwi r29, { PickupType::IceTrap.kind() }; - bne continue_init_power_up; + bne check_floaty_jump; mr r16, r5; lwz r3, 0x84c(r25); mr r4, r25; @@ -11534,6 +11347,27 @@ fn patch_dol( not_dead_from_ice_trap: b end_init_power_up; + check_floaty_jump: + cmpwi r29, { PickupType::FloatyJump.kind() }; + bne continue_init_power_up; + lwz r3, 0x84c(r25); + lwz r4, 0xe4(r3); + cmpwi r14, 0; + blt remove_floaty_jump; + li r5, 0x4000; // 4 << 12 => Fluid Count = 0b100 + or r4, r4, r5; + lis r5, 0x41a0; // 20.0 + b apply_floaty_jump; + remove_floaty_jump: + li r5, 0x7000; // 7 << 12 => Fluid Count = 0b111 + nor r5, r5, r5; + and r4, r4, r5; + lis r5, 0; + apply_floaty_jump: + stw r4, 0xe4(r3); + stw r5, 0x828(r3); + b end_init_power_up; + // check for max capacity incr_capacity: rlwinm r0, r3, 0x3, 0x0, 0x1c; From 5555223b5a0868babf0f8b43ef2c8b72458b9985 Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Fri, 6 Sep 2024 04:37:12 +0200 Subject: [PATCH 04/10] Added non NTSC version support to custom items --- generated/dol_symbol_table/1.01.txt | 2 + generated/dol_symbol_table/jpn.txt | 2 + generated/dol_symbol_table/kor.txt | 2 + generated/dol_symbol_table/pal.txt | 2 + src/patches.rs | 59 +++++++++++++++++------------ 5 files changed, 43 insertions(+), 24 deletions(-) diff --git a/generated/dol_symbol_table/1.01.txt b/generated/dol_symbol_table/1.01.txt index 2cc949a7..6edc87ae 100644 --- a/generated/dol_symbol_table/1.01.txt +++ b/generated/dol_symbol_table/1.01.txt @@ -27,6 +27,8 @@ 0x80091ac0 EnableItem__12CPlayerStateFQ212CPlayerState9EItemType 0x80091b14 GetPowerUp__12CPlayerStateFQ212CPlayerState9EItemType 0x80091b3c HasPowerUp__12CPlayerStateCFQ212CPlayerState9EItemType +0x80091b70 GetItemCapacity__12CPlayerStateCFQ212CPlayerState9EItemType +0x80091b98 GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType 0x80091c10 DecrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei 0x80091c6c IncrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei 0x80091de4 InitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei diff --git a/generated/dol_symbol_table/jpn.txt b/generated/dol_symbol_table/jpn.txt index def2c265..800c51e7 100644 --- a/generated/dol_symbol_table/jpn.txt +++ b/generated/dol_symbol_table/jpn.txt @@ -27,6 +27,8 @@ 0x80092c3c DisableItem__12CPlayerStateFQ212CPlayerState9EItemType 0x80092c90 EnableItem__12CPlayerStateFQ212CPlayerState9EItemType 0x80092d0c HasPowerUp__12CPlayerStateCFQ212CPlayerState9EItemType +0x80092d40 GetItemCapacity__12CPlayerStateCFQ212CPlayerState9EItemType +0x80092d68 GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType 0x80092de0 DecrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei 0x80092e3c IncrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei 0x80092f4c ResetAndIncrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei diff --git a/generated/dol_symbol_table/kor.txt b/generated/dol_symbol_table/kor.txt index 9dd25a3d..d27a713e 100644 --- a/generated/dol_symbol_table/kor.txt +++ b/generated/dol_symbol_table/kor.txt @@ -27,6 +27,8 @@ 0x80091ab8 EnableItem__12CPlayerStateFQ212CPlayerState9EItemType 0x80091b0c GetPowerUp__12CPlayerStateFQ212CPlayerState9EItemType 0x80091b34 HasPowerUp__12CPlayerStateCFQ212CPlayerState9EItemType +0x80091b68 GetItemCapacity__12CPlayerStateCFQ212CPlayerState9EItemType +0x80091b90 GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType 0x80091c08 DecrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei 0x80091c64 IncrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei 0x80091ddc InitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei diff --git a/generated/dol_symbol_table/pal.txt b/generated/dol_symbol_table/pal.txt index d8c97b34..03d04ab1 100644 --- a/generated/dol_symbol_table/pal.txt +++ b/generated/dol_symbol_table/pal.txt @@ -383,6 +383,8 @@ 0x80091d54 DisableItem__12CPlayerStateFQ212CPlayerState9EItemType 0x80091da8 EnableItem__12CPlayerStateFQ212CPlayerState9EItemType 0x80091e24 HasPowerUp__12CPlayerStateCFQ212CPlayerState9EItemType +0x80091e58 GetItemCapacity__12CPlayerStateCFQ212CPlayerState9EItemType +0x80091e80 GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType 0x80091ef8 DecrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei 0x80091f54 IncrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei 0x80092064 ResetAndIncrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei diff --git a/src/patches.rs b/src/patches.rs index ef96b89e..ab63b98e 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -11253,6 +11253,16 @@ fn patch_dol( // custom item support let first_custom_item_idx = -1 * (PickupType::ArtifactOfNewborn.kind() + 1) as i32; + let (actor_flags_offset, fluid_depth_offset) = if [Version::Pal, Version::NtscJ, Version::NtscU0_02].contains(&version) { + (0xf0, 0x838) + } else { + (0xe4, 0x828) + }; + let life_time_offset = if [Version::Pal, Version::NtscJ].contains(&version) { + 0x27c + } else { + 0x26c + }; let custom_item_initialize_power_up_hook = ppcasm!(symbol_addr!("InitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei", version) + 0x1c, { b { new_text_section_end }; }); @@ -11266,7 +11276,7 @@ fn patch_dol( // add to item total if pickup isn't disappearing lwz r3, 0x14(r1); - lwz r3, 0x26c(r3); + lwz r3, { life_time_offset }(r3); cmpwi r3, 0; bne check_custom_item; li r3, { PickupType::PowerSuit.kind() }; @@ -11323,7 +11333,7 @@ fn patch_dol( cmpwi r29, { PickupType::IceTrap.kind() }; bne check_floaty_jump; mr r16, r5; - lwz r3, 0x84c(r25); + lwz r3, 0x84c(r30); mr r4, r25; lis r5, 0x6FC0; ori r5, r5, 0x3D46; @@ -11334,7 +11344,7 @@ fn patch_dol( lis r5, data@h; addi r5, r5, data@l; lfs f14, 0x0(r5); - lwz r5, 0x8b8(r25); + lwz r5, 0x8b8(r30); lwz r5, 0x0(r5); lfs f15, 0x0c(r5); fsubs f15, f15, f14; @@ -11350,8 +11360,8 @@ fn patch_dol( check_floaty_jump: cmpwi r29, { PickupType::FloatyJump.kind() }; bne continue_init_power_up; - lwz r3, 0x84c(r25); - lwz r4, 0xe4(r3); + lwz r3, 0x84c(r30); + lwz r4, { actor_flags_offset }(r3); cmpwi r14, 0; blt remove_floaty_jump; li r5, 0x4000; // 4 << 12 => Fluid Count = 0b100 @@ -11360,12 +11370,12 @@ fn patch_dol( b apply_floaty_jump; remove_floaty_jump: li r5, 0x7000; // 7 << 12 => Fluid Count = 0b111 - nor r5, r5, r5; - and r4, r4, r5; + nor r5, r5, r5; // flip bits + and r4, r4, r5; // set Fluid Count to 0b000 lis r5, 0; apply_floaty_jump: - stw r4, 0xe4(r3); - stw r5, 0x828(r3); + stw r4, { actor_flags_offset }(r3); + stw r5, { fluid_depth_offset }(r3); b end_init_power_up; // check for max capacity @@ -11456,29 +11466,30 @@ fn patch_dol( new_text_section_end += custom_item_has_power_up_patch.encoded_bytes().len() as u32; new_text_section.extend(custom_item_has_power_up_patch.encoded_bytes()); + // TODO: Fix it for PAL let custom_item_get_item_amount_hook = ppcasm!(symbol_addr!("GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType", version), { b { new_text_section_end }; }); dol_patcher.ppcasm_patch(&custom_item_get_item_amount_hook)?; let custom_item_get_item_amount_patch = ppcasm!(new_text_section_end, { // backup arguments - mr r14, r3; + mr r21, r3; // preload unknown item 2 for future checks in the function - li r15, { PickupType::UnknownItem2.kind() }; - rlwinm r0, r15, 0x3, 0x0, 0x1c; - add r15, r3, r0; - addi r15, r15, 0x2c; - lwz r15, 0x0(r15); + li r22, { PickupType::UnknownItem2.kind() }; + rlwinm r0, r22, 0x3, 0x0, 0x1c; + add r22, r3, r0; + addi r22, r22, 0x2c; + lwz r22, 0x0(r22); cmpwi r4, { PickupType::Missile.kind() }; bne { new_text_section_end + 0x40 }; // check for missile launcher - andi r15, r3, { PickupType::MissileLauncher.custom_item_value() }; + andi r22, r3, { PickupType::MissileLauncher.custom_item_value() }; cmpwi r3, 0; beq { new_text_section_end + 0x68 }; // check for unlimited missiles - andi r15, r3, { PickupType::UnlimitedMissiles.custom_item_value() }; + andi r22, r3, { PickupType::UnlimitedMissiles.custom_item_value() }; cmpwi r3, 0; beq { new_text_section_end + 0x78 }; li r3, 255; @@ -11487,11 +11498,11 @@ fn patch_dol( cmpwi r4, { PickupType::PowerBomb.kind() }; bne { new_text_section_end + 0x78 }; // check for power bomb launcher - andi r15, r3, { PickupType::PowerBombLauncher.custom_item_value() }; + andi r22, r3, { PickupType::PowerBombLauncher.custom_item_value() }; cmpwi r3, 0; beq { new_text_section_end + 0x68 }; // check for unlimited power bombs - andi r15, r3, { PickupType::UnlimitedPowerBombs.custom_item_value() }; + andi r22, r3, { PickupType::UnlimitedPowerBombs.custom_item_value() }; cmpwi r3, 0; beq { new_text_section_end + 0x78 }; li r3, 8; @@ -11499,14 +11510,14 @@ fn patch_dol( li r3, 0; - andi r14, r14, 0; - andi r15, r15, 0; + andi r21, r21, 0; + andi r22, r22, 0; blr; // restore previous context - mr r3, r14; - andi r14, r14, 0; - andi r15, r15, 0; + mr r3, r21; + andi r21, r21, 0; + andi r22, r22, 0; cmpwi r4, 0; b { symbol_addr!("GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType", version) + 0x4 }; }); From 86e6af8650b3d415d86bafa9baa343ad74927aa8 Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Fri, 6 Sep 2024 04:53:15 +0200 Subject: [PATCH 05/10] Added a check to prevent starting items to add completion percentage --- src/patches.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/patches.rs b/src/patches.rs index ab63b98e..18d7123b 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -11258,10 +11258,10 @@ fn patch_dol( } else { (0xe4, 0x828) }; - let life_time_offset = if [Version::Pal, Version::NtscJ].contains(&version) { - 0x27c + let (probability_offset, life_time_offset) = if [Version::Pal, Version::NtscJ].contains(&version) { + (0x274, 0x27c) } else { - 0x26c + (0x264, 0x26c) }; let custom_item_initialize_power_up_hook = ppcasm!(symbol_addr!("InitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei", version) + 0x1c, { b { new_text_section_end }; @@ -11274,10 +11274,13 @@ fn patch_dol( lis r15, { symbol_addr!("CPlayerState_PowerUpMaxValues", version) }@h; addi r15, r15, { symbol_addr!("CPlayerState_PowerUpMaxValues", version) }@l; - // add to item total if pickup isn't disappearing - lwz r3, 0x14(r1); - lwz r3, { life_time_offset }(r3); + // add to item total if pickup isn't disappearing and has 100% probability to spawn + lwz r4, 0x14(r1); + lwz r3, { life_time_offset }(r4); cmpwi r3, 0; + lhz r3, { probability_offset }(r4); + bne check_custom_item; + cmpwi r3, 0x42c8; bne check_custom_item; li r3, { PickupType::PowerSuit.kind() }; rlwinm r0, r3, 0x3, 0x0, 0x1c; From 145604e31797b68f98e849dd2052615ee6040097 Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Tue, 17 Sep 2024 16:53:30 +0200 Subject: [PATCH 06/10] Removed TODO comment for PAL --- src/patches.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/patches.rs b/src/patches.rs index 18d7123b..422a9501 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -11469,7 +11469,6 @@ fn patch_dol( new_text_section_end += custom_item_has_power_up_patch.encoded_bytes().len() as u32; new_text_section.extend(custom_item_has_power_up_patch.encoded_bytes()); - // TODO: Fix it for PAL let custom_item_get_item_amount_hook = ppcasm!(symbol_addr!("GetItemAmount__12CPlayerStateCFQ212CPlayerState9EItemType", version), { b { new_text_section_end }; }); From 9d5a68e9114d04ca42e521c3b324d45f77267918 Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Tue, 17 Sep 2024 16:58:01 +0200 Subject: [PATCH 07/10] Set defaults for Missile/Power Bomb Launcher when increase not specified --- src/patches.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/patches.rs b/src/patches.rs index 422a9501..0e508fb9 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -2520,8 +2520,10 @@ fn patch_add_item<'r>( 0 } else if pickup_config.curr_increase.is_some() { pickup_config.curr_increase.unwrap() - } else if pickup_type == PickupType::Missile { + } else if [PickupType::Missile, PickupType::MissileLauncher].contains(&pickup_type) { 5 + } else if pickup_type == PickupType::PowerBombLauncher { + 4 } else if pickup_type == PickupType::HealthRefill { 50 } else { @@ -4657,8 +4659,10 @@ fn update_pickup( 0 } else if pickup_config.curr_increase.is_some() { pickup_config.curr_increase.unwrap() - } else if pickup_type == PickupType::Missile { + } else if [PickupType::Missile, PickupType::MissileLauncher].contains(&pickup_type) { 5 + } else if pickup_type == PickupType::PowerBombLauncher { + 4 } else if pickup_type == PickupType::HealthRefill { 50 } else { From e966bd59c871b0ce141c4796e5ad5a8d744b6f9d Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Sat, 5 Oct 2024 17:21:14 +0200 Subject: [PATCH 08/10] Fixed Floaty Jump behavior --- src/patches.rs | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/patches.rs b/src/patches.rs index 0e508fb9..1af4d66c 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -11257,10 +11257,10 @@ fn patch_dol( // custom item support let first_custom_item_idx = -1 * (PickupType::ArtifactOfNewborn.kind() + 1) as i32; - let (actor_flags_offset, fluid_depth_offset) = if [Version::Pal, Version::NtscJ, Version::NtscU0_02].contains(&version) { - (0xf0, 0x838) + let (actor_flags_offset, out_of_water_ticks_offset, fluid_depth_offset) = if [Version::Pal, Version::NtscJ, Version::NtscU0_02].contains(&version) { + (0xf0, 0x2c0, 0x838) } else { - (0xe4, 0x828) + (0xe4, 0x2b0, 0x828) }; let (probability_offset, life_time_offset) = if [Version::Pal, Version::NtscJ].contains(&version) { (0x274, 0x27c) @@ -11364,25 +11364,47 @@ fn patch_dol( not_dead_from_ice_trap: b end_init_power_up; + // check if it is floaty jump check_floaty_jump: cmpwi r29, { PickupType::FloatyJump.kind() }; bne continue_init_power_up; lwz r3, 0x84c(r30); - lwz r4, { actor_flags_offset }(r3); + lwz r0, { out_of_water_ticks_offset }(r3); + lwz r5, { actor_flags_offset }(r3); + mr r4, r5; + srwi r5, r5, 14; // remove bits on the right of fluid count + andi r5, r5, 7; // remove bits on the left of fluid count + + // remove fluid counts + lis r6, 0xffff; + ori r6, r6, 0x3fff; + and r4, r4, r6; + cmpwi r14, 0; blt remove_floaty_jump; - li r5, 0x4000; // 4 << 12 => Fluid Count = 0b100 - or r4, r4, r5; + addi r5, r5, 1; // add 1 to fluid count + andi r5, r5, 7; // making sure we don't go past 3 bits (=> 0b111) + slwi r5, r5, 14; + or r4, r4, r5; // actor flags |= (value << 14) + cmpwi r0, 2; // check if we are in water + bne apply_underwater_floaty_jump; lis r5, 0x41a0; // 20.0 b apply_floaty_jump; remove_floaty_jump: - li r5, 0x7000; // 7 << 12 => Fluid Count = 0b111 - nor r5, r5, r5; // flip bits - and r4, r4, r5; // set Fluid Count to 0b000 + cmpwi r5, 0; + ble do_not_decrement_fluid_count; + addi r5, r5, -1; // subtract 1 to fluid count + do_not_decrement_fluid_count: + andi r5, r5, 7; // making sure we don't go past 3 bits (=> 0b111) + slwi r5, r5, 14; + or r4, r4, r5; // actor flags |= (value << 14) + cmpwi r0, 2; // check if we are in water + bne apply_underwater_floaty_jump; lis r5, 0; apply_floaty_jump: - stw r4, { actor_flags_offset }(r3); stw r5, { fluid_depth_offset }(r3); + apply_underwater_floaty_jump: + stw r4, { actor_flags_offset }(r3); b end_init_power_up; // check for max capacity From 8ff90f53231ffc363cb51e76f7c66763b71aad86 Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Sat, 5 Oct 2024 17:22:21 +0200 Subject: [PATCH 09/10] Changed parsing item type for special fn in add_modify_obj_patches.rs --- src/add_modify_obj_patches.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/add_modify_obj_patches.rs b/src/add_modify_obj_patches.rs index 80528e43..e3b3d29e 100644 --- a/src/add_modify_obj_patches.rs +++ b/src/add_modify_obj_patches.rs @@ -616,9 +616,15 @@ pub fn patch_add_special_fn( let default_unknown0 = "".to_string(); let unknown0 = config.unknown1.as_ref().unwrap_or(&default_unknown0); let unknown0 = string_to_cstr(unknown0.clone()); - let default_item_id = "Power Beam".to_string(); - let item_id = config.item_id.as_ref().unwrap_or(&default_item_id); - let item_id = PickupType::from_str(&item_id[..]) as u32; + let pickup_type = match config.item_id.as_ref() { + Some(item_id) => { + PickupType::from_str(item_id) + }, + None => { + PickupType::PowerBeam + }, + }; + let item_id = pickup_type as u32; macro_rules! new { () => { From 9541520527ca6006a9b32991076b30be69328111 Mon Sep 17 00:00:00 2001 From: UltiNaruto Date: Sun, 13 Oct 2024 15:15:25 +0200 Subject: [PATCH 10/10] Fixed remove floaty jump giving pseudo gravity when picked up underwater --- src/patches.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/patches.rs b/src/patches.rs index 1af4d66c..a28e2647 100644 --- a/src/patches.rs +++ b/src/patches.rs @@ -11391,8 +11391,15 @@ fn patch_dol( lis r5, 0x41a0; // 20.0 b apply_floaty_jump; remove_floaty_jump: + cmpwi r0, 2; // check if we are in water + bne do_not_decrement_fluid_count_more_than_one; cmpwi r5, 0; ble do_not_decrement_fluid_count; + b decrement_fluid_count; + do_not_decrement_fluid_count_more_than_one: + cmpwi r5, 1; + ble do_not_decrement_fluid_count; + decrement_fluid_count: addi r5, r5, -1; // subtract 1 to fluid count do_not_decrement_fluid_count: andi r5, r5, 7; // making sure we don't go past 3 bits (=> 0b111)