diff --git a/CREDITS.md b/CREDITS.md index 46e7e196c5..b2bf3d5685 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -364,7 +364,7 @@ This page lists all the individual contributions to the project by their author. - `TurretOffset` support for SHP vehicles - Customizable rocker amplitude - Customizable wake anim - - Initial effort on optimization for crates' random distribution + - Initial effort on optimization for crates' random distribution - **Fryone** - Customizable ElectricBolt Arcs - Sound entry on unit's creation @@ -384,6 +384,7 @@ This page lists all the individual contributions to the project by their author. - Enhanced Straight trajectory - Enable Building Production Queue - Fix for sidebar not updating queued unit numbers when on hold + - Grey cameo preview and cameo overlays - **Ollerus** - Build limit group enhancement - Customizable rocker amplitude diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 9d7cc80e0f..180f3e5f30 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -397,6 +397,49 @@ In `rulesmd.ini`: MissingCameo=XXICON.SHP ; filename - including the .shp/.pcx extension ``` +### Show cameo when unbuildable + +- A setting that allows you to preview information. This feature can be used as before, playing "new construction options" and clearing the specific production queue when prerequisites loss. + - `Cameo.AlwaysExist` controls whether you can see the cameo when the prerequisite have not satisfied (`TechnoLevel`, `Owner` & `Cameo.RequiredHouses`, `RequiredHouses`, `ForbiddenHouses`, `FactoryOwners`, `StolenTech`, `SecretLab` and `RequiredTheaters`, etc should be satisfied, if the `Cameo.OverrideTechnos` is met, it will override the `Owner` & `Cameo.RequiredHouses` conditions). Defaults to `[AudioVisual]` -> `Cameo.AlwaysExist`. + - `Cameo.RequiredHouses` determines whether to add a condition together with `Owner` for `Cameo.AlwaysExist` check when the value is not empty. Suitable for situations where cameo is no need to be modified in games. + - `Cameo.OverrideTechnos` determines whether the cameo can also be displayed when you own one of these technos when the value is not empty. Suitable for situations where cameo need to be dynamically modified in games. + - `ShowBuildingStatistics` controls whether the number of buildings of this type that you currently own needs to be displayed in the upper left corner of the building cameo (requires the cameo exist). + - `Cameo.ShouldCount` controls whether this type of building need to count if `ShowBuildingStatistics=true`. Default to check if building's own `BuildCat` is not `Combat` or `BuildLimit` is set. + - `Cameo.OverlayShapes` controls the drawn image file. + - `Cameo.OverlayFrames` controls which frame in `Cameo.OverlayShapes` to draw in four different situations: currently owned this building type, can automatically build this building, grey cameo and have its prerequisite, grey cameo but have no prerequisite (The second situation requires `AutoBuilding` to be true, the last situation requires `Cameo.AlwaysExist` to be true). When set to a negative number, it means that there is no need to draw under the corresponding conditions. + - `Cameo.OverlayPalette` the color palette used when drawing `Cameo.OverlayShapes`. + - The `UIDescription.Unbuildable` is like `UIDescription`, but this only appearing when the techno is truly unbuildable. + +In `ra2md.ini`: +```ini +[Phobos] +ShowBuildingStatistics=false ; boolean +``` + +In `rulesmd.ini`: +```ini +[AudioVisual] +Cameo.AlwaysExist=false ; boolean +Cameo.OverlayShapes=pips.shp ; filename - including the .shp extension +Cameo.OverlayFrames= ; integer - owned this building, can automatically build, grey and have its prerequisite, grey but have no prerequisite +Cameo.OverlayPalette=palette.pal ; filename - including the .pal extension + +[SOMETECHNO] ; TechnoType +Cameo.AlwaysExist= ; boolean +Cameo.RequiredHouses= ; list of house types +Cameo.OverrideTechnos= ; List of TechnoTypes +UIDescription.Unbuildable= ; CSF entry key + +[SOMEBUILDING] ; BuildingType +Cameo.ShouldCount= ; boolean +``` + +In `artmd.ini`: +```ini +[SOMETECHNO] ; TechnoType +GreyCameoPCX= ; PCX filename - including the .pcx extension +``` + ### Harvester counter ![image](_static/images/harvestercounter-01.gif) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 1e06935086..a64c094556 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -319,6 +319,7 @@ New: - Custom exit cell for infantry factory (by Starkku) - Option for vehicles to keep target when issued move command (by Starkku) - Skip anim delay for burst fire (by TaranDahl) +- Grey cameo preview and cameo overlays (by CrimRecya) Vanilla fixes: - Aircraft will now behave as expected according to it's `MovementZone` and `SpeedType` when moving onto different surfaces. In particular, this fixes erratic behavior when vanilla aircraft is ordered to move onto water surface and instead the movement order changes to a shore nearby (by CrimRecya) diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index cbd7e26c1a..60c157c6bf 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -1,8 +1,13 @@ #include "Body.h" -#include +#include +#include + #include +#include +#include #include +#include BuildingTypeExt::ExtContainer BuildingTypeExt::ExtMap; @@ -61,19 +66,6 @@ int BuildingTypeExt::GetEnhancedPower(BuildingClass* pBuilding, HouseClass* pHou return static_cast(std::round(pBuilding->GetPowerOutput() * fFactor)) + nAmount; } -int BuildingTypeExt::CountOwnedNowWithDeployOrUpgrade(BuildingTypeClass* pType, HouseClass* pHouse) -{ - const auto upgrades = BuildingTypeExt::GetUpgradesAmount(pType, pHouse); - - if (upgrades != -1) - return upgrades; - - if (const auto pUndeploy = pType->UndeploysInto) - return pHouse->CountOwnedNow(pType) + pHouse->CountOwnedNow(pUndeploy); - - return pHouse->CountOwnedNow(pType); -} - int BuildingTypeExt::GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* pHouse) // not including producing upgrades { int result = 0; @@ -163,6 +155,8 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->SellBuildupLength.Read(exINI, pSection, "SellBuildupLength"); this->IsDestroyableObstacle.Read(exINI, pSection, "IsDestroyableObstacle"); + this->Cameo_ShouldCount.Read(exINI, pSection, "Cameo.ShouldCount"); + this->FactoryPlant_AllowTypes.Read(exINI, pSection, "FactoryPlant.AllowTypes"); this->FactoryPlant_DisallowTypes.Read(exINI, pSection, "FactoryPlant.DisallowTypes"); @@ -289,6 +283,7 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) .Process(this->ConsideredVehicle) .Process(this->ZShapePointMove_OnBuildup) .Process(this->SellBuildupLength) + .Process(this->Cameo_ShouldCount) .Process(this->AircraftDockingDirs) .Process(this->FactoryPlant_AllowTypes) .Process(this->FactoryPlant_DisallowTypes) diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 46133c2020..dcda13b03d 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,8 @@ class BuildingTypeExt Valueable SellBuildupLength; Valueable IsDestroyableObstacle; + Nullable Cameo_ShouldCount; + Valueable IsAnimDelayedBurst; std::vector> AircraftDockingDirs; @@ -119,6 +122,7 @@ class BuildingTypeExt , ConsideredVehicle {} , ZShapePointMove_OnBuildup { false } , SellBuildupLength { 23 } + , Cameo_ShouldCount {} , AircraftDockingDirs {} , FactoryPlant_AllowTypes {} , FactoryPlant_DisallowTypes {} @@ -170,6 +174,5 @@ class BuildingTypeExt static int GetEnhancedPower(BuildingClass* pBuilding, HouseClass* pHouse); static bool CanUpgrade(BuildingClass* pBuilding, BuildingTypeClass* pUpgradeType, HouseClass* pUpgradeOwner); - static int CountOwnedNowWithDeployOrUpgrade(BuildingTypeClass* pBuilding, HouseClass* pHouse); static int GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* pHouse); }; diff --git a/src/Ext/BuildingType/Hooks.Upgrade.cpp b/src/Ext/BuildingType/Hooks.Upgrade.cpp index 000af33eb0..d4fc06883b 100644 --- a/src/Ext/BuildingType/Hooks.Upgrade.cpp +++ b/src/Ext/BuildingType/Hooks.Upgrade.cpp @@ -74,45 +74,46 @@ int BuildLimitRemaining(HouseClass const* const pHouse, BuildingTypeClass const* return -BuildLimit - pHouse->CountOwnedEver(pItem); } -int CheckBuildLimit(HouseClass const* const pHouse, BuildingTypeClass const* const pItem, bool const includeQueued) +CanBuildResult CheckBuildLimit(HouseClass const* const pHouse, BuildingTypeClass const* const pItem, bool const includeQueued) { - enum { NotReached = 1, ReachedPermanently = -1, ReachedTemporarily = 0 }; - int BuildLimit = pItem->BuildLimit; int Remaining = BuildLimitRemaining(pHouse, pItem); if (BuildLimit >= 0 && Remaining <= 0) - return (includeQueued && FactoryClass::FindByOwnerAndProduct(pHouse, pItem)) ? NotReached : ReachedPermanently; - - return Remaining > 0 ? NotReached : ReachedTemporarily; + return (includeQueued && FactoryClass::FindByOwnerAndProduct(pHouse, pItem)) ? CanBuildResult::Buildable : CanBuildResult::Unbuildable; + return Remaining > 0 ? CanBuildResult::Buildable : CanBuildResult::TemporarilyUnbuildable; } DEFINE_HOOK(0x4F8361, HouseClass_CanBuild_UpgradesInteraction, 0x5) { - GET(HouseClass const* const, pThis, ECX); - GET_STACK(TechnoTypeClass const* const, pItem, 0x4); - GET_STACK(bool, buildLimitOnly, 0x8); - GET_STACK(bool const, includeInProduction, 0xC); - GET(CanBuildResult const, resultOfAres, EAX); + GET(HouseClass* const, pThis, ECX); + GET_STACK(TechnoTypeClass* const, pItem, 0x4); + GET_STACK(const bool, buildLimitOnly, 0x8); + GET_STACK(const bool, includeInProduction, 0xC); + GET(CanBuildResult, canBuild, EAX); // resultOfAres - if (auto const pBuilding = abstract_cast(pItem)) + if (canBuild == CanBuildResult::Buildable) { - if (auto pBuildingExt = BuildingTypeExt::ExtMap.Find(pBuilding)) + if (auto const pBuilding = abstract_cast(pItem)) { - if (pBuildingExt->PowersUp_Buildings.size() > 0 && resultOfAres == CanBuildResult::Buildable) - R->EAX(CheckBuildLimit(pThis, pBuilding, includeInProduction)); + if (BuildingTypeExt::ExtMap.Find(pBuilding)->PowersUp_Buildings.size() > 0) + canBuild = CheckBuildLimit(pThis, pBuilding, includeInProduction); } } - if (resultOfAres == CanBuildResult::Buildable) + if (canBuild == CanBuildResult::Buildable) { - R->EAX(HouseExt::BuildLimitGroupCheck(pThis, pItem, buildLimitOnly, includeInProduction)); + canBuild = HouseExt::BuildLimitGroupCheck(pThis, pItem, buildLimitOnly, includeInProduction); if (HouseExt::ReachedBuildLimit(pThis, pItem, true)) - R->EAX(CanBuildResult::TemporarilyUnbuildable); + canBuild = CanBuildResult::TemporarilyUnbuildable; } + if (!buildLimitOnly && includeInProduction && pThis == HouseClass::CurrentPlayer()) // Eliminate any non-producible calls + canBuild = TechnoTypeExt::CheckAlwaysExistCameo(pItem, canBuild); + + R->EAX(canBuild); return 0; } diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index f0ffe3a93d..449d85e76b 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -3,8 +3,7 @@ #include #include #include - -#include +#include //Static init @@ -361,6 +360,181 @@ void HouseExt::GetAIChronoshiftSupers(HouseClass* pThis, SuperClass*& pSuperCSph } } +int HouseExt::CountOwnedPresentExt(HouseClass* pHouse, TechnoTypeClass* pTechnoType, bool upgrade, bool deploy) +{ + switch (pTechnoType->WhatAmI()) + { + case AbstractType::BuildingType: + return HouseExt::CountOwnedPresentWithDeployOrUpgrade(pHouse, static_cast(pTechnoType), upgrade, deploy); + case AbstractType::InfantryType: + return pHouse->CountOwnedAndPresent(static_cast(pTechnoType)); + case AbstractType::UnitType: + return HouseExt::CountOwnedPresentWithDeploy(pHouse, static_cast(pTechnoType), deploy); + case AbstractType::AircraftType: + return pHouse->CountOwnedAndPresent(static_cast(pTechnoType)); + default: + break; + } + + return 0; +} + +int HouseExt::CountOwnedPresentWithDeploy(HouseClass* pHouse, UnitTypeClass* pUnitType, bool deploy) +{ + auto count = pHouse->CountOwnedAndPresent(pUnitType); + + if (deploy && pUnitType->DeploysInto) + count += pHouse->CountOwnedAndPresent(pUnitType->DeploysInto); + + return count; +} + +int HouseExt::CountOwnedPresentWithDeployOrUpgrade(HouseClass* pHouse, BuildingTypeClass* pBuildingType, bool upgrade, bool deploy) +{ + auto count = pHouse->CountOwnedAndPresent(pBuildingType); + + if (deploy && pBuildingType->UndeploysInto) + count += pHouse->CountOwnedAndPresent(pBuildingType->UndeploysInto); + + if (!upgrade) + return count; + + const auto upgrades = BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse); + + if (upgrades != -1) + count += upgrades; + + return count; +} + +int HouseExt::CountOwnedNowWithDeployOrUpgrade(HouseClass* pHouse, BuildingTypeClass* pBuildingType, bool upgrade, bool deploy) +{ + auto count = pHouse->CountOwnedNow(pBuildingType); + + if (deploy && pBuildingType->UndeploysInto) + count += pHouse->CountOwnedNow(pBuildingType->UndeploysInto); + + if (!upgrade) + return count; + + const auto upgrades = BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse); + + if (upgrades != -1) + count += upgrades; + + return count; +} + +bool HouseExt::CheckOwnerBitfieldForCurrentPlayer(TechnoTypeClass* pType) +{ + const auto pScenarioExt = ScenarioExt::Global(); + DWORD baseBits = TechnoTypeExt::ExtMap.Find(pType)->Cameo_RequiredHouses & pType->GetOwners(); + baseBits &= (1u << HouseClass::CurrentPlayer->Type->FindParentCountryIndex()); + + if (!baseBits) + return false; + + bool result = false; + + switch (pType->WhatAmI()) + { + case AbstractType::Building: + case AbstractType::BuildingType: + { + result = pScenarioExt->OwnerBitfield_BuildingType & baseBits; + break; + } + case AbstractType::Infantry: + case AbstractType::InfantryType: + { + result = pScenarioExt->OwnerBitfield_InfantryType & baseBits; + break; + } + case AbstractType::Unit: + case AbstractType::UnitType: + { + if (!pType->Naval) + result = pScenarioExt->OwnerBitfield_VehicleType & baseBits; + else + result = pScenarioExt->OwnerBitfield_NavyType & baseBits; + + break; + } + case AbstractType::Aircraft: + case AbstractType::AircraftType: + { + result = pScenarioExt->OwnerBitfield_AircraftType & baseBits; + break; + } + default: + { + break; + } + } + + return result; +} + +void HouseExt::RecheckOwnerBitfieldForCurrentPlayer() +{ + const auto pScenarioExt = ScenarioExt::Global(); + pScenarioExt->OwnerBitfield_BuildingType = 0; + pScenarioExt->OwnerBitfield_InfantryType = 0; + pScenarioExt->OwnerBitfield_VehicleType = 0; + pScenarioExt->OwnerBitfield_NavyType = 0; + pScenarioExt->OwnerBitfield_AircraftType = 0; + + for (const auto& pBuilding : HouseClass::CurrentPlayer->Buildings) + { + const auto pBuildingType = pBuilding->Type; + + switch (pBuildingType->Factory) + { + case AbstractType::Building: + case AbstractType::BuildingType: + { + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pBuildingType); + DWORD baseBits = pTypeExt->Cameo_RequiredHouses & pBuildingType->GetOwners(); + pScenarioExt->OwnerBitfield_BuildingType |= baseBits; + break; + } + case AbstractType::Infantry: + case AbstractType::InfantryType: + { + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pBuildingType); + DWORD baseBits = pTypeExt->Cameo_RequiredHouses & pBuildingType->GetOwners(); + pScenarioExt->OwnerBitfield_InfantryType |= baseBits; + break; + } + case AbstractType::Unit: + case AbstractType::UnitType: + { + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pBuildingType); + DWORD baseBits = pTypeExt->Cameo_RequiredHouses & pBuildingType->GetOwners(); + + if (!pBuildingType->Naval) + pScenarioExt->OwnerBitfield_VehicleType |= baseBits; + else + pScenarioExt->OwnerBitfield_NavyType |= baseBits; + + break; + } + case AbstractType::Aircraft: + case AbstractType::AircraftType: + { + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pBuildingType); + DWORD baseBits = pTypeExt->Cameo_RequiredHouses & pBuildingType->GetOwners(); + pScenarioExt->OwnerBitfield_AircraftType |= baseBits; + break; + } + default: + { + break; + } + } + } +} + // Ares HouseClass* HouseExt::GetHouseKind(OwnerHouseKind const kind, bool const allowRandom, HouseClass* const pDefault, HouseClass* const pInvoker, HouseClass* const pVictim) { diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index 590dd3ce9c..96b964b348 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -181,4 +181,10 @@ class HouseExt static CanBuildResult BuildLimitGroupCheck(const HouseClass* pThis, const TechnoTypeClass* pItem, bool buildLimitOnly, bool includeQueued); static bool ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass* pType, bool ignoreQueued); + static int CountOwnedPresentExt(HouseClass* pHouse, TechnoTypeClass* pTechnoType, bool upgrade = false, bool deploy = false); + static int CountOwnedPresentWithDeploy(HouseClass* pHouse, UnitTypeClass* pUnitType, bool deploy = false); + static int CountOwnedPresentWithDeployOrUpgrade(HouseClass* pHouse, BuildingTypeClass* pBuildingType, bool upgrade = false, bool deploy = false); + static int CountOwnedNowWithDeployOrUpgrade(HouseClass* pHouse, BuildingTypeClass* pBuildingType, bool upgrade = true, bool deploy = true); + static bool CheckOwnerBitfieldForCurrentPlayer(TechnoTypeClass* pType); + static void RecheckOwnerBitfieldForCurrentPlayer(); }; diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp index e0ef7b7ffd..50af7ebad7 100644 --- a/src/Ext/House/Hooks.cpp +++ b/src/Ext/House/Hooks.cpp @@ -323,7 +323,7 @@ static inline bool CheckShouldDisableDefensesCameo(HouseClass* pHouse, TechnoTyp const auto BuildLimit = pBldType->BuildLimit; if (BuildLimit >= 0) - return BuildLimit - BuildingTypeExt::CountOwnedNowWithDeployOrUpgrade(pBldType, pHouse); + return BuildLimit - HouseExt::CountOwnedNowWithDeployOrUpgrade(pHouse, pBldType); else return -BuildLimit - pHouse->CountOwnedEver(pBldType); }; @@ -336,17 +336,62 @@ static inline bool CheckShouldDisableDefensesCameo(HouseClass* pHouse, TechnoTyp return false; } +static inline bool CheckShowGreyCameo(const HouseClass* const pHouse, const TechnoTypeClass* const pType, const int address) +{ + return (pHouse == HouseClass::CurrentPlayer + && (address == 0x6A5FED // Check redraw sidebar when techno loss + || address == 0x6A97EF // Draw sidebar cameos + || address == 0x6AB65B) // Prevent click sidebar cameo + && TechnoTypeExt::ExtMap.Find(pType)->IsGreyCameoForCurrentPlayer); +} + DEFINE_HOOK(0x50B669, HouseClass_ShouldDisableCameo_GreyCameo, 0x5) { - GET(HouseClass*, pThis, ECX); - GET_STACK(TechnoTypeClass*, pType, 0x4); - GET(bool, aresDisable, EAX); + GET(HouseClass* const, pThis, ECX); + GET_STACK(TechnoTypeClass* const, pType, 0x4); + GET(const bool, aresDisable, EAX); if (aresDisable || !pType) return 0; - if (CheckShouldDisableDefensesCameo(pThis, pType) || HouseExt::ReachedBuildLimit(pThis, pType, false)) + if (CheckShouldDisableDefensesCameo(pThis, pType) + || HouseExt::ReachedBuildLimit(pThis, pType, false) + || CheckShowGreyCameo(pThis, pType, *R->ESP())) + { R->EAX(true); + } + + return 0; +} + +DEFINE_HOOK(0x4F9286, HouseClass_Update_RecheckOwnerBitfield, 0x6) +{ + enum { SkipLoop = 0x4F92DD, StartLoop = 0x4F928C }; + + GET(const int, buildingCount, EBP); + + R->EBX(0); + + if (!buildingCount) + return SkipLoop; + + HouseExt::RecheckOwnerBitfieldForCurrentPlayer(); + return StartLoop; +} + +// All technos have Cameo_AlwaysExist=true need to change the EVA_NewConstructionOptions playing time +DEFINE_HOOK(0x6A640B, SideBarClass_AddCameo_DoNotPlayEVA, 0x5) +{ + enum { SkipPlaying = 0x6A641A }; + + GET(AbstractType, absType, ESI); + GET(int, idxType, EBP); + + if (const auto pType = ObjectTypeClass::GetTechnoType(absType, idxType)) + { + if (TechnoTypeExt::ExtMap.Find(pType)->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + return SkipPlaying; + } return 0; } diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index fccb225b16..6a7c556712 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -137,6 +137,10 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->HeightShadowScaling = false; this->HeightShadowScaling_MinScale.Read(exINI, GameStrings::AudioVisual, "HeightShadowScaling.MinScale"); + this->Cameo_AlwaysExist.Read(exINI, GameStrings::AudioVisual, "Cameo.AlwaysExist"); + this->Cameo_OverlayShapes.Read(exINI, GameStrings::AudioVisual, "Cameo.OverlayShapes"); + this->Cameo_OverlayFrames.Read(exINI, GameStrings::AudioVisual, "Cameo.OverlayFrames"); + this->Cameo_OverlayPalette.LoadFromINI(pINI, GameStrings::AudioVisual, "Cameo.OverlayPalette"); this->ExtendedAircraftMissions.Read(exINI, GameStrings::General, "ExtendedAircraftMissions"); this->BuildingProductionQueue.Read(exINI, GameStrings::General, "BuildingProductionQueue"); @@ -335,6 +339,10 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->AirShadowBaseScale_log) .Process(this->HeightShadowScaling) .Process(this->HeightShadowScaling_MinScale) + .Process(this->Cameo_AlwaysExist) + .Process(this->Cameo_OverlayShapes) + .Process(this->Cameo_OverlayFrames) + .Process(this->Cameo_OverlayPalette) .Process(this->ExtendedAircraftMissions) .Process(this->BuildingProductionQueue) .Process(this->AllowParallelAIQueues) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index a84fdd31a0..55f811aa4b 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -93,6 +93,10 @@ class RulesExt Valueable HeightShadowScaling_MinScale; double AirShadowBaseScale_log; + Valueable Cameo_AlwaysExist; + Valueable Cameo_OverlayShapes; + ValueableVector Cameo_OverlayFrames; + CustomPalette Cameo_OverlayPalette; Valueable ExtendedAircraftMissions; Valueable BuildingProductionQueue; @@ -229,6 +233,10 @@ class RulesExt , HeightShadowScaling_MinScale { 0.0 } , AirShadowBaseScale_log { 0.693376137 } + , Cameo_AlwaysExist { false } + , Cameo_OverlayShapes { FileSystem::PIPS_SHP } + , Cameo_OverlayFrames {} + , Cameo_OverlayPalette {} , ExtendedAircraftMissions { false } , BuildingProductionQueue { false } diff --git a/src/Ext/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index 53c8ef3985..d9ea438ecc 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -162,6 +162,11 @@ void ScenarioExt::ExtData::Serialize(T& Stm) .Process(this->BriefingTheme) .Process(this->AutoDeathObjects) .Process(this->TransportReloaders) + .Process(this->OwnerBitfield_BuildingType) + .Process(this->OwnerBitfield_InfantryType) + .Process(this->OwnerBitfield_VehicleType) + .Process(this->OwnerBitfield_NavyType) + .Process(this->OwnerBitfield_AircraftType) ; } diff --git a/src/Ext/Scenario/Body.h b/src/Ext/Scenario/Body.h index 3d296a5c5f..61a1682120 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -36,6 +36,12 @@ class ScenarioExt std::vector AutoDeathObjects; std::vector TransportReloaders; // Objects that can reload ammo in limbo + DWORD OwnerBitfield_BuildingType; + DWORD OwnerBitfield_InfantryType; + DWORD OwnerBitfield_VehicleType; + DWORD OwnerBitfield_NavyType; + DWORD OwnerBitfield_AircraftType; + ExtData(ScenarioClass* OwnerObject) : Extension(OwnerObject) , ShowBriefing { false } , BriefingTheme { -1 } @@ -43,6 +49,11 @@ class ScenarioExt , Variables { } , AutoDeathObjects {} , TransportReloaders {} + , OwnerBitfield_BuildingType { 0 } + , OwnerBitfield_InfantryType { 0 } + , OwnerBitfield_VehicleType { 0 } + , OwnerBitfield_NavyType { 0 } + , OwnerBitfield_AircraftType { 0 } { } void SetVariableToByID(bool bIsGlobal, int nIndex, char bState); diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index c81ec2a650..31b280543f 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -3,7 +3,13 @@ #include #include #include + #include +#include +#include +#include +#include +#include DEFINE_HOOK(0x6A593E, SidebarClass_InitForHouse_AdditionalFiles, 0x5) { @@ -97,3 +103,111 @@ DEFINE_HOOK(0x72FCB5, InitSideRectangles_CenterBackground, 0x5) return 0; } + +DEFINE_HOOK(0x4F92DD, HouseClass_Update_RedrawSidebarWhenRecheckTechTree, 0x5) +{ + SidebarClass::Instance->SidebarBackgroundNeedsRedraw = true; + return 0; +} + +DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) +{ + GET(const bool, greyCameo, EBX); + GET(const int, destX, ESI); + GET(const int, destY, EBP); + GET_STACK(const RectangleStruct, boundingRect, STACK_OFFSET(0x48C, -0x3E0)); + GET_STACK(TechnoTypeClass* const, pType, STACK_OFFSET(0x48C, -0x458)); + + const auto position = Point2D { destX + 30, destY + 24 }; + const auto pRulesExt = RulesExt::Global(); + const auto& frames = pRulesExt->Cameo_OverlayFrames; + const auto frameSize = frames.size(); + + if (greyCameo && frameSize > 2) // Only draw extras over grey cameos + { + auto frame = frames[2]; + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + if (frameSize > 3 && pTypeExt && pTypeExt->IsGreyCameoForCurrentPlayer) + { + if (const auto CameoPCX = pTypeExt->GreyCameoPCX.GetSurface()) + { + auto drawRect = RectangleStruct { destX, destY, 60, 48 }; + PCX::Instance->BlitToSurface(&drawRect, DSurface::Sidebar, CameoPCX); + } + + frame = frames[3]; + } + + if (frame >= 0) + { + DSurface::Sidebar->DrawSHP( + pRulesExt->Cameo_OverlayPalette.GetOrDefaultConvert(FileSystem::PALETTE_PAL), + pRulesExt->Cameo_OverlayShapes, + frame, + &position, + &boundingRect, + BlitterFlags(0x600), + 0, 0, + ZGradient::Ground, + 1000, 0, 0, 0, 0, 0); + } + } + + if (const auto pBuildingType = abstract_cast(pType)) // Only count owned buildings + { + const auto pTypeExt = BuildingTypeExt::ExtMap.Find(pBuildingType); +/* TODO if merge #1479 + if (Phobos::Config::AutoBuilding_Enable && frameSize > 1 && frames[1] >= 0 && !greyCameo && pTypeExt->AutoBuilding.Get(RulesExt::Global()->AutoBuilding)) + { + DSurface::Sidebar->DrawSHP( + pRulesExt->Cameo_OverlayPalette.GetOrDefaultConvert(FileSystem::PALETTE_PAL), + pRulesExt->Cameo_OverlayShapes, + frames[1], + &position, + &boundingRect, + BlitterFlags(0x600), + 0, 0, + ZGradient::Ground, + 1000, 0, 0, 0, 0, 0); + } +*/ + const bool statistics = Phobos::Config::ShowBuildingStatistics + && pTypeExt->Cameo_ShouldCount.Get(pBuildingType->BuildCat != BuildCat::Combat || pBuildingType->BuildLimit != INT_MAX); + + if ((frameSize && frames[0] >= 0) || statistics) + { + if (const auto count = HouseExt::CountOwnedPresentWithDeployOrUpgrade(HouseClass::CurrentPlayer(), pBuildingType, true)) + { + if (frameSize && frames[0] >= 0) + { + DSurface::Sidebar->DrawSHP( + pRulesExt->Cameo_OverlayPalette.GetOrDefaultConvert(FileSystem::PALETTE_PAL), + pRulesExt->Cameo_OverlayShapes, + frames[0], + &position, + &boundingRect, + BlitterFlags(0x600), + 0, 0, + ZGradient::Ground, + 1000, 0, 0, 0, 0, 0); + } + + if (statistics) + { + GET_STACK(RectangleStruct, surfaceRect, STACK_OFFSET(0x48C, -0x438)); + + const COLORREF color = Drawing::RGB_To_Int(Drawing::TooltipColor); + const TextPrintType printType = TextPrintType::Background | TextPrintType::FullShadow | TextPrintType::Point8; + auto textPosition = Point2D { destX, destY + 1 }; + + wchar_t text[0x20]; + swprintf_s(text, L"%d", count); + DSurface::Sidebar->DrawTextA(text, &surfaceRect, &textPosition, color, 0, printType); + } + } + } + } + + return 0; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 7b8385da3f..0799f68ec9 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -6,13 +6,16 @@ #include #include #include +#include #include #include #include #include +#include #include +#include TechnoTypeExt::ExtContainer TechnoTypeExt::ExtMap; @@ -258,6 +261,109 @@ TechnoClass* TechnoTypeExt::CreateUnit(TechnoTypeClass* pType, CoordStruct locat return nullptr; } +int __fastcall TechnoTypeExt::RequirementsMetExtraCheck(void* pAresHouseExt, void* _, TechnoTypeClass* pType) +{ + // Only with Ares will call this function, so skip sanity check. + const auto result = AresFunctions::RequirementsMet(pAresHouseExt, pType); + + if (*reinterpret_cast(pAresHouseExt) == HouseClass::CurrentPlayer()) + { + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + if (pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + pTypeExt->IsMetTheEssentialConditions = (result > 2); + } + + return result; +} + +CanBuildResult TechnoTypeExt::CheckAlwaysExistCameo(TechnoTypeClass* pType, CanBuildResult canBuild) +{ + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto ForceRedrawSidebar = [pType]() + { + const auto tabIndex = SidebarClass::GetObjectTabIdx(pType->WhatAmI(), pType->GetArrayIndex(), 0); + const auto pSidebar = SidebarClass::Instance(); + + if (tabIndex != pSidebar->ActiveTabIndex) + return; + + pSidebar->SidebarNeedsRedraw = true; + pSidebar->SidebarBackgroundNeedsRedraw = true; // Necessary + pSidebar->Tabs[tabIndex].NeedsRedraw = true; + pSidebar->RedrawSidebar(0); + }; + + if (canBuild == CanBuildResult::Unbuildable) + { + const auto pCurrent = HouseClass::CurrentPlayer(); + auto CheckOverrideTechnos = [pCurrent, pTypeExt]() + { + const auto& pAuxTypes = pTypeExt->Cameo_OverrideTechnos; + + if (pAuxTypes.size()) + { + for (const auto& pAuxType : pAuxTypes) + { + if (HouseExt::CountOwnedPresentExt(pCurrent, pAuxType, true, true)) + return true; + } + } + + return false; + }; + + if (pTypeExt->IsMetTheEssentialConditions && (CheckOverrideTechnos() || HouseExt::CheckOwnerBitfieldForCurrentPlayer(pType))) + { + if (!pTypeExt->IsGreyCameoForCurrentPlayer) + { + pTypeExt->IsGreyCameoForCurrentPlayer = true; + ForceRedrawSidebar(); + auto buildCat = BuildCat::DontCare; + + if (const auto pBldType = abstract_cast(pType)) + { + buildCat = pBldType->BuildCat; + const auto pDisplay = DisplayClass::Instance(); +/* const auto pCurType = abstract_cast(pDisplay->CurrentBuildingType); // TODO If merge #1479 + + if (!RulesExt::Global()->ExtendedBuildingPlacing || !pCurType || BuildingTypeExt::IsSameBuildingType(pBldType, pCurType))*/ + { + pDisplay->SetActiveFoundation(nullptr); + pDisplay->CurrentBuilding = nullptr; + pDisplay->CurrentBuildingType = nullptr; + pDisplay->CurrentBuildingOwnerArrayIndex = -1; + } + } + + if (pCurrent->GetPrimaryFactory(pType->WhatAmI(), pType->Naval, buildCat)) + { + const EventClass event + ( + pCurrent->ArrayIndex, + EventType::AbandonAll, + static_cast(pType->WhatAmI()), + pType->GetArrayIndex(), + pType->Naval + ); + EventClass::AddEvent(event); + } + } + + canBuild = CanBuildResult::TemporarilyUnbuildable; + } + } + else if (pTypeExt->IsGreyCameoForCurrentPlayer) + { + pTypeExt->IsGreyCameoForCurrentPlayer = false; + pTypeExt->IsGreyCameoAbandonedProduct = false; + VoxClass::Play(&Make_Global(0x83FA64)); // 0x83FA64 -> EVA_NewConstructionOptions + ForceRedrawSidebar(); + } + + return canBuild; +} + // ============================= // load / save @@ -454,6 +560,12 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->BuildLimitGroup_ExtraLimit_Nums.Read(exINI, pSection, "BuildLimitGroup.ExtraLimit.Nums"); this->BuildLimitGroup_ExtraLimit_MaxCount.Read(exINI, pSection, "BuildLimitGroup.ExtraLimit.MaxCount"); this->BuildLimitGroup_ExtraLimit_MaxNum.Read(exINI, pSection, "BuildLimitGroup.ExtraLimit.MaxNum"); + + this->Cameo_AlwaysExist.Read(exINI, pSection, "Cameo.AlwaysExist"); + this->Cameo_OverrideTechnos.Read(exINI, pSection, "Cameo.OverrideTechnos"); + this->Cameo_RequiredHouses = pINI->ReadHouseTypesList(pSection, "Cameo.RequiredHouses", this->Cameo_RequiredHouses); + this->UIDescription_Unbuildable.Read(exINI, pSection, "UIDescription.Unbuildable"); + this->Wake.Read(exINI, pSection, "Wake"); this->Wake_Grapple.Read(exINI, pSection, "Wake.Grapple"); this->Wake_Sinking.Read(exINI, pSection, "Wake.Sinking"); @@ -510,6 +622,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) INI_EX exArtINI(CCINIClass::INI_Art); auto pArtSection = pThis->ImageFile; + this->GreyCameoPCX.Read(&CCINIClass::INI_Art, pArtSection, "GreyCameoPCX"); + this->TurretOffset.Read(exArtINI, pArtSection, "TurretOffset"); this->TurretShadow.Read(exArtINI, pArtSection, "TurretShadow"); ValueableVector shadow_indices; @@ -830,6 +944,15 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->BuildLimitGroup_ExtraLimit_MaxCount) .Process(this->BuildLimitGroup_ExtraLimit_MaxNum) + .Process(this->Cameo_AlwaysExist) + .Process(this->Cameo_OverrideTechnos) + .Process(this->Cameo_RequiredHouses) + .Process(this->IsMetTheEssentialConditions) + .Process(this->IsGreyCameoForCurrentPlayer) + .Process(this->IsGreyCameoAbandonedProduct) + .Process(this->UIDescription_Unbuildable) + .Process(this->GreyCameoPCX) + .Process(this->Wake) .Process(this->Wake_Grapple) .Process(this->Wake_Sinking) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 139a98e6d1..0ffb7b6906 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -226,6 +226,15 @@ class TechnoTypeExt ValueableVector BuildLimitGroup_ExtraLimit_MaxCount; Valueable BuildLimitGroup_ExtraLimit_MaxNum; + Nullable Cameo_AlwaysExist; + ValueableVector Cameo_OverrideTechnos; + DWORD Cameo_RequiredHouses; + bool IsMetTheEssentialConditions; // Not read from ini + bool IsGreyCameoForCurrentPlayer; // Not read from ini + bool IsGreyCameoAbandonedProduct; // Not read from ini + Valueable UIDescription_Unbuildable; + PhobosPCXFile GreyCameoPCX; + Nullable Wake; Nullable Wake_Grapple; Nullable Wake_Sinking; @@ -453,6 +462,15 @@ class TechnoTypeExt , BuildLimitGroup_ExtraLimit_MaxCount {} , BuildLimitGroup_ExtraLimit_MaxNum { 0 } + , Cameo_AlwaysExist {} + , Cameo_OverrideTechnos {} + , Cameo_RequiredHouses { 0xFFFFFFFF } + , IsMetTheEssentialConditions { false } + , IsGreyCameoForCurrentPlayer { false } + , IsGreyCameoAbandonedProduct { true } + , UIDescription_Unbuildable {} + , GreyCameoPCX {} + , Wake { } , Wake_Grapple { } , Wake_Sinking { } @@ -501,6 +519,9 @@ class TechnoTypeExt TechnoClass* pInvoker = nullptr, HouseClass* pInvokerHouse = nullptr, AnimTypeClass* pSpawnAnimType = nullptr, int spawnHeight = -1, bool alwaysOnGround = false, bool checkPathfinding = false, bool parachuteIfInAir = false, Mission mission = Mission::Guard, Mission* missionAI = nullptr); + static int __fastcall RequirementsMetExtraCheck(void* pAresHouseExt, void* _, TechnoTypeClass* pType); + static CanBuildResult CheckAlwaysExistCameo(TechnoTypeClass* pType, CanBuildResult canBuild); + // Ares 0.A static const char* GetSelectionGroupID(ObjectTypeClass* pType); static bool HasSelectionGroupID(ObjectTypeClass* pType, const char* pID); diff --git a/src/Misc/Hooks.Ares.cpp b/src/Misc/Hooks.Ares.cpp index e53e0e3bc4..14fd6ce532 100644 --- a/src/Misc/Hooks.Ares.cpp +++ b/src/Misc/Hooks.Ares.cpp @@ -6,6 +6,7 @@ #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. @@ -48,6 +49,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 RequirementsMet to our implementation: + Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x021B40, GET_OFFSET(TechnoTypeExt::RequirementsMetExtraCheck)); + // Redirect Ares's RemoveCameo to our implementation: Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x02BDD0, GET_OFFSET(SidebarExt::AresTabCameo_RemoveCameo)); @@ -67,6 +71,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 RequirementsMet to our implementation: + Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x0225C0, GET_OFFSET(TechnoTypeExt::RequirementsMetExtraCheck)); + // Redirect Ares's RemoveCameo to our implementation: Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x02C910, GET_OFFSET(SidebarExt::AresTabCameo_RemoveCameo)); diff --git a/src/Misc/PhobosToolTip.cpp b/src/Misc/PhobosToolTip.cpp index 7599cc4a62..78567e567b 100644 --- a/src/Misc/PhobosToolTip.cpp +++ b/src/Misc/PhobosToolTip.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,13 @@ inline const wchar_t* PhobosToolTip::GetUIDescription(TechnoTypeExt::ExtData* pD : nullptr; } +inline const wchar_t* PhobosToolTip::GetUnbuildableUIDescription(TechnoTypeExt::ExtData* pData) const +{ + return Phobos::Config::ToolTipDescriptions && !pData->UIDescription_Unbuildable.Get().empty() + ? pData->UIDescription_Unbuildable.Get().Text + : nullptr; +} + inline const wchar_t* PhobosToolTip::GetUIDescription(SWTypeExt::ExtData* pData) const { return Phobos::Config::ToolTipDescriptions && !pData->UIDescription.Get().empty() @@ -158,6 +166,12 @@ void PhobosToolTip::HelpText_Techno(TechnoTypeClass* pType) if (auto pDesc = this->GetUIDescription(pData)) oss << L"\n" << pDesc; + if (pData->IsGreyCameoForCurrentPlayer) + { + if (auto pExDesc = this->GetUnbuildableUIDescription(pData)) + oss << L"\n" << pExDesc; + } + this->TextBuffer = oss.str(); } diff --git a/src/Misc/PhobosToolTip.h b/src/Misc/PhobosToolTip.h index 1d054751a2..c2dee79b26 100644 --- a/src/Misc/PhobosToolTip.h +++ b/src/Misc/PhobosToolTip.h @@ -20,6 +20,7 @@ class PhobosToolTip private: inline const wchar_t* GetUIDescription(TechnoTypeExt::ExtData* pData) const; + inline const wchar_t* GetUnbuildableUIDescription(TechnoTypeExt::ExtData* pData) const; inline const wchar_t* GetUIDescription(SWTypeExt::ExtData* pData) const; inline int GetBuildTime(TechnoTypeClass* pType) const; inline int GetPower(TechnoTypeClass* pType) const; diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 247eb46fee..e97577e034 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -43,6 +43,7 @@ bool Phobos::Config::ShowPlanningPath = false; bool Phobos::Config::ArtImageSwap = false; bool Phobos::Config::ShowPlacementPreview = false; bool Phobos::Config::DigitalDisplay_Enable = false; +bool Phobos::Config::ShowBuildingStatistics = false; bool Phobos::Config::RealTimeTimers = false; bool Phobos::Config::RealTimeTimers_Adaptive = false; int Phobos::Config::CampaignDefaultGameSpeed = 2; @@ -72,6 +73,7 @@ DEFINE_HOOK(0x5FACDF, OptionsClass_LoadSettings_LoadPhobosSettings, 0x5) Phobos::Config::RealTimeTimers = CCINIClass::INI_RA2MD->ReadBool("Phobos", "RealTimeTimers", false); Phobos::Config::RealTimeTimers_Adaptive = CCINIClass::INI_RA2MD->ReadBool("Phobos", "RealTimeTimers.Adaptive", false); Phobos::Config::DigitalDisplay_Enable = CCINIClass::INI_RA2MD->ReadBool("Phobos", "DigitalDisplay.Enable", false); + Phobos::Config::ShowBuildingStatistics = CCINIClass::INI_RA2MD->ReadBool("Phobos", "ShowBuildingStatistics", false); Phobos::Config::SaveGameOnScenarioStart = CCINIClass::INI_RA2MD->ReadBool("Phobos", "SaveGameOnScenarioStart", true); Phobos::Config::ShowBriefing = CCINIClass::INI_RA2MD->ReadBool("Phobos", "ShowBriefing", true); Phobos::Config::ShowPowerDelta = CCINIClass::INI_RA2MD->ReadBool("Phobos", "ShowPowerDelta", true); diff --git a/src/Phobos.h b/src/Phobos.h index da85fd883b..7c4c5eafa8 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -77,6 +77,7 @@ class Phobos static bool ShowPlacementPreview; static bool EnableBuildingPlacementPreview; static bool DigitalDisplay_Enable; + static bool ShowBuildingStatistics; static bool RealTimeTimers; static bool RealTimeTimers_Adaptive; static int CampaignDefaultGameSpeed; diff --git a/src/Utilities/AresAddressInit.cpp b/src/Utilities/AresAddressInit.cpp index 48c247c43d..f9e6ce357f 100644 --- a/src/Utilities/AresAddressInit.cpp +++ b/src/Utilities/AresAddressInit.cpp @@ -5,6 +5,7 @@ decltype(AresFunctions::ConvertTypeTo) AresFunctions::ConvertTypeTo = nullptr; decltype(AresFunctions::SpawnSurvivors) AresFunctions::SpawnSurvivors = nullptr; +decltype(AresFunctions::RequirementsMet) AresFunctions::RequirementsMet = nullptr; std::function AresFunctions::SWTypeExtMap_Find; void* AresFunctions::_SWTypeExtMap = nullptr; @@ -24,6 +25,8 @@ void AresFunctions::InitAres3_0() else NOTE_ARES_FUN(SpawnSurvivors, 0x464C0); + NOTE_ARES_FUN(RequirementsMet, 0x021FF0); + NOTE_ARES_FUN(_SWTypeExtMapFind, 0x57C70); NOTE_ARES_FUN(_SWTypeExtMap, 0xC1C54); SWTypeExtMap_Find = [](SuperWeaponTypeClass* swt) { return _SWTypeExtMapFind(_SWTypeExtMap, swt); }; @@ -44,6 +47,8 @@ void AresFunctions::InitAres3_0p1() else NOTE_ARES_FUN(SpawnSurvivors, 0x47030); + NOTE_ARES_FUN(RequirementsMet, 0x022A70); + NOTE_ARES_FUN(_SWTypeExtMapFind, 0x58900); NOTE_ARES_FUN(_SWTypeExtMap, 0xC2C50); SWTypeExtMap_Find = [](SuperWeaponTypeClass* swt) { return _SWTypeExtMapFind(_SWTypeExtMap, swt); }; diff --git a/src/Utilities/AresFunctions.h b/src/Utilities/AresFunctions.h index 9418cd3831..b260365c48 100644 --- a/src/Utilities/AresFunctions.h +++ b/src/Utilities/AresFunctions.h @@ -24,6 +24,9 @@ class AresFunctions static bool(__stdcall* ConvertTypeTo)(TechnoClass* pFoot, TechnoTypeClass* pConvertTo); static void(__stdcall* SpawnSurvivors)(FootClass* pThis, TechnoClass* pKiller, bool Select, bool IgnoreDefenses); + + static int(__thiscall* RequirementsMet)(void* pAresHouseExt, TechnoTypeClass* pType); + static std::function SWTypeExtMap_Find; private: static constexpr bool _maybe = false;