Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Highly Customized] Optimize aircrafts actions #1366

Merged
merged 59 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
218fa3b
Core
CrimRecya Aug 25, 2024
a2d91cb
Docs
CrimRecya Aug 25, 2024
6157437
Fix a mistake
CrimRecya Aug 25, 2024
d855e1d
Optimize and remove some useless hook
CrimRecya Aug 25, 2024
05c8cc5
Remove useless check
CrimRecya Aug 25, 2024
a59956b
Fit with aircraft waypoint
CrimRecya Sep 1, 2024
5d8f4d9
Merge branch 'develop' into develop-aircraft
CrimRecya Sep 1, 2024
6481cc1
Merge branch 'develop' into develop-aircraft
CrimRecya Sep 15, 2024
77847a6
Add notes
CrimRecya Sep 16, 2024
ce00c49
Rename variable
CrimRecya Sep 16, 2024
e35eac3
Fix a mistake
CrimRecya Sep 18, 2024
d28d175
Merge branch 'develop' into develop-aircraft
CrimRecya Sep 18, 2024
8943f30
Update CREDITS.md
CrimRecya Sep 18, 2024
96c5482
Merge branch 'develop' into develop-aircraft
CrimRecya Sep 23, 2024
3f27ca2
Update
CrimRecya Sep 23, 2024
2b5dd08
Adapt to Kratos aircraft area guard
CrimRecya Sep 28, 2024
4e1d14c
Correct select target
CrimRecya Oct 10, 2024
a9635b5
Merge branch 'develop' into develop-aircraft
CrimRecya Oct 14, 2024
283f0a9
Small change
CrimRecya Oct 16, 2024
245b52d
Fix
CrimRecya Oct 26, 2024
9c07122
Merge branch 'develop' into develop-aircraft
CrimRecya Nov 7, 2024
1249ec7
Fix merge with const auto
CrimRecya Nov 7, 2024
5a631ec
General
CrimRecya Nov 8, 2024
71d4545
Merge branch 'develop' into develop-aircraft
CrimRecya Dec 10, 2024
48f5129
Fix aircrafts guard mission from hunt like
CrimRecya Dec 10, 2024
acee930
Smooth aircrafts moving actions and enable aircrafts waypoint
CrimRecya Dec 10, 2024
3f72785
Notes
CrimRecya Dec 10, 2024
4cfe4d8
Core
CrimRecya Dec 10, 2024
723463b
Fix `AirportBound=no` aircraft's moving action
CrimRecya Dec 10, 2024
02dd0bd
Fix `AirportBound=no` aircraft's stop action
CrimRecya Dec 10, 2024
11d8147
Merge branch 'develop' into develop-aircraft
CrimRecya Dec 10, 2024
5118bc0
Fix `AirportBound=no` aircraft's stop action
CrimRecya Dec 10, 2024
14dbf03
Fix a vanilla aircraft's no reloading bug and fix a typo in idle
CrimRecya Dec 10, 2024
841cb51
Use existing function
CrimRecya Dec 11, 2024
d553698
Prevent aircrafts from duplicatedly finding airport and overlapping
CrimRecya Dec 11, 2024
5028b7b
Resolve the vanilla bug from the root in PR #1449
CrimRecya Dec 11, 2024
445b1ac
Temporary fix the issue where 'AirportBound=no' aircrafts cannot acce…
CrimRecya Dec 12, 2024
a452dcc
Merge branch 'develop' into develop-aircraft
CrimRecya Dec 12, 2024
17325e2
Prevent aircrafts with `AirportBound=no` ignore stop command
CrimRecya Dec 12, 2024
a1c2169
Fix typo
CrimRecya Dec 12, 2024
6f94f86
Fix typo
CrimRecya Dec 12, 2024
4175056
Notes
CrimRecya Dec 12, 2024
5ea6f11
Aircrafts with team will not smooth their route, because of the scrip…
CrimRecya Dec 14, 2024
464cca9
Fix a typo that the aircraft can receive the stop command when it is …
CrimRecya Dec 17, 2024
d1542fb
Fix a typo that the aircraft can receive the stop command when it is …
CrimRecya Dec 17, 2024
d4a4250
Fix a problem that aircraft will keep losing its target and searching…
CrimRecya Dec 17, 2024
396f982
Rename `ExtendedAircraftMissions` and fix doc typo
CrimRecya Dec 26, 2024
c3cf57f
Remove useless
CrimRecya Dec 26, 2024
92b2d8c
More readable
CrimRecya Dec 26, 2024
f15b1b8
Merge branch 'develop-stopfix' into develop-aircraft
CrimRecya Dec 26, 2024
5a7fb09
Delete duplicated hooks
CrimRecya Dec 26, 2024
9b188f5
Merge branch 'develop' into develop-aircraft
CrimRecya Dec 26, 2024
7670926
Merge branch 'develop' into develop-aircraft
CrimRecya Dec 27, 2024
83376da
Update YRpp
CrimRecya Dec 27, 2024
d7f98de
Update YRpp and codes comments
CrimRecya Dec 28, 2024
5f508b8
Change an implementation
CrimRecya Dec 28, 2024
673927b
Change a check function
CrimRecya Dec 28, 2024
becf4a6
Doc and spelling/style improvements
Metadorius Dec 27, 2024
6d91e3a
Merge pull request #2 from Metadorius/feature/aircraft-fixes
CrimRecya Dec 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ This page lists all the individual contributions to the project by their author.
- Allow to change the speed of gas particles
- **CrimRecya**
- Fix `LimboKill` not working reliably
- Allow using waypoints, area guard and attack move with aircraft
- Fix `Stop` command not working so well in some cases
- **Ollerus**
- Build limit group enhancement
- Customizable rocker amplitude
Expand Down
2 changes: 1 addition & 1 deletion YRpp
20 changes: 20 additions & 0 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
- `<Player @ X>` can now be used as owner for pre-placed objects on skirmish and multiplayer maps.
- Follower vehicle index for preplaced vehicles in maps is now explicitly constrained to `[Units]` list in map files and is no longer thrown off by vehicles that could not be created or created vehicles having other vehicles as initial passengers.
- Drive/Jumpjet/Ship/Teleport locomotor did not power on when it is un-piggybacked bugfix
- Stop command (`[S]` by default) behavior is now more correct:
- Jumpjets no longer fall into a state of standing by idly.
- Technos are no longer unable to stop the attack move mission.
- Aircraft no longer find airport twice and overlap.
- Aircraft no longer briefly pause in the air before returning.
- Aircraft with `AirportBound=no` continue moving forward.
- Unit `Speed` setting now accepts floating-point values. Internally parsed values are clamped down to maximum of 100, multiplied by 256 and divided by 100, the result (which at this point is converted to an integer) then clamped down to maximum of 255 giving effective internal speed value range of 0 to 255, e.g leptons traveled per game frame.
- Subterranean movement now benefits from speed multipliers from all sources such as veterancy, AttachEffect etc.

Expand Down Expand Up @@ -228,6 +234,20 @@ In `rulesmd.ini`:
LandingDir= ; Direction type (integers from 0-255). Accepts negative values as a special case.
```

### Extended Aircraft Missions

- Aircraft will now be able to use waypoints.
- When a `guard` command (`[G]` by default) is issued, the aircraft will search for targets around the current location and return immediately when target is not found, target is destroyed or ammos are depleted.
- If the target is destroyed but ammos are not depleted yet, it will also return because the aircraft's command is one-time.
- When an `attack move` command (`[Ctrl]+[Shift]`) is issued, the aircraft will move towards the destination and search for nearby targets on the route for attack. Once ammo is depleted or the destination is reached, it will return.
- If the automatically selected target is destroyed but ammo is not depleted yet during the process, the aircraft will continue flying to the destination.

In `rulesmd.ini`:
```ini
[General]
ExtendedAircraftMissions=false ; boolean
```

## Animations

### Animation weapon and damage settings
Expand Down
2 changes: 2 additions & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ New:
- Option for Warhead to remove all shield types at once (by Starkku)
- Allow customizing voxel light source position (by Kerbiter, Morton, based on knowledge of thomassnedon)
- Option to fix voxel light source being offset and incorrectly tilting on slopes (by Kerbiter)
- Allow using waypoints, area guard and attack move with aircraft (by CrimRecya)
- AI superweapon delay timer customization (by Starkku)
- Disabling `MultipleFactory` bonus from specific BuildingType (by Starkku)
- Customizable ChronoSphere teleport delays for units (by Starkku)
Expand Down Expand Up @@ -552,6 +553,7 @@ Vanilla fixes:
- Fixed objects with ally target and `AttackFriendlies=true` having their target reset every frame, particularly AI-owned buildings (by Starkku)
- Follower vehicle index for preplaced vehicles in maps is now explicitly constrained to `[Units]` list in map files and is no longer thrown off by vehicles that could not be created or created vehicles having other vehicles as initial passengers (by Starkku)
- Drive/Jumpjet/Ship/Teleport locomotor did not power on when it is un-piggybacked bugfix (by tyuah8)
- Fix `Stop` command not working so well in some cases (by CrimRecya)
- Subterranean movement now benefits from speed multipliers from all sources such as veterancy, AttachEffect etc. (by Starkku)

Phobos fixes:
Expand Down
173 changes: 173 additions & 0 deletions src/Ext/Aircraft/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,179 @@ DEFINE_HOOK(0x415EEE, AircraftClass_Fire_KickOutPassengers, 0x6)
return SkipKickOutPassengers;
}

// Aircraft mission hard code are all disposable that no ammo, target died or arrived destination all will call the aircraft return airbase
#pragma region ExtendedAircraftMissions

// Waypoint: enable and smooth moving action
bool __fastcall AircraftTypeClass_CanUseWaypoint(AircraftTypeClass* pThis)
{
return RulesExt::Global()->ExtendedAircraftMissions.Get();
}
DEFINE_JUMP(VTABLE, 0x7E2908, GET_OFFSET(AircraftTypeClass_CanUseWaypoint))

// KickOut: skip useless tether
DEFINE_JUMP(LJMP, 0x444021, 0x44402E)

// Move: smooth the planning paths and returning route
DEFINE_HOOK_AGAIN(0x4168C7, AircraftClass_Mission_Move_SmoothMoving, 0x5)
DEFINE_HOOK(0x416A0A, AircraftClass_Mission_Move_SmoothMoving, 0x5)
{
enum { EnterIdleAndReturn = 0x416AC0, ContinueMoving1 = 0x416908, ContinueMoving2 = 0x416A47 };

GET(AircraftClass* const, pThis, ESI);
GET(CoordStruct* const, pCoords, EAX);

if (!RulesExt::Global()->ExtendedAircraftMissions)
return 0;

const auto pType = pThis->Type;

if (!pType->AirportBound || pThis->Team || pThis->Airstrike || pThis->Spawned)
return 0;

const int distance = Game::F2I(Point2D { pCoords->X, pCoords->Y }.DistanceFrom(Point2D { pThis->Location.X, pThis->Location.Y }));

// When the horizontal distance between the aircraft and its destination is greater than half of its deceleration distance
// or its turning radius, continue to move forward, otherwise return to airbase or execute the next planning path
if (distance > std::max((pType->SlowdownDistance >> 1), (2048 / pType->ROT)))
return (R->Origin() == 0x4168C7 ? ContinueMoving1 : ContinueMoving2);

pThis->EnterIdleMode(false, true);
Copy link
Contributor

@chaserli chaserli Dec 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example an easy but nasty way to "avoid" this is to add remaining waypoints check before enteridlemode. But it's probably not correct.

Copy link
Contributor Author

@CrimRecya CrimRecya Dec 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally bool __thiscall EnterIdleMode(TechnoClass *this, bool construction, bool nextPlanningPath) should have handled the next waypoint, but I don't understand why it didn't take effect. I'll check it carefully.


return EnterIdleAndReturn;
}

// AreaGuard: return when no ammo or first target died
DEFINE_HOOK_AGAIN(0x41A982, AircraftClass_Mission_AreaGuard, 0x6)
DEFINE_HOOK(0x41A96C, AircraftClass_Mission_AreaGuard, 0x6)
{
enum { SkipGameCode = 0x41A97A };

GET(AircraftClass* const, pThis, ESI);

if (RulesExt::Global()->ExtendedAircraftMissions && !pThis->Team && pThis->Ammo && pThis->IsArmed())
{
auto coords = pThis->GetCoords();

if (pThis->TargetAndEstimateDamage(coords, ThreatType::Area))
{
pThis->QueueMission(Mission::Attack, false);
return SkipGameCode;
}
}

return 0;
}

// AttackMove: return when no ammo or arrived destination
bool __fastcall AircraftTypeClass_CanAttackMove(AircraftTypeClass* pThis)
{
return RulesExt::Global()->ExtendedAircraftMissions.Get();
}
DEFINE_JUMP(VTABLE, 0x7E290C, GET_OFFSET(AircraftTypeClass_CanAttackMove))

DEFINE_HOOK(0x6FA68B, TechnoClass_Update_AttackMovePaused, 0xA) // To make aircrafts not search for targets while resting at the airport, this is designed to adapt to loop waypoint
{
enum { SkipGameCode = 0x6FA6F5 };

GET(TechnoClass* const, pThis, ESI);

const bool skip = RulesExt::Global()->ExtendedAircraftMissions
&& pThis->WhatAmI() == AbstractType::Aircraft
&& (!pThis->Ammo || !pThis->IsInAir());

return skip ? SkipGameCode : 0;
}

DEFINE_HOOK(0x4DF3BA, FootClass_UpdateAttackMove_AircraftHoldAttackMoveTarget1, 0x6) // When it have MegaDestination
{
enum { LoseTarget = 0x4DF3D3, HoldTarget = 0x4DF4AB };

GET(FootClass* const, pThis, ESI);

// The aircraft is constantly moving, which may cause its target to constantly enter and leave its range, so it is fixed to hold the target.
if (RulesExt::Global()->ExtendedAircraftMissions && pThis->WhatAmI() == AbstractType::Aircraft)
return HoldTarget;

return pThis->InAuxiliarySearchRange(pThis->Target) ? HoldTarget : LoseTarget;
}

DEFINE_HOOK(0x4DF42A, FootClass_UpdateAttackMove_AircraftHoldAttackMoveTarget2, 0x6) // When it have MegaTarget
{
enum { ContinueCheck = 0x4DF462, HoldTarget = 0x4DF4AB };

GET(FootClass* const, pThis, ESI);

// Although if the target selected by CS is an object rather than cell.
return (RulesExt::Global()->ExtendedAircraftMissions && pThis->WhatAmI() == AbstractType::Aircraft) ? HoldTarget : ContinueCheck;
}

DEFINE_HOOK(0x418CD1, AircraftClass_Mission_Attack_ContinueFlyToDestination, 0x6)
{
enum { Continue = 0x418C43, Return = 0x418CE8 };

GET(AircraftClass* const, pThis, ESI);

if (!pThis->Target)
{
if (!RulesExt::Global()->ExtendedAircraftMissions || !pThis->MegaMissionIsAttackMove() || !pThis->MegaDestination)
return Continue;

pThis->SetDestination(pThis->MegaDestination, false);
pThis->QueueMission(Mission::Move, true);
pThis->HaveAttackMoveTarget = false;
}
else
{
pThis->MissionStatus = 1;
}

R->EAX(1);
return Return;
}

// Idle: clear the target if no ammo
DEFINE_HOOK(0x414D4D, AircraftClass_Update_ClearTargetIfNoAmmo, 0x6)
{
enum { ClearTarget = 0x414D3F };

GET(AircraftClass* const, pThis, ESI);

if (RulesExt::Global()->ExtendedAircraftMissions && !pThis->Ammo && !pThis->Airstrike && !pThis->Spawned)
{
if (!SessionClass::IsCampaign()) // To avoid AI's aircrafts team repeatedly attempting to attack the target when no ammo
{
if (const auto pTeam = pThis->Team)
pTeam->LiberateMember(pThis);
}

return ClearTarget;
}

return 0;
}

// Stop: clear the mega mission and return to airbase immediately
// (StopEventFix's DEFINE_HOOK(0x4C75DA, EventClass_RespondToEvent_Stop, 0x6) in Hooks.BugFixes.cpp)

// GreatestThreat: for all the mission that should let the aircraft auto select a target
AbstractClass* __fastcall AircraftClass_GreatestThreat(AircraftClass* pThis, void* _, ThreatType threatType, CoordStruct* pSelectCoords, bool onlyTargetHouseEnemy)
{
if (RulesExt::Global()->ExtendedAircraftMissions)
{
if (const auto pPrimaryWeapon = pThis->GetWeapon(0)->WeaponType)
threatType |= pPrimaryWeapon->AllowedThreats();

if (const auto pSecondaryWeapon = pThis->GetWeapon(1)->WeaponType)
threatType |= pSecondaryWeapon->AllowedThreats();
}

return pThis->FootClass::GreatestThreat(threatType, pSelectCoords, onlyTargetHouseEnemy);
}
DEFINE_JUMP(VTABLE, 0x7E2668, GET_OFFSET(AircraftClass_GreatestThreat))

#pragma endregion

static __forceinline bool CheckSpyPlaneCameraCount(AircraftClass* pThis)
{
auto const pExt = TechnoExt::ExtMap.Find(pThis);
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
this->HeightShadowScaling = false;
this->HeightShadowScaling_MinScale.Read(exINI, GameStrings::AudioVisual, "HeightShadowScaling.MinScale");

this->ExtendedAircraftMissions.Read(exINI, GameStrings::General, "ExtendedAircraftMissions");

this->AllowParallelAIQueues.Read(exINI, "GlobalControls", "AllowParallelAIQueues");
this->ForbidParallelAIQueues_Aircraft.Read(exINI, "GlobalControls", "ForbidParallelAIQueues.Aircraft");
this->ForbidParallelAIQueues_Building.Read(exINI, "GlobalControls", "ForbidParallelAIQueues.Building");
Expand Down Expand Up @@ -330,6 +332,7 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->AirShadowBaseScale_log)
.Process(this->HeightShadowScaling)
.Process(this->HeightShadowScaling_MinScale)
.Process(this->ExtendedAircraftMissions)
.Process(this->AllowParallelAIQueues)
.Process(this->ForbidParallelAIQueues_Aircraft)
.Process(this->ForbidParallelAIQueues_Building)
Expand Down
4 changes: 4 additions & 0 deletions src/Ext/Rules/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class RulesExt
Valueable<double> HeightShadowScaling_MinScale;
double AirShadowBaseScale_log;

Valueable<bool> ExtendedAircraftMissions;

Valueable<bool> AllowParallelAIQueues;
Valueable<bool> ForbidParallelAIQueues_Aircraft;
Valueable<bool> ForbidParallelAIQueues_Building;
Expand Down Expand Up @@ -223,6 +225,8 @@ class RulesExt
, HeightShadowScaling_MinScale { 0.0 }
, AirShadowBaseScale_log { 0.693376137 }

, ExtendedAircraftMissions { false }

, AllowParallelAIQueues { true }
, ForbidParallelAIQueues_Aircraft { false }
, ForbidParallelAIQueues_Building { false }
Expand Down
72 changes: 72 additions & 0 deletions src/Misc/Hooks.BugFixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,78 @@ DEFINE_HOOK(0x743664, UnitClass_ReadFromINI_Follower3, 0x6)

#pragma endregion

#pragma region StopEventFix

DEFINE_HOOK(0x4C75DA, EventClass_RespondToEvent_Stop, 0x6)
{
enum { SkipGameCode = 0x4C762A };

GET(TechnoClass* const, pTechno, ESI);

// Check aircraft
const auto pAircraft = abstract_cast<AircraftClass*>(pTechno);
const bool commonAircraft = pAircraft && !pAircraft->Airstrike && !pAircraft->Spawned;
const auto mission = pTechno->CurrentMission;

// To avoid aircraft overlap by keep link if is returning or is in airport now.
if (!commonAircraft || (mission != Mission::Sleep && mission != Mission::Guard && mission != Mission::Enter)
|| !pAircraft->DockNowHeadingTo || (pAircraft->DockNowHeadingTo != pAircraft->GetNthLink()))
{
pTechno->SendToEachLink(RadioCommand::NotifyUnlink);
}

// To avoid technos being unable to stop in attack move mega mission
if (pTechno->MegaMissionIsAttackMove())
pTechno->ClearMegaMissionData();

// Clearing the current target should still be necessary for all technos
pTechno->SetTarget(nullptr);

if (commonAircraft)
{
if (pAircraft->Type->AirportBound)
{
// To avoid `AirportBound=yes` aircraft with ammo at low altitudes cannot correctly receive stop command and queue Mission::Guard with a `Destination`.
if (pAircraft->Ammo)
pTechno->SetDestination(nullptr, true);

// To avoid `AirportBound=yes` aircraft pausing in the air and let they returning to air base immediately.
if (!pAircraft->DockNowHeadingTo || (pAircraft->DockNowHeadingTo != pAircraft->GetNthLink())) // If the aircraft have no valid dock, try to find a new one
pAircraft->EnterIdleMode(false, true);
}
else if (pAircraft->Ammo)
{
// To avoid `AirportBound=no` aircraft ignoring the stop task or directly return to the airport.
if (pAircraft->Destination && static_cast<int>(CellClass::Coord2Cell(pAircraft->Destination->GetCoords()).DistanceFromSquared(pAircraft->GetMapCoords())) > 2) // If the aircraft is moving, find the forward cell then stop in it
pAircraft->SetDestination(pAircraft->GetCell()->GetNeighbourCell(static_cast<FacingType>(pAircraft->PrimaryFacing.Current().GetValue<3>())), true);
}
else if (!pAircraft->DockNowHeadingTo || (pAircraft->DockNowHeadingTo != pAircraft->GetNthLink()))
{
pAircraft->EnterIdleMode(false, true);
}
// Otherwise landing or idling normally without answering the stop command
}
else
{
// Check Jumpjets
const auto pFoot = abstract_cast<FootClass*>(pTechno);
const auto pJumpjetLoco = pFoot ? locomotion_cast<JumpjetLocomotionClass*>(pFoot->Locomotor) : nullptr;

// To avoid jumpjets falling into a state of standing idly by
if (!pJumpjetLoco) // If is not jumpjet, clear the destination is enough
pTechno->SetDestination(nullptr, true);
else if (!pFoot->Destination) // When in attack move and have had a target, the destination will be cleaned up, enter the guard mission can prevent the jumpjets stuck in a status of standing idly by
pTechno->QueueMission(Mission::Guard, true);
else if (static_cast<int>(CellClass::Coord2Cell(pFoot->Destination->GetCoords()).DistanceFromSquared(pTechno->GetMapCoords())) > 2) // If the jumpjet is moving, find the forward cell then stop in it
pTechno->SetDestination(pTechno->GetCell()->GetNeighbourCell(static_cast<FacingType>(pJumpjetLoco->LocomotionFacing.Current().GetValue<3>())), true);
// Otherwise landing or idling normally without answering the stop command
}

return SkipGameCode;
}

#pragma endregion

// This shouldn't be here
// Author: tyuah8
DEFINE_HOOK_AGAIN(0x4AF94D, EndPiggyback_PowerOn, 0x7) // Drive
Expand Down
Loading