Help:Patching the motorcycle area checks: Difference between revisions

From ZeldaMods (Breath of the Wild)
Jump to navigation Jump to search
imported>Leoetlino
(Created page with "The Master Cycle Zero cannot be used in some areas. This article contains a list of checks that must be patched to remove usage restrictions. Most of them are located in a co...")
 
imported>Leoetlino
(remove a pretty much unused category)
 
(13 intermediate revisions by the same user not shown)
Line 1: Line 1:
The Master Cycle Zero cannot be used in some areas. This article contains a list of checks that must be patched to remove usage restrictions.
Because of arbitrary, hardcoded limitations, the Master Cycle Zero cannot be used in some areas. This article lists and documents checks that must be patched to remove usage restrictions.


Most of them are located in a component that is nicknamed the “ride manager” (or “RideMgr” when following BotW’s naming conventions) since it manages both horses{{check}} and the Master Cycle Zero.
Most of them are located in the MotorcycleMgr.


== Check ==
The game uses the current climate for the area check<ref>[https://github.com/leoetlino/botw-re-notes/blob/2c0b98c1d5e4cb35a11d4a3ea764323432a596ea/tools/check_master_cycle_ok_areas Extracted check from Switch 1.5.0]</ref>:
The game uses the current climate for the area check<ref>[https://github.com/leoetlino/botw-re-notes/blob/2c0b98c1d5e4cb35a11d4a3ea764323432a596ea/tools/check_master_cycle_ok_areas Extracted check from Switch 1.5.0]</ref>:


Line 25: Line 26:
* ✅ KorogForest
* ✅ KorogForest
* 🚫 GerudoDesertClimateLv2
* 🚫 GerudoDesertClimateLv2
<br>


== Disappearing when entering a blacklisted climate ==
== Patching ==
* On Switch, read the rest of the article to understand what to patch.
* On Wii U, the checks are implemented in the exact same way (which makes sense since it's the same codebase), but only a single instruction has to be patched because the Wii U compiler is not quite as good at inlining functions. Simply patch the instruction at <code>0x02A32A30</code> to <code>li r3, 1</code>.
 
== Technical details ==
''Note: the addresses below are only valid for Switch 1.5.0. Other versions (on Switch or Wii U) can be patched in a similar way, though.''
 
=== Disappearing when entering a blacklisted climate ===


Called from the Motorcycle AI root code.
Called from the Motorcycle AI root code.


<source lang="cpp">if ( v6 && !RideMgr::motorcycleCanBeUsed(v6, (float *)&v68, 0LL) )  // 0x71004ADF18
<source lang="cpp">if ( v6 && !MotorcycleMgr::motorcycleCanBeUsed(v6, (float *)&v68, 0LL) )  // 0x71004ADF18
   goto triggerDisappear;</source>
   goto triggerDisappear;</source>
Either change the condition to be always false, or change <code>RideMgr::motorcycleCanBeUsed</code> (preferred to avoid side effects):
Either change the condition to be always false, or change <code>MotorcycleMgr::motorcycleCanBeUsed</code> (preferred to avoid side effects):


<source lang="cpp">bool RideMgr::motorcycleCanBeUsed(RideMgr* this, Vec3 *positions, __int64 a3) // 0x7100679D10
<source lang="cpp">bool MotorcycleMgr::motorcycleCanBeUsed(MotorcycleMgr* this, Vec3 *positions, __int64 a3) // 0x7100679D10
{
{
   if ( (a3 || (a3 = ActorSystem::sInstance->field_C0) != 0)
   if ( (a3 || (a3 = ActorSystem::sInstance->field_C0) != 0)
Line 51: Line 58:
Patch:
Patch:


<pre>.text:0000007100679D84                AND            W0, W8, #1</pre>
<source lang="armasm">.text:0000007100679D84                AND            W0, W8, #1</source>
To:
To:


<pre>.text:0000007100679D84                 MOV             W0, #1</pre>
<source lang="armasm">.text:0000007100679D84                 MOV             W0, #1</source>
== Rune UI/sound effect ==
 
=== Rune UI/sound effect ===


Same climate based check as above, but done in a different RideMgr function. Just change:
Same climate based check as above, but done in a different MotorcycleMgr function. Just change:


<pre>.text:0000007100679650 TBNZ           W8, #0, loc_710067965C</pre>
<source lang="armasm">.text:0000007100679650 TBNZ            W8, #0, loc_710067965C</source>
to:
to:


<pre>.text:0000007100679650 B               loc_710067965C</pre>
<source lang="armasm">.text:0000007100679650 B               loc_710067965C</source>
== Refusing to spawn when in a blacklisted climate ==
=== Refusing to spawn when in a blacklisted climate ===


=== Check 1 ===
==== Check 1 ====


Deep in some RideMgr function, called from the PlayerNormal AI:
Deep in some MotorcycleMgr function, called from the PlayerNormal AI:


<source lang="cpp">else if ( !WorldMgr::sInstance
<source lang="cpp">else if ( !WorldMgr::sInstance
Line 78: Line 86:
Patch:
Patch:


<pre>.text:0000007100679BA8                 CBZ             X0, loc_7100679BD8</pre>
<source lang="armasm">.text:0000007100679BA8                 CBZ             X0, loc_7100679BD8</source>
to:
to:


<pre>.text:0000007100679BA8                 B               loc_7100679BD8</pre>
<source lang="armasm">.text:0000007100679BA8                 B               loc_7100679BD8</source>
so that the game always executes the code inside of the if block.
so that the game always executes the code inside of the if block.


=== Check 2 ===
==== Check 2 ====


Found in another RideMgr member function and also called from the PlayerNormal AI code.
Found in another MotorcycleMgr member function and also called from the PlayerNormal AI code.


<source lang="cpp">bool RideMgr::checkUsable2(RideMgr *this, __int64 a2)
<source lang="cpp">bool MotorcycleMgr::checkUsable2(MotorcycleMgr *this, __int64 a2)
{
{
   if ( this->someFlag == 1 )
   if ( this->someFlag == 1 )
     return 0;
     return 0;
   float* v4 = this->gap12C;
   float* v4 = this->gap12C;
   RideMgr::x_7(this, (float *)this->gap12C, (__int64)&this->field_134 + 4);
   MotorcycleMgr::x_7(this, (float *)this->gap12C, (__int64)&this->field_134 + 4);
   if ( a2 || (a2 = ActorSystem::sInstance->field_C0) != 0 )
   if ( a2 || (a2 = ActorSystem::sInstance->field_C0) != 0 )
   {
   {
Line 106: Line 114:
Change:
Change:


<pre>.text:000000710067B5FC                 CBZ             X0, loc_710067B62C</pre>
<source lang="armasm">.text:000000710067B5FC                 CBZ             X0, loc_710067B62C</source>
to:
to:


<pre>.text:000000710067B5FC                 B               loc_710067B62C</pre>
<source lang="armasm">.text:000000710067B5FC                 B               loc_710067B62C</source>
to make the game always return “true”.
to make the game always return “true”.


== Disappearing when being away from the motorcycle ==
=== Disappearing when being away from the motorcycle ===


This is checked by Motorcycle AI root code (at 0x71004ADF28).
This is checked by Motorcycle AI root code (at 0x71004ADF28).
Line 122: Line 130:
It is easy to change this condition to be always false.
It is easy to change this condition to be always false.


== Other checks<ref>0x7100678E80 in Switch 1.5.0</ref> ==
=== Other checks ===
 
There are a few more conditions for spawning the motorcycle<ref>0x7100678E80 in Switch 1.5.0</ref>; however patching them is not recommended as they are implemented for good reasons.


=== Actor check ===
==== Actor check ====


If the actor cannot be loaded, the game will not allow the player to use the motorcycle and will show the regular “You can’t use that here” message.
If the actor cannot be loaded, the game will not allow the player to use the motorcycle and will show the regular “You can’t use that here” message.


=== 6 or 7 other checks ===
==== 6 or 7 other checks ====


The RideMgr function at 0x7100678E80 calls 6 member functions that must all return true; otherwise, the motorcycle cannot be used.
The MotorcycleMgr function at 0x7100678E80 (which can be identified with a xref to "GameROMMotorcycle") calls 6 member functions that must all return true; otherwise, the motorcycle cannot be used.


The first 4 functions appear to be using the Havok AI NavMesh system. Something called “motorcycle shape cast” is used in the 3 other functions.
The first 4 functions appear to be using the Havok AI NavMesh system. Something called “motorcycle shape cast” is used in the 3 other functions.


[[Category:Guides]]
[[Category:Guides]]

Latest revision as of 06:53, 31 July 2019

Because of arbitrary, hardcoded limitations, the Master Cycle Zero cannot be used in some areas. This article lists and documents checks that must be patched to remove usage restrictions.

Most of them are located in the MotorcycleMgr.

Check

The game uses the current climate for the area check[1]:

  • ✅ HyrulePlainClimate
  • ✅ NorthHyrulePlainClimate
  • ✅ HebraFrostClimate
  • ✅ TabantaAridClimate
  • ✅ FrostClimate
  • 🚫 GerudoDesertClimate
  • ✅ GerudoPlateauClimate
  • ✅ EldinClimateLv0
  • ✅ TamourPlainClimate
  • ✅ ZoraTemperateClimate
  • ✅ HateruPlainClimate
  • ✅ FiloneSubtropicalClimate
  • ✅ SouthHateruHumidTemperateClimate
  • 🚫 EldinClimateLv1
  • 🚫 EldinClimateLv2
  • ✅ DarkWoodsClimat
  • ✅ LostWoodClimate
  • ✅ GerudoFrostClimate
  • ✅ KorogForest
  • 🚫 GerudoDesertClimateLv2

Patching

  • On Switch, read the rest of the article to understand what to patch.
  • On Wii U, the checks are implemented in the exact same way (which makes sense since it's the same codebase), but only a single instruction has to be patched because the Wii U compiler is not quite as good at inlining functions. Simply patch the instruction at 0x02A32A30 to li r3, 1.

Technical details

Note: the addresses below are only valid for Switch 1.5.0. Other versions (on Switch or Wii U) can be patched in a similar way, though.

Disappearing when entering a blacklisted climate

Called from the Motorcycle AI root code.

if ( v6 && !MotorcycleMgr::motorcycleCanBeUsed(v6, (float *)&v68, 0LL) )  // 0x71004ADF18
  goto triggerDisappear;

Either change the condition to be always false, or change MotorcycleMgr::motorcycleCanBeUsed (preferred to avoid side effects):

bool MotorcycleMgr::motorcycleCanBeUsed(MotorcycleMgr* this, Vec3 *positions, __int64 a3) // 0x7100679D10
{
  if ( (a3 || (a3 = ActorSystem::sInstance->field_C0) != 0)
    && (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a3 + 0x180LL))(a3) & 1 )
  {
    return false;
  }
  if ( WorldMgr::sInstance
         && (v5 = (u64)WorldMgr::getClimateNum(WorldMgr::sInstance, positions) - 5, v5 <= 14) )
  {
    return (0x3CFEu >> v5) & 1;
  }
  return true;
}

Patch:

.text:0000007100679D84                 AND             W0, W8, #1

To:

.text:0000007100679D84                 MOV             W0, #1

Rune UI/sound effect

Same climate based check as above, but done in a different MotorcycleMgr function. Just change:

.text:0000007100679650 TBNZ            W8, #0, loc_710067965C

to:

.text:0000007100679650 B               loc_710067965C

Refusing to spawn when in a blacklisted climate

Check 1

Deep in some MotorcycleMgr function, called from the PlayerNormal AI:

else if ( !WorldMgr::sInstance
       || (v7 = (unsigned __int64)WorldMgr::getClimateNum(WorldMgr::sInstance, (float *)v4) - 5, v7 > 0xE)
       || (0x3CFEu >> v7) & 1 )
{
  v8 = (*(__int64 (**)(void))(**(_QWORD **)(HavokAI::sInstance->navMeshQueryReqPool + 0x1A0LL) + 0x30LL))();
  ...
}

Patch:

.text:0000007100679BA8                 CBZ             X0, loc_7100679BD8

to:

.text:0000007100679BA8                 B               loc_7100679BD8

so that the game always executes the code inside of the if block.

Check 2

Found in another MotorcycleMgr member function and also called from the PlayerNormal AI code.

bool MotorcycleMgr::checkUsable2(MotorcycleMgr *this, __int64 a2)
{
  if ( this->someFlag == 1 )
    return 0;
  float* v4 = this->gap12C;
  MotorcycleMgr::x_7(this, (float *)this->gap12C, (__int64)&this->field_134 + 4);
  if ( a2 || (a2 = ActorSystem::sInstance->field_C0) != 0 )
  {
    if ( (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)a2 + 0x180LL))(a2) & 1 )
      return 0;
  }
  if ( !WorldMgr::sInstance )
    return 1;
  unsigned int climateNumMinus5 = WorldMgr::getClimateNum(WorldMgr::sInstance, v4) - 5;
  return ( climateNumMinus5 > 14 || (0x3CFEu >> climateNumMinus5) & 1 );
}

Change:

.text:000000710067B5FC                 CBZ             X0, loc_710067B62C

to:

.text:000000710067B5FC                 B               loc_710067B62C

to make the game always return “true”.

Disappearing when being away from the motorcycle

This is checked by Motorcycle AI root code (at 0x71004ADF28).

v40 = (float *)PlayerInfo::getPlayerPos(PlayerInfo::sInstance);
if ( (float)((float)((float)(*(float *)&v68 - *v40) * (float)(*(float *)&v68 - *v40))
           + (float)((float)(*(float *)&v70 - v40[2]) * (float)(*(float *)&v70 - v40[2]))) > (float)(**(float **)&this->_4abuf[8] * **(float **)&this->_4abuf[8]) )
  goto triggerDisappear;

It is easy to change this condition to be always false.

Other checks

There are a few more conditions for spawning the motorcycle[2]; however patching them is not recommended as they are implemented for good reasons.

Actor check

If the actor cannot be loaded, the game will not allow the player to use the motorcycle and will show the regular “You can’t use that here” message.

6 or 7 other checks

The MotorcycleMgr function at 0x7100678E80 (which can be identified with a xref to "GameROMMotorcycle") calls 6 member functions that must all return true; otherwise, the motorcycle cannot be used.

The first 4 functions appear to be using the Havok AI NavMesh system. Something called “motorcycle shape cast” is used in the 3 other functions.

  1. Extracted check from Switch 1.5.0
  2. 0x7100678E80 in Switch 1.5.0