diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 2c5345e977..d455a4f221 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -604,6 +604,17 @@ DEFINE_HOOK(0x6A9789, StripClass_DrawStrip_NoGreyCameo, 0x6) return (!RulesExt::Global()->BuildingProductionQueue && pType->WhatAmI() == AbstractType::BuildingType && clicked) ? SkipGameCode : ContinueCheck; } +DEFINE_HOOK(0x6AA88D, StripClass_RecheckCameo_FindFactoryDehardCode, 0x6) +{ + GET(TechnoTypeClass* const, pType, EBX); + LEA_STACK(BuildCat*, pBuildCat, STACK_OFFSET(0x158, -0x158)); + + if (const auto pBuildingType = abstract_cast(pType)) + *pBuildCat = pBuildingType->BuildCat; + + return 0; +} + #pragma endregion #pragma region BarracksExitCell diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp index 72e8f99f14..395ec4c122 100644 --- a/src/Ext/House/Hooks.cpp +++ b/src/Ext/House/Hooks.cpp @@ -279,7 +279,7 @@ static inline bool CheckShouldDisableDefensesCameo(HouseClass* pHouse, TechnoTyp const auto BuildLimit = pBldType->BuildLimit; if (BuildLimit >= 0) - return BuildLimit - BuildingTypeExt::CountOwnedNowWithDeployOrUpgrade(pBldType, pHouse); + return BuildLimit - BuildingTypeExt::CountOwnedNowWithDeployOrUpgrade(pBldType, pHouse); else return -BuildLimit - pHouse->CountOwnedEver(pBldType); }; diff --git a/src/Ext/Sidebar/Body.cpp b/src/Ext/Sidebar/Body.cpp index 4674584781..1de4289841 100644 --- a/src/Ext/Sidebar/Body.cpp +++ b/src/Ext/Sidebar/Body.cpp @@ -1,5 +1,9 @@ #include "Body.h" +#include +#include +#include + std::unique_ptr SidebarExt::Data = nullptr; SHPStruct* SidebarExt::TabProducingProgress[4]; @@ -14,6 +18,59 @@ void SidebarExt::Remove(SidebarClass* pThis) Data = nullptr; } +// Reversed from Ares source code (In fact, it's the same as Vanilla). +// And compared to 0.A, it has been encapsulated. That's why here's such a simple way to make modifications. +bool __stdcall SidebarExt::AresTabCameo_RemoveCameo(BuildType* pItem) +{ + const auto pTechnoType = TechnoTypeClass::GetByTypeAndIndex(pItem->ItemType, pItem->ItemIndex); + const auto pCurrent = HouseClass::CurrentPlayer(); + + if (pTechnoType) + { + const auto pFactory = pTechnoType->FindFactory(true, false, false, pCurrent); + + if (pFactory && pFactory->Owner->CanBuild(pTechnoType, false, true) != CanBuildResult::Unbuildable) + return false; + } + else + { + const auto& supers = pCurrent->Supers; + + if (supers.ValidIndex(pItem->ItemIndex) && supers[pItem->ItemIndex]->IsPresent) + return false; + } + + if (pItem->CurrentFactory) + { + EventClass event = EventClass(pCurrent->ArrayIndex, EventType::Abandon, static_cast(pItem->ItemType), pItem->ItemIndex, pTechnoType && pTechnoType->Naval); + EventClass::AddEvent(event); + } + + if (pItem->ItemType == AbstractType::BuildingType || pItem->ItemType == AbstractType::Building) + { + DisplayClass::Instance->CurrentBuilding = nullptr; + DisplayClass::Instance->CurrentBuildingType = nullptr; + DisplayClass::Instance->CurrentBuildingOwnerArrayIndex = -1; + DisplayClass::Instance->SetActiveFoundation(nullptr); + } + + if (pTechnoType) + { + const auto absType = pTechnoType->WhatAmI(); + + // Here we make correction to the hardcoded BuildCat::DontCare + const auto buildCat = absType == AbstractType::BuildingType ? static_cast(pTechnoType)->BuildCat : BuildCat::DontCare; + + if (pCurrent->GetPrimaryFactory(absType, pTechnoType->Naval, buildCat)) + { + EventClass event = EventClass(pCurrent->ArrayIndex, EventType::AbandonAll, static_cast(pItem->ItemType), pItem->ItemIndex, pTechnoType->Naval); + EventClass::AddEvent(event); + } + } + + return true; +} + // ============================= // load / save diff --git a/src/Ext/Sidebar/Body.h b/src/Ext/Sidebar/Body.h index b3ccbfd9ac..33d6a757f2 100644 --- a/src/Ext/Sidebar/Body.h +++ b/src/Ext/Sidebar/Body.h @@ -58,4 +58,6 @@ class SidebarExt { Global()->InvalidatePointer(ptr, removed); } + + static bool __stdcall AresTabCameo_RemoveCameo(BuildType* pItem); }; diff --git a/src/Misc/Hooks.Ares.cpp b/src/Misc/Hooks.Ares.cpp index e4e0016b77..e53e0e3bc4 100644 --- a/src/Misc/Hooks.Ares.cpp +++ b/src/Misc/Hooks.Ares.cpp @@ -5,6 +5,8 @@ #include #include +#include + // In vanilla YR, game destroys building animations directly by calling constructor. // Ares changed this to call UnInit() which has a consequence of doing pointer invalidation on the AnimClass pointer. // This notably causes an issue with Grinder that restores ActiveAnim if the building is sold/destroyed while SpecialAnim is playing even if the building is gone or in limbo. @@ -46,6 +48,9 @@ void Apply_Ares3_0_Patches() Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x528C8, &Helpers::Alex::getCellSpreadItems); Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x5273A, &Helpers::Alex::getCellSpreadItems); + // Redirect Ares's RemoveCameo to our implementation: + Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x02BDD0, GET_OFFSET(SidebarExt::AresTabCameo_RemoveCameo)); + // InitialPayload creation: Patch::Apply_CALL6(AresHelper::AresBaseAddress + 0x43D5D, &CreateInitialPayload); } @@ -62,6 +67,9 @@ void Apply_Ares3_0p1_Patches() Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x53578, &Helpers::Alex::getCellSpreadItems); Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x533EA, &Helpers::Alex::getCellSpreadItems); + // Redirect Ares's RemoveCameo to our implementation: + Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x02C910, GET_OFFSET(SidebarExt::AresTabCameo_RemoveCameo)); + // InitialPayload creation: Patch::Apply_CALL6(AresHelper::AresBaseAddress + 0x4483D, &CreateInitialPayload); }