Skip to content

[Minor] Extended ExtendedAircraftMissions #1654

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

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 10 additions & 2 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,24 @@ VoicePickup= ; Sound entry
### Extended Aircraft Missions

- Aircraft will now be able to use waypoints.
- Aircraft can fly at a certain speed as much as possible, when the distance to the destination is less than half of `SlowdownDistance` or 8 cell distances divided by `ROT`, it will return to the airport. And now aircraft not have to fly directly above the airport before starting to descend.
- 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 addition, the actions of aircraft are also changed.
- `ExtendedAircraftActions.SmoothMoving` controls whether the aircraft will return to the airport when the distance to the destination is less than half of `SlowdownDistance` or its turning radius.
- `ExtendedAircraftActions.EarlyDescend` controls whether the aircraft not have to fly directly above the airport before starting to descend when the distance between the aircraft and the landing point is less than `SlowdownDistance` (also work for aircraft spawned by aircraft carriers).
- `ExtendedAircraftActions.RearApproach` controls whether the aircraft should start landing at the airport from the opposite direction of `LandingDir`.

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

[SOMEAIRCRAFT] ; AircraftType
ExtendedAircraftActions.SmoothMoving= ; boolean, default to [General] -> ExtendedAircraftMissions
ExtendedAircraftActions.EarlyDescend= ; boolean, default to [General] -> ExtendedAircraftMissions
ExtendedAircraftActions.RearApproach= ; boolean, default to [General] -> ExtendedAircraftMissions
```

### Fixed spawn distance & spawn height for airstrike / SpyPlane aircraft
Expand Down
186 changes: 165 additions & 21 deletions src/Ext/Aircraft/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,21 +283,27 @@ DEFINE_HOOK(0x414C0B, AircraftClass_ChronoSparkleDelay, 0x5)

DEFINE_HOOK(0x4CF31C, FlyLocomotionClass_FlightUpdate_LandingDir, 0x9)
{
enum { SkipGameCode = 0x4CF351 };
enum { SkipGameCode = 0x4CF3D0, SetSecondaryFacing = 0x4CF351 };

GET(FootClass**, pLinkedToPtr, ESI);
GET(FootClass** const, pFootPtr, ESI);
GET_STACK(IFlyControl* const, iFly, STACK_OFFSET(0x48, -0x38));
REF_STACK(unsigned int, dir, STACK_OFFSET(0x48, 0x8));

auto const pLinkedTo = *pLinkedToPtr;
const auto pFoot = *pFootPtr;
dir = 0;

if (pLinkedTo->CurrentMission == Mission::Enter || pLinkedTo->GetMapCoords() == CellClass::Coord2Cell(pLinkedTo->Locomotor->Destination()))
{
if (auto const pAircraft = abstract_cast<AircraftClass*>(pLinkedTo))
dir = DirStruct(AircraftExt::GetLandingDir(pAircraft)).Raw;
}
if (!iFly)
return SetSecondaryFacing;

return SkipGameCode;
if (iFly->Is_Locked())
return SkipGameCode;

if (const auto pAircraft = abstract_cast<AircraftClass*, true>(pFoot))
dir = DirStruct(AircraftExt::GetLandingDir(pAircraft)).Raw;
else
dir = (iFly->Landing_Direction() << 13);

return SetSecondaryFacing;
}

namespace SeparateAircraftTemp
Expand All @@ -314,11 +320,10 @@ DEFINE_HOOK(0x446F57, BuildingClass_GrandOpening_PoseDir_SetContext, 0x6)
return 0;
}

DirType _fastcall AircraftClass_PoseDir_Wrapper(AircraftClass* pThis)
DirType __fastcall AircraftClass_PoseDir_Wrapper(AircraftClass* pThis)
{
return AircraftExt::GetLandingDir(pThis, SeparateAircraftTemp::pBuilding);
}

DEFINE_FUNCTION_JUMP(CALL, 0x446F67, AircraftClass_PoseDir_Wrapper); // BuildingClass_GrandOpening

DEFINE_HOOK(0x443FC7, BuildingClass_ExitObject_PoseDir1, 0x8)
Expand All @@ -337,7 +342,10 @@ DEFINE_HOOK(0x44402E, BuildingClass_ExitObject_PoseDir2, 0x5)
GET(AircraftClass*, pAircraft, EBP);

auto dir = DirStruct(AircraftExt::GetLandingDir(pAircraft, pThis));
// pAircraft->PrimaryFacing.SetCurrent(dir);

if (RulesExt::Global()->ExtendedAircraftMissions)
pAircraft->PrimaryFacing.SetCurrent(dir);

pAircraft->SecondaryFacing.SetCurrent(dir);

return 0;
Expand Down Expand Up @@ -384,23 +392,31 @@ DEFINE_HOOK(0x416A0A, AircraftClass_Mission_Move_SmoothMoving, 0x5)
GET(AircraftClass* const, pThis, ESI);
GET(CoordStruct* const, pCoords, EAX);

if (!RulesExt::Global()->ExtendedAircraftMissions)
if (pThis->Team || pThis->Airstrike || pThis->Spawned)
return 0;

const auto pType = pThis->Type;

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

const auto extendedMissions = RulesExt::Global()->ExtendedAircraftMissions;

if (!TechnoTypeExt::ExtMap.Find(pType)->ExtendedAircraftActions_SmoothMoving.Get(extendedMissions))
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 waypoint
if (distance > std::max((pType->SlowdownDistance >> 1), (2048 / pType->ROT)))
const auto rotRadian = std::abs(pThis->PrimaryFacing.ROT.Raw * (Math::TwoPi / 65536)); // GetRadian<65536>() is an incorrect method
const auto turningRadius = rotRadian > 1e-10 ? static_cast<int>(pType->Speed / rotRadian) : 0;

if (distance > std::max((pType->SlowdownDistance / 2), turningRadius))
return (R->Origin() == 0x4168C7 ? ContinueMoving1 : ContinueMoving2);

// Try next planning waypoint first, then return to air base if it does not exist or cannot be taken
if (!pThis->TryNextPlanningTokenNode())
if (!extendedMissions || !pThis->TryNextPlanningTokenNode())
pThis->EnterIdleMode(false, true);

return EnterIdleAndReturn;
Expand All @@ -421,14 +437,142 @@ DEFINE_HOOK(0x4DDD66, FootClass_IsLandZoneClear_ReplaceHardcode, 0x6) // To avoi
return SkipGameCode;
}

DEFINE_HOOK(0x4CF408, FlyLocomotionClass_FlightUpdate_SetFlightLevel, 0x6) // Make aircraft not have to fly directly above the airport before starting to descend
DEFINE_HOOK(0x4CF190, FlyLocomotionClass_FlightUpdate_SetPrimaryFacing, 0x6) // Make aircraft not to fly directly to the airport before starting to land
{
enum { SkipGameCode = 0x4CF29A };

GET(IFlyControl* const, iFly, EAX);

if (!iFly || !iFly->Is_Locked())
{
GET(FootClass** const, pFootPtr, ESI);
GET(const int, distance, EBX);

const auto pFoot = *pFootPtr;
const auto pAircraft = abstract_cast<AircraftClass*, true>(pFoot);

// Rewrite vanilla implement
if (!pAircraft || !TechnoTypeExt::ExtMap.Find(pAircraft->Type)->ExtendedAircraftActions_RearApproach.Get(RulesExt::Global()->ExtendedAircraftMissions))
{
REF_STACK(const CoordStruct, destination, STACK_OFFSET(0x48, 0x8));

const auto footCoords = pFoot->GetCoords();
const auto desired = DirStruct(Math::atan2(footCoords.Y - destination.Y, destination.X - footCoords.X));

if (!iFly || !iFly->Is_Strafe() || distance > 768 // I don't know why it's 3 cells' length, but its vanilla, keep it
|| std::abs(static_cast<short>(static_cast<short>(desired.Raw) - static_cast<short>(pFoot->PrimaryFacing.Current().Raw))) >= 8192)
{
pFoot->PrimaryFacing.SetDesired(desired);
}
}
else
{
// No const because it also need to be used by SecondaryFacing
REF_STACK(CoordStruct, destination, STACK_OFFSET(0x48, 0x8));

const auto footCoords = pAircraft->GetCoords();
const auto landingDir = DirStruct(AircraftExt::GetLandingDir(pAircraft));

// Try to land from the rear
if (pAircraft->Destination && (pAircraft->DockNowHeadingTo == pAircraft->Destination || pAircraft->SpawnOwner == pAircraft->Destination))
{
const auto pType = pAircraft->Type;

// Like smooth moving
const auto rotRadian = std::abs(pAircraft->PrimaryFacing.ROT.Raw * (Math::TwoPi / 65536));
const auto turningRadius = rotRadian > 1e-10 ? static_cast<int>(pType->Speed / rotRadian) : 0;

// diameter = 2 * radius
const auto cellCounts = Math::max((pType->SlowdownDistance / 256), (turningRadius / 128));

// The direction of the airport
const auto currentDir = DirStruct(Math::atan2(footCoords.Y - destination.Y, destination.X - footCoords.X));

// Included angle's raw
const auto difference = static_cast<short>(static_cast<short>(currentDir.Raw) - static_cast<short>(landingDir.Raw));

// Land from this direction of the airport
const auto landingFace = landingDir.GetFacing<8>(4);
auto cellOffset = Unsorted::AdjacentCoord[landingFace];

// When the direction is opposite, moving to the side first, then automatically shorten based on the current distance
if (std::abs(difference) >= 12288) // 12288 -> 3/16 * 65536 (1/8 < 3/16 < 1/4, so the landing can begin at the appropriate location)
cellOffset = (cellOffset + Unsorted::AdjacentCoord[((difference > 0) ? (landingFace + 2) : (landingFace - 2)) & 7]) * cellCounts;
else // 724 -> 512√2
cellOffset *= Math::min(cellCounts, ((landingFace & 1) ? (distance / 724) : (distance / 512)));

// On the way back, increase the offset value of the destination so that it looks like a real airplane
destination.X += cellOffset.X;
destination.Y += cellOffset.Y;
}

if (footCoords.Y != destination.Y && footCoords.X != destination.X)
pAircraft->PrimaryFacing.SetDesired(DirStruct(Math::atan2(footCoords.Y - destination.Y, destination.X - footCoords.X)));
else
pAircraft->PrimaryFacing.SetDesired(landingDir);
}
}

return SkipGameCode;
}

DEFINE_HOOK(0x4CF3D0, FlyLocomotionClass_FlightUpdate_SetFlightLevel, 0x7) // Make aircraft not have to fly directly above the airport before starting to descend
{
enum { SkipGameCode = 0x4CF40E };
GET(FootClass** const, pFootPtr, ESI);

const auto pAircraft = abstract_cast<AircraftClass*, true>(*pFootPtr);

if (!pAircraft)
return 0;

const auto pType = pAircraft->Type;

// Ares hook
if (pType->HunterSeeker)
return 0;

GET(FlyLocomotionClass* const, pThis, EBP);
GET(TechnoTypeClass* const, pType, EAX);
if (!TechnoTypeExt::ExtMap.Find(pType)->ExtendedAircraftActions_EarlyDescend.Get(RulesExt::Global()->ExtendedAircraftMissions))
return 0;

enum { SkipGameCode = 0x4CF4D2 };

GET_STACK(FlyLocomotionClass* const, pThis, STACK_OFFSET(0x48, -0x28));
GET(const int, distance, EBX);

// Restore skipped code
R->EBP(pThis);

// Same as vanilla
if (pThis->IsElevating && distance < 768)
{
// Fast descent
const auto floorHeight = MapClass::Instance.GetCellFloorHeight(pThis->MovingDestination);
pThis->FlightLevel = pThis->MovingDestination.Z - floorHeight;

// Bug fix
if (MapClass::Instance.GetCellAt(pAircraft->Location)->ContainsBridge() && pThis->FlightLevel >= CellClass::BridgeHeight)
pThis->FlightLevel -= CellClass::BridgeHeight;

return SkipGameCode;
}

const auto flightLevel = pType->GetFlightLevel();

// Check returning actions
if (distance < pType->SlowdownDistance && pAircraft->Destination
&& (pAircraft->DockNowHeadingTo == pAircraft->Destination || pAircraft->SpawnOwner == pAircraft->Destination))
{
// Slow descent
const auto floorHeight = MapClass::Instance.GetCellFloorHeight(pThis->MovingDestination);
const auto destinationHeight = pThis->MovingDestination.Z - floorHeight + 1;
pThis->FlightLevel = static_cast<int>((flightLevel - destinationHeight) * (static_cast<double>(distance) / pType->SlowdownDistance)) + destinationHeight;
}
else
{
// Horizontal flight
pThis->FlightLevel = flightLevel;
}

R->ECX(RulesExt::Global()->ExtendedAircraftMissions && pThis->LinkedTo->CurrentMission == Mission::Enter || pType->IsDropship);
return SkipGameCode;
}

Expand Down
14 changes: 11 additions & 3 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,12 +564,16 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->Overload_ParticleSys.Read(exINI, pSection, "Overload.ParticleSys");
this->Overload_ParticleSysCount.Read(exINI, pSection, "Overload.ParticleSysCount");

this->FallingDownDamage.Read(exINI, pSection, "FallingDownDamage");
this->FallingDownDamage_Water.Read(exINI, pSection, "FallingDownDamage.Water");

this->Harvester_CanGuardArea.Read(exINI, pSection, "Harvester.CanGuardArea");
this->HarvesterScanAfterUnload.Read(exINI, pSection, "HarvesterScanAfterUnload");

this->ExtendedAircraftActions_SmoothMoving.Read(exINI, pSection, "ExtendedAircraftActions.SmoothMoving");
this->ExtendedAircraftActions_EarlyDescend.Read(exINI, pSection, "ExtendedAircraftActions.EarlyDescend");
this->ExtendedAircraftActions_RearApproach.Read(exINI, pSection, "ExtendedAircraftActions.RearApproach");

this->FallingDownDamage.Read(exINI, pSection, "FallingDownDamage");
this->FallingDownDamage_Water.Read(exINI, pSection, "FallingDownDamage.Water");

// Ares 0.2
this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius");

Expand Down Expand Up @@ -1112,6 +1116,10 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->Harvester_CanGuardArea)
.Process(this->HarvesterScanAfterUnload)

.Process(this->ExtendedAircraftActions_SmoothMoving)
.Process(this->ExtendedAircraftActions_EarlyDescend)
.Process(this->ExtendedAircraftActions_RearApproach)

.Process(this->FallingDownDamage)
.Process(this->FallingDownDamage_Water)
;
Expand Down
8 changes: 8 additions & 0 deletions src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ class TechnoTypeExt
Valueable<bool> Harvester_CanGuardArea;
Nullable<bool> HarvesterScanAfterUnload;

Nullable<bool> ExtendedAircraftActions_SmoothMoving;
Nullable<bool> ExtendedAircraftActions_EarlyDescend;
Nullable<bool> ExtendedAircraftActions_RearApproach;

Valueable<double> FallingDownDamage;
Nullable<double> FallingDownDamage_Water;

Expand Down Expand Up @@ -660,6 +664,10 @@ class TechnoTypeExt
, Harvester_CanGuardArea { false }
, HarvesterScanAfterUnload {}

, ExtendedAircraftActions_SmoothMoving {}
, ExtendedAircraftActions_EarlyDescend {}
, ExtendedAircraftActions_RearApproach {}

, FallingDownDamage { 1.0 }
, FallingDownDamage_Water {}
{ }
Expand Down