From e397601ccc42684a85b7fa178037d11fcba8fa5d Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Thu, 26 Dec 2024 18:33:53 +0800 Subject: [PATCH 01/16] Core --- CREDITS.md | 1 + docs/User-Interface.md | 39 +++++++++ docs/Whats-New.md | 1 + src/Ext/BuildingType/Body.cpp | 114 ++++++++++++++++++++++++- src/Ext/BuildingType/Body.h | 3 + src/Ext/BuildingType/Hooks.Upgrade.cpp | 32 ++++--- src/Ext/House/Hooks.cpp | 39 ++++++++- src/Ext/Rules/Body.cpp | 9 ++ src/Ext/Rules/Body.h | 10 +++ src/Ext/Scenario/Body.cpp | 1 + src/Ext/Scenario/Body.h | 3 + src/Ext/Sidebar/Hooks.cpp | 98 +++++++++++++++++++++ src/Ext/TechnoType/Body.cpp | 15 ++++ src/Ext/TechnoType/Body.h | 14 +++ src/Misc/PhobosToolTip.cpp | 19 +++++ src/Misc/PhobosToolTip.h | 1 + src/Phobos.INI.cpp | 2 + src/Phobos.h | 1 + 18 files changed, 386 insertions(+), 16 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 4c788fe55f..eb0ba427eb 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -372,6 +372,7 @@ 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 + - 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 2e1ad86969..ab7dc07174 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -367,6 +367,45 @@ 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`, `RequiredHouses` and `ForbiddenHouses` should be satisfied). Defaults to `[AudioVisual]` -> `Cameo.AlwaysExist`. + - `ShowBuildingStatistics` controls whether the number of buildings of this type that you currently own needs to be displayed in the upper right corner of the building cameo (requires the cameo exist). + - `Cameo.OverlayShapes` controls the drawn image file. + - `Cameo.OverlayFrames` controls which frame in `Cameo.OverlayShapes` to draw in three different situations: currently owned this building type, grey cameo and have its prerequisite, grey cameo but have no prerequisite (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`. + - If `Cameo.AuxTechnos` is not set, in addition to basic conditions, the grey cameo will only show when `AIBasePlanningSide` condition is satisfied. Otherwise, the grey cameo will only show when at least one of these types is owned by you or its `TechnoLevel`, `Owner`, `RequiredHouses`, `ForbiddenHouses`, `Cameo.AuxTechnos` (use `AIBasePlanningSide` if not set) and `Cameo.NegTechnos` (if set) conditions are satisfied. + - If `Cameo.NegTechnos` is set, the grey cameo will not show when you have a techno in one of these types. + - 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=-1,-1,-1 ; integer - owned this building, 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.AuxTechnos= ; List of TechnoTypes +Cameo.NegTechnos= ; List of TechnoTypes +UIDescription.Unbuildable= ; CSF entry key +``` + +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 37c9f39e2f..e5a5b93dc5 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -441,6 +441,7 @@ New: - Allow customizing extra tint intensity for Iron Curtain & Force Shield (by Starkku) - Option to enable parsing 8-bit RGB values from `[ColorAdd]` instead of RGB565 (by Starkku) - Customizing height and speed at which subterranean units travel (by Starkku) +- Grey cameo preview and cameo overlays (by CrimRecya) - Option for Warhead damage to penetrate Iron Curtain or Force Shield (by Starkku) - 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) diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 105956e045..3880f13b0e 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -1,8 +1,14 @@ #include "Body.h" -#include +#include +#include +#include + #include +#include +#include #include +#include BuildingTypeExt::ExtContainer BuildingTypeExt::ExtMap; @@ -97,6 +103,112 @@ int BuildingTypeExt::GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* return isUpgrade ? result : -1; } +bool BuildingTypeExt::ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const pTypeExt) +{ + const auto pType = pTypeExt->OwnerObject(); + const auto techLevel = pType->TechLevel; + + if (techLevel <= 0 || techLevel > Game::TechLevel) + return false; + + const auto pHouse = HouseClass::CurrentPlayer(); + + if (!pHouse->InOwners(pType)) + return false; + + if (!pHouse->InRequiredHouses(pType)) + return false; + + if (pHouse->InForbiddenHouses(pType)) + return false; + + const auto& pNegTypes = pTypeExt->Cameo_NegTechnos; + + if (pNegTypes.size()) + { + for (const auto& pNegType : pNegTypes) + { + if (pNegType && pHouse->CountOwnedAndPresent(pNegType)) + return false; + } + } + + const auto& pAuxTypes = pTypeExt->Cameo_AuxTechnos; + + if (!pAuxTypes.size()) + { + const auto sideIndex = pType->AIBasePlanningSide; + + return (sideIndex == -1 || sideIndex == pHouse->Type->SideIndex); + } + + for (const auto& pAuxType : pAuxTypes) + { + const auto pAuxTypeExt = TechnoTypeExt::ExtMap.Find(pAuxType); + + if (pAuxTypeExt && !pAuxTypeExt->CameoCheckMutex) + { + if (pHouse->CountOwnedAndPresent(pAuxType)) + return true; + + pAuxTypeExt->CameoCheckMutex = true; + const auto exist = BuildingTypeExt::ShouldExistGreyCameo(pAuxTypeExt); + pAuxTypeExt->CameoCheckMutex = false; + + if (exist) + return true; + } + } + + return false; +} + +// Check the cameo change +CanBuildResult BuildingTypeExt::CheckAlwaysExistCameo(const TechnoTypeClass* const pType, CanBuildResult canBuild) +{ + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + if (pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + { + auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; + + if (canBuild == CanBuildResult::Unbuildable) // Unbuildable + Satisfy basic limitations = Change it to TemporarilyUnbuildable + { + pTypeExt->CameoCheckMutex = true; + const auto exist = BuildingTypeExt::ShouldExistGreyCameo(pTypeExt); + pTypeExt->CameoCheckMutex = false; + + if (exist) + { + if (std::find(vec.begin(), vec.end(), pTypeExt) == vec.end()) // … + Not in the list = Need to add it into list + { + vec.push_back(pTypeExt); + SidebarClass::Instance->SidebarNeedsRepaint(); + const EventClass event + ( + HouseClass::CurrentPlayer->ArrayIndex, + EventType::AbandonAll, + static_cast(pType->WhatAmI()), + pType->GetArrayIndex(), + pType->Naval + ); + EventClass::AddEvent(event); + } + + canBuild = CanBuildResult::TemporarilyUnbuildable; + } + } + else if (std::find(vec.begin(), vec.end(), pTypeExt) != vec.end()) // Not Unbuildable + In the list = remove it from the list and play EVA + { + vec.erase(std::remove(vec.begin(), vec.end(), pTypeExt), vec.end()); + SidebarClass::Instance->SidebarNeedsRepaint(); + VoxClass::Play(&Make_Global(0x83FA64)); // 0x83FA64 -> EVA_NewConstructionOptions + } + } + + return canBuild; +} + void BuildingTypeExt::ExtData::Initialize() { } diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index f7b00ae5e4..6b3582879a 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -165,4 +166,6 @@ class BuildingTypeExt static int GetEnhancedPower(BuildingClass* pBuilding, HouseClass* pHouse); static bool CanUpgrade(BuildingClass* pBuilding, BuildingTypeClass* pUpgradeType, HouseClass* pUpgradeOwner); static int GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* pHouse); + static bool ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const pTypeExt); + static CanBuildResult CheckAlwaysExistCameo(const TechnoTypeClass* const pType, CanBuildResult canBuild); }; diff --git a/src/Ext/BuildingType/Hooks.Upgrade.cpp b/src/Ext/BuildingType/Hooks.Upgrade.cpp index 000af33eb0..5d8c32b5d6 100644 --- a/src/Ext/BuildingType/Hooks.Upgrade.cpp +++ b/src/Ext/BuildingType/Hooks.Upgrade.cpp @@ -74,18 +74,15 @@ 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) @@ -94,25 +91,32 @@ DEFINE_HOOK(0x4F8361, HouseClass_CanBuild_UpgradesInteraction, 0x5) GET_STACK(TechnoTypeClass const* const, pItem, 0x4); GET_STACK(bool, buildLimitOnly, 0x8); GET_STACK(bool const, includeInProduction, 0xC); - GET(CanBuildResult const, resultOfAres, EAX); + 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 (auto pBuildingExt = BuildingTypeExt::ExtMap.Find(pBuilding)) + { + if (pBuildingExt->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 to change the list safely + canBuild = BuildingTypeExt::CheckAlwaysExistCameo(pItem, canBuild); + + R->EAX(canBuild); return 0; } diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp index fa005f0ff1..e1ed06550f 100644 --- a/src/Ext/House/Hooks.cpp +++ b/src/Ext/House/Hooks.cpp @@ -257,7 +257,7 @@ DEFINE_HOOK(0x65E997, HouseClass_SendAirstrike_PlaceAircraft, 0x6) return result ? SkipGameCode : SkipGameCodeNoSuccess; } -DEFINE_HOOK(0x50B669, HouseClass_ShouldDisableCameo, 0x5) +DEFINE_HOOK(0x50B669, HouseClass_ShouldDisableCameo_GreyCameo, 0x5) { GET(HouseClass*, pThis, ECX); GET_STACK(TechnoTypeClass*, pType, 0x4); @@ -267,7 +267,44 @@ DEFINE_HOOK(0x50B669, HouseClass_ShouldDisableCameo, 0x5) return 0; if (HouseExt::ReachedBuildLimit(pThis, pType, false)) + { R->EAX(true); + } + else if (pThis == HouseClass::CurrentPlayer) + { + GET(int*, pAddress, ESP); + + if (*pAddress == 0x6A5FED || *pAddress == 0x6A97EF || *pAddress == 0x6AB65B) + { + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + // The types exist in the list means that they are not buildable now + if (pTypeExt && pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + { + auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; + + if (std::find(vec.begin(), vec.end(), pTypeExt) != vec.end()) + R->EAX(true); + } + } + } + + return 0; +} + +// 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 bc72f69570..f0779c6ed4 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -137,6 +137,11 @@ 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->AllowParallelAIQueues.Read(exINI, "GlobalControls", "AllowParallelAIQueues"); this->ForbidParallelAIQueues_Aircraft.Read(exINI, "GlobalControls", "ForbidParallelAIQueues.Aircraft"); this->ForbidParallelAIQueues_Building.Read(exINI, "GlobalControls", "ForbidParallelAIQueues.Building"); @@ -330,6 +335,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->AllowParallelAIQueues) .Process(this->ForbidParallelAIQueues_Aircraft) .Process(this->ForbidParallelAIQueues_Building) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index b2cce2d4cd..cf6d8b3405 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -93,6 +93,11 @@ class RulesExt Valueable HeightShadowScaling_MinScale; double AirShadowBaseScale_log; + Valueable Cameo_AlwaysExist; + Valueable Cameo_OverlayShapes; + Valueable> Cameo_OverlayFrames; + CustomPalette Cameo_OverlayPalette; + Valueable AllowParallelAIQueues; Valueable ForbidParallelAIQueues_Aircraft; Valueable ForbidParallelAIQueues_Building; @@ -223,6 +228,11 @@ class RulesExt , HeightShadowScaling_MinScale { 0.0 } , AirShadowBaseScale_log { 0.693376137 } + , Cameo_AlwaysExist { false } + , Cameo_OverlayShapes { FileSystem::PIPS_SHP } + , Cameo_OverlayFrames { { -1, -1, -1 } } + , Cameo_OverlayPalette {} + , AllowParallelAIQueues { true } , ForbidParallelAIQueues_Aircraft { false } , ForbidParallelAIQueues_Building { false } diff --git a/src/Ext/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index 53c8ef3985..e01cd05076 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -162,6 +162,7 @@ void ScenarioExt::ExtData::Serialize(T& Stm) .Process(this->BriefingTheme) .Process(this->AutoDeathObjects) .Process(this->TransportReloaders) + .Process(this->OwnedExistCameoTechnoTypes) ; } diff --git a/src/Ext/Scenario/Body.h b/src/Ext/Scenario/Body.h index 3d296a5c5f..38f0352b82 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -36,6 +36,8 @@ class ScenarioExt std::vector AutoDeathObjects; std::vector TransportReloaders; // Objects that can reload ammo in limbo + std::vector OwnedExistCameoTechnoTypes; + ExtData(ScenarioClass* OwnerObject) : Extension(OwnerObject) , ShowBriefing { false } , BriefingTheme { -1 } @@ -43,6 +45,7 @@ class ScenarioExt , Variables { } , AutoDeathObjects {} , TransportReloaders {} + , OwnedExistCameoTechnoTypes {} { } void SetVariableToByID(bool bIsGlobal, int nIndex, char bState); diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index c81ec2a650..4ddd519579 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,95 @@ DEFINE_HOOK(0x72FCB5, InitSideRectangles_CenterBackground, 0x5) 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.Get(); + + if (greyCameo) // Only draw extras over grey cameos + { + auto frame = frames.Y; + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + if (pTypeExt && pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + { + auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; + + if (std::find(vec.begin(), vec.end(), pTypeExt) != vec.end()) + { + if (const auto CameoPCX = pTypeExt->GreyCameoPCX.GetSurface()) + { + auto drawRect = RectangleStruct { destX, destY, 60, 48 }; + PCX::Instance->BlitToSurface(&drawRect, DSurface::Sidebar, CameoPCX); + } + + frame = frames.Z; + } + } + + 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); + } + } + + const auto pBuildingType = abstract_cast(pType); + + if (pBuildingType) // Only count owned buildings + { + const auto pHouse = HouseClass::CurrentPlayer(); + auto count = BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse); + + if (count == -1) + count = pHouse->CountOwnedAndPresent(pBuildingType); + + if (count > 0) + { + if (frames.X >= 0) + { + DSurface::Sidebar->DrawSHP( + pRulesExt->Cameo_OverlayPalette.GetOrDefaultConvert(FileSystem::PALETTE_PAL), + pRulesExt->Cameo_OverlayShapes, + frames.X, + &position, + &boundingRect, + BlitterFlags(0x600), + 0, 0, + ZGradient::Ground, + 1000, 0, 0, 0, 0, 0); + } + + if (Phobos::Config::ShowBuildingStatistics) + { + GET_STACK(RectangleStruct, surfaceRect, STACK_OFFSET(0x48C, -0x438)); + + const COLORREF color = Drawing::RGB_To_Int(Drawing::TooltipColor); + const TextPrintType printType = TextPrintType::Background | TextPrintType::Right | TextPrintType::FullShadow | TextPrintType::Point8; + auto textPosition = Point2D { destX + 60, 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 0091ceb22e..7060c6a1f7 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -454,6 +454,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_AuxTechnos.Read(exINI, pSection, "Cameo.AuxTechnos"); + this->Cameo_NegTechnos.Read(exINI, pSection, "Cameo.NegTechnos"); + 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"); @@ -505,6 +511,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; @@ -825,6 +833,13 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->BuildLimitGroup_ExtraLimit_MaxCount) .Process(this->BuildLimitGroup_ExtraLimit_MaxNum) + .Process(this->Cameo_AlwaysExist) + .Process(this->Cameo_AuxTechnos) + .Process(this->Cameo_NegTechnos) + .Process(this->CameoCheckMutex) + .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 3335acac05..542894645e 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -226,6 +226,13 @@ class TechnoTypeExt ValueableVector BuildLimitGroup_ExtraLimit_MaxCount; Valueable BuildLimitGroup_ExtraLimit_MaxNum; + Nullable Cameo_AlwaysExist; + ValueableVector Cameo_AuxTechnos; + ValueableVector Cameo_NegTechnos; + bool CameoCheckMutex; // Not read from ini + Valueable UIDescription_Unbuildable; + PhobosPCXFile GreyCameoPCX; + Nullable Wake; Nullable Wake_Grapple; Nullable Wake_Sinking; @@ -449,6 +456,13 @@ class TechnoTypeExt , BuildLimitGroup_ExtraLimit_MaxCount {} , BuildLimitGroup_ExtraLimit_MaxNum { 0 } + , Cameo_AlwaysExist {} + , Cameo_AuxTechnos {} + , Cameo_NegTechnos {} + , CameoCheckMutex { false } + , UIDescription_Unbuildable {} + , GreyCameoPCX {} + , Wake { } , Wake_Grapple { } , Wake_Sinking { } diff --git a/src/Misc/PhobosToolTip.cpp b/src/Misc/PhobosToolTip.cpp index 9e780a2aa7..f034e6a50d 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() @@ -142,6 +150,17 @@ void PhobosToolTip::HelpText_Techno(TechnoTypeClass* pType) if (auto pDesc = this->GetUIDescription(pData)) oss << L"\n" << pDesc; + if (pData->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + { + auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; + + if (std::find(vec.begin(), vec.end(), pData) != vec.end()) + { + 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 1f5d20345d..3640b042a8 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; @@ -70,6 +71,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 9ba1a63879..8dcd851633 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; From a139bbdf72fd6f23e03ccb8bbf632374457e2204 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Thu, 23 Jan 2025 16:36:05 +0800 Subject: [PATCH 02/16] Fit with building production queue --- src/Ext/Sidebar/Hooks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index 4ddd519579..2f0d39bfea 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -183,8 +183,8 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) GET_STACK(RectangleStruct, surfaceRect, STACK_OFFSET(0x48C, -0x438)); const COLORREF color = Drawing::RGB_To_Int(Drawing::TooltipColor); - const TextPrintType printType = TextPrintType::Background | TextPrintType::Right | TextPrintType::FullShadow | TextPrintType::Point8; - auto textPosition = Point2D { destX + 60, destY + 1 }; + 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); From b97e86f72edfc7aa6d8eebceec20cd14cad77bdb Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 24 Jan 2025 17:13:28 +0800 Subject: [PATCH 03/16] Fix that not count building upgrade --- src/Ext/BuildingType/Body.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 7ed78b1c9e..4f51d08912 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -131,6 +131,8 @@ bool BuildingTypeExt::ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const p { if (pNegType && pHouse->CountOwnedAndPresent(pNegType)) return false; + else if (pNegType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pNegType), pHouse)) + return false; } } @@ -151,6 +153,8 @@ bool BuildingTypeExt::ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const p { if (pHouse->CountOwnedAndPresent(pAuxType)) return true; + else if (pAuxType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pAuxType), pHouse)) + return true; pAuxTypeExt->CameoCheckMutex = true; const auto exist = BuildingTypeExt::ShouldExistGreyCameo(pAuxTypeExt); From 4a232d98977d476643616e8eb9a126ed5b9016ad Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 24 Jan 2025 23:24:55 +0800 Subject: [PATCH 04/16] Fix a typo --- src/Ext/BuildingType/Body.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 4f51d08912..6cda047fab 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -131,7 +131,7 @@ bool BuildingTypeExt::ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const p { if (pNegType && pHouse->CountOwnedAndPresent(pNegType)) return false; - else if (pNegType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pNegType), pHouse)) + else if (pNegType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pNegType), pHouse) > 0) return false; } } @@ -153,7 +153,7 @@ bool BuildingTypeExt::ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const p { if (pHouse->CountOwnedAndPresent(pAuxType)) return true; - else if (pAuxType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pAuxType), pHouse)) + else if (pAuxType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pAuxType), pHouse) > 0) return true; pAuxTypeExt->CameoCheckMutex = true; From fd521bc1bd929554a1b19729deacd76183cc3a46 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 24 Jan 2025 23:48:01 +0800 Subject: [PATCH 05/16] Doc --- docs/User-Interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 575798a636..cec9f80d2f 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -391,7 +391,7 @@ MissingCameo=XXICON.SHP ; filename - including the .shp/.pcx extension - 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`, `RequiredHouses` and `ForbiddenHouses` should be satisfied). Defaults to `[AudioVisual]` -> `Cameo.AlwaysExist`. - - `ShowBuildingStatistics` controls whether the number of buildings of this type that you currently own needs to be displayed in the upper right corner of the building cameo (requires the cameo exist). + - `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.OverlayShapes` controls the drawn image file. - `Cameo.OverlayFrames` controls which frame in `Cameo.OverlayShapes` to draw in three different situations: currently owned this building type, grey cameo and have its prerequisite, grey cameo but have no prerequisite (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`. From b7b58ef258048f01c54bf1b6c29ca3c0de224378 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 25 Jan 2025 01:17:05 +0800 Subject: [PATCH 06/16] New `Cameo.ShouldCount` --- docs/User-Interface.md | 4 ++++ src/Ext/BuildingType/Body.cpp | 3 +++ src/Ext/BuildingType/Body.h | 3 +++ src/Ext/Sidebar/Hooks.cpp | 3 ++- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index cec9f80d2f..ddd62579af 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -392,6 +392,7 @@ MissingCameo=XXICON.SHP ; filename - including the .shp/.pcx extension - 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`, `RequiredHouses` and `ForbiddenHouses` should be satisfied). Defaults to `[AudioVisual]` -> `Cameo.AlwaysExist`. - `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 not zero. - `Cameo.OverlayShapes` controls the drawn image file. - `Cameo.OverlayFrames` controls which frame in `Cameo.OverlayShapes` to draw in three different situations: currently owned this building type, grey cameo and have its prerequisite, grey cameo but have no prerequisite (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`. @@ -418,6 +419,9 @@ Cameo.AlwaysExist= ; boolean Cameo.AuxTechnos= ; List of TechnoTypes Cameo.NegTechnos= ; List of TechnoTypes UIDescription.Unbuildable= ; CSF entry key + +[SOMEBUILDING] ; BuildingType +Cameo.ShouldCount= ; boolean ``` In `artmd.ini`: diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 6cda047fab..57b0a59bd6 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -274,6 +274,8 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Units_RepairPercent.Read(exINI, pSection, "Units.RepairPercent"); this->Units_UseRepairCost.Read(exINI, pSection, "Units.UseRepairCost"); + this->Cameo_ShouldCount.Read(exINI, pSection, "Cameo.ShouldCount"); + this->NoBuildAreaOnBuildup.Read(exINI, pSection, "NoBuildAreaOnBuildup"); this->Adjacent_Allowed.Read(exINI, pSection, "Adjacent.Allowed"); this->Adjacent_Disallowed.Read(exINI, pSection, "Adjacent.Disallowed"); @@ -391,6 +393,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 c198342fa6..2f363d7a6c 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -65,6 +65,8 @@ class BuildingTypeExt Valueable SellBuildupLength; Valueable IsDestroyableObstacle; + Nullable Cameo_ShouldCount; + std::vector> AircraftDockingDirs; ValueableVector FactoryPlant_AllowTypes; @@ -118,6 +120,7 @@ class BuildingTypeExt , ConsideredVehicle {} , ZShapePointMove_OnBuildup { false } , SellBuildupLength { 23 } + , Cameo_ShouldCount {} , AircraftDockingDirs {} , FactoryPlant_AllowTypes {} , FactoryPlant_DisallowTypes {} diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index 2f0d39bfea..bc64f1bd7b 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -178,7 +178,8 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) 1000, 0, 0, 0, 0, 0); } - if (Phobos::Config::ShowBuildingStatistics) + if (Phobos::Config::ShowBuildingStatistics + && BuildingTypeExt::ExtMap.Find(pBuildingType)->Cameo_ShouldCount.Get(pBuildingType->BuildCat != BuildCat::Combat || pBuildingType->BuildLimit)) { GET_STACK(RectangleStruct, surfaceRect, STACK_OFFSET(0x48C, -0x438)); From b3744b1bf2cc3fbffd9d6cb58e0c6a89f70f7281 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 25 Jan 2025 16:09:14 +0800 Subject: [PATCH 07/16] Fix a typo --- docs/User-Interface.md | 2 +- src/Ext/Sidebar/Hooks.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index ddd62579af..91fc7f96af 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -392,7 +392,7 @@ MissingCameo=XXICON.SHP ; filename - including the .shp/.pcx extension - 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`, `RequiredHouses` and `ForbiddenHouses` should be satisfied). Defaults to `[AudioVisual]` -> `Cameo.AlwaysExist`. - `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 not zero. + - `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 three different situations: currently owned this building type, grey cameo and have its prerequisite, grey cameo but have no prerequisite (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`. diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index bc64f1bd7b..966c280bcb 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -179,7 +179,7 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) } if (Phobos::Config::ShowBuildingStatistics - && BuildingTypeExt::ExtMap.Find(pBuildingType)->Cameo_ShouldCount.Get(pBuildingType->BuildCat != BuildCat::Combat || pBuildingType->BuildLimit)) + && BuildingTypeExt::ExtMap.Find(pBuildingType)->Cameo_ShouldCount.Get(pBuildingType->BuildCat != BuildCat::Combat || pBuildingType->BuildLimit != INT_MAX)) { GET_STACK(RectangleStruct, surfaceRect, STACK_OFFSET(0x48C, -0x438)); From 204b00d059a3f9a74864f3079f849697e7ac5ab8 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 25 Jan 2025 22:01:03 +0800 Subject: [PATCH 08/16] Simplified --- src/Ext/BuildingType/Body.cpp | 4 ++-- src/Ext/Sidebar/Hooks.cpp | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index d9319f03d3..1e992ae1b7 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -142,7 +142,7 @@ bool BuildingTypeExt::ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const p { for (const auto& pNegType : pNegTypes) { - if (pNegType && pHouse->CountOwnedAndPresent(pNegType)) + if (pHouse->CountOwnedAndPresent(pNegType)) return false; else if (pNegType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pNegType), pHouse) > 0) return false; @@ -162,7 +162,7 @@ bool BuildingTypeExt::ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const p { const auto pAuxTypeExt = TechnoTypeExt::ExtMap.Find(pAuxType); - if (pAuxTypeExt && !pAuxTypeExt->CameoCheckMutex) + if (!pAuxTypeExt->CameoCheckMutex) { if (pHouse->CountOwnedAndPresent(pAuxType)) return true; diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index 966c280bcb..1b2310e1c2 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -156,13 +156,7 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) if (pBuildingType) // Only count owned buildings { - const auto pHouse = HouseClass::CurrentPlayer(); - auto count = BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse); - - if (count == -1) - count = pHouse->CountOwnedAndPresent(pBuildingType); - - if (count > 0) + if (const auto count = BuildingTypeExt::CountOwnedNowWithDeployOrUpgrade(pBuildingType, HouseClass::CurrentPlayer())) { if (frames.X >= 0) { From 08359ed8649a4aa003c39bca994a2bee3bafdfcd Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 27 Jan 2025 16:17:13 +0800 Subject: [PATCH 09/16] Not counting the producing --- src/Ext/Sidebar/Hooks.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index 1b2310e1c2..966c280bcb 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -156,7 +156,13 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) if (pBuildingType) // Only count owned buildings { - if (const auto count = BuildingTypeExt::CountOwnedNowWithDeployOrUpgrade(pBuildingType, HouseClass::CurrentPlayer())) + const auto pHouse = HouseClass::CurrentPlayer(); + auto count = BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse); + + if (count == -1) + count = pHouse->CountOwnedAndPresent(pBuildingType); + + if (count > 0) { if (frames.X >= 0) { From 93bcafa192a16c6dd333fe2f85498e10c6cb58f6 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 27 Jan 2025 20:00:23 +0800 Subject: [PATCH 10/16] Update sidebar --- src/Ext/Sidebar/Hooks.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index 966c280bcb..6f99d396ea 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -104,6 +104,12 @@ 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); From 32c90bded6f720ad15594d11a32c5f24e11b0fcd Mon Sep 17 00:00:00 2001 From: Noble Fish <89088785+DeathFishAtEase@users.noreply.github.com> Date: Mon, 3 Feb 2025 04:12:50 +0800 Subject: [PATCH 11/16] ")" --- docs/Fixed-or-Improved-Logics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index d39932ba2b..5606546923 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1124,7 +1124,7 @@ Palette= ; filename - excluding .pal extension and three-character the - Instead of showing at 1 point of HP left, TerrainTypes switch to damaged frames once their health reaches `[AudioVisual]` -> `ConditionYellow.Terrain` percentage of their maximum health. Defaults to `ConditionYellow` if not set. - In addition, TerrainTypes can now show 'crumbling' animation after their health has reached zero and before they are deleted from the map by setting `HasCrumblingFrames` to true. - Crumbling frames start from first frame after both regular & damaged frames and ends at halfway point of the frames in TerrainType's image. - - Note that the number of regular & damage frames considered for this depends on value of `HasDamagedFrames` and for `IsAnimated` TerrainTypes, `AnimationLength` (see [Animated TerrainTypes](#animated-terraintypes). Exercise caution and ensure there are correct amount of frames to display. + - Note that the number of regular & damage frames considered for this depends on value of `HasDamagedFrames` and for `IsAnimated` TerrainTypes, `AnimationLength` (see [Animated TerrainTypes](#animated-terraintypes)). Exercise caution and ensure there are correct amount of frames to display. - Sound event from `CrumblingSound` (if set) is played when crumbling animation starts playing. - [Destroy animation & sound](New-or-Enhanced-Logics.md#destroy-animation--sound) only play after crumbling animation has finished. From d1fd8872490d43f7e0c4a27ea7a4df7911524d84 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 5 Feb 2025 17:25:30 +0800 Subject: [PATCH 12/16] Fit with #1479 --- docs/User-Interface.md | 4 +- src/Ext/Rules/Body.h | 4 +- src/Ext/Sidebar/Hooks.cpp | 92 ++++++++++++++++++++++++--------------- 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 91fc7f96af..f227e9a72c 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -394,7 +394,7 @@ MissingCameo=XXICON.SHP ; filename - including the .shp/.pcx extension - `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 three different situations: currently owned this building type, grey cameo and have its prerequisite, grey cameo but have no prerequisite (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.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`. - If `Cameo.AuxTechnos` is not set, in addition to basic conditions, the grey cameo will only show when `AIBasePlanningSide` condition is satisfied. Otherwise, the grey cameo will only show when at least one of these types is owned by you or its `TechnoLevel`, `Owner`, `RequiredHouses`, `ForbiddenHouses`, `Cameo.AuxTechnos` (use `AIBasePlanningSide` if not set) and `Cameo.NegTechnos` (if set) conditions are satisfied. - If `Cameo.NegTechnos` is set, the grey cameo will not show when you have a techno in one of these types. @@ -411,7 +411,7 @@ In `rulesmd.ini`: [AudioVisual] Cameo.AlwaysExist=false ; boolean Cameo.OverlayShapes=pips.shp ; filename - including the .shp extension -Cameo.OverlayFrames=-1,-1,-1 ; integer - owned this building, grey and have its prerequisite, grey but have no prerequisite +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 diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index b1cc387402..d37ab42798 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -95,7 +95,7 @@ class RulesExt Valueable Cameo_AlwaysExist; Valueable Cameo_OverlayShapes; - Valueable> Cameo_OverlayFrames; + ValueableVector Cameo_OverlayFrames; CustomPalette Cameo_OverlayPalette; Valueable ExtendedAircraftMissions; @@ -233,7 +233,7 @@ class RulesExt , Cameo_AlwaysExist { false } , Cameo_OverlayShapes { FileSystem::PIPS_SHP } - , Cameo_OverlayFrames { { -1, -1, -1 } } + , Cameo_OverlayFrames {} , Cameo_OverlayPalette {} , ExtendedAircraftMissions { false } diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index 6f99d396ea..5cce58f86c 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -120,14 +120,15 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) const auto position = Point2D { destX + 30, destY + 24 }; const auto pRulesExt = RulesExt::Global(); - const auto frames = pRulesExt->Cameo_OverlayFrames.Get(); + const auto& frames = pRulesExt->Cameo_OverlayFrames; + const auto frameSize = frames.size(); - if (greyCameo) // Only draw extras over grey cameos + if (greyCameo && frameSize > 2) // Only draw extras over grey cameos { - auto frame = frames.Y; + auto frame = frames[2]; const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - if (pTypeExt && pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + if (frameSize > 3 && pTypeExt && pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) { auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; @@ -139,7 +140,7 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) PCX::Instance->BlitToSurface(&drawRect, DSurface::Sidebar, CameoPCX); } - frame = frames.Z; + frame = frames[3]; } } @@ -158,44 +159,63 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) } } - const auto pBuildingType = abstract_cast(pType); - - if (pBuildingType) // Only count owned buildings + if (const auto pBuildingType = abstract_cast(pType)) // Only count owned buildings { - const auto pHouse = HouseClass::CurrentPlayer(); - auto count = BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse); - - if (count == -1) - count = pHouse->CountOwnedAndPresent(pBuildingType); + 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 (count > 0) + if ((frameSize && frames[0] >= 0) || statistics) { - if (frames.X >= 0) - { - DSurface::Sidebar->DrawSHP( - pRulesExt->Cameo_OverlayPalette.GetOrDefaultConvert(FileSystem::PALETTE_PAL), - pRulesExt->Cameo_OverlayShapes, - frames.X, - &position, - &boundingRect, - BlitterFlags(0x600), - 0, 0, - ZGradient::Ground, - 1000, 0, 0, 0, 0, 0); - } + const auto pHouse = HouseClass::CurrentPlayer(); + auto count = BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse); + + if (count == -1) + count = pHouse->CountOwnedAndPresent(pBuildingType); - if (Phobos::Config::ShowBuildingStatistics - && BuildingTypeExt::ExtMap.Find(pBuildingType)->Cameo_ShouldCount.Get(pBuildingType->BuildCat != BuildCat::Combat || pBuildingType->BuildLimit != INT_MAX)) + if (count > 0) { - GET_STACK(RectangleStruct, surfaceRect, STACK_OFFSET(0x48C, -0x438)); + if (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); + } - const COLORREF color = Drawing::RGB_To_Int(Drawing::TooltipColor); - const TextPrintType printType = TextPrintType::Background | TextPrintType::FullShadow | TextPrintType::Point8; - auto textPosition = Point2D { destX, destY + 1 }; + if (statistics) + { + GET_STACK(RectangleStruct, surfaceRect, STACK_OFFSET(0x48C, -0x438)); - wchar_t text[0x20]; - swprintf_s(text, L"%d", count); - DSurface::Sidebar->DrawTextA(text, &surfaceRect, &textPosition, color, 0, printType); + 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); + } } } } From 42e07bbf1cc0b6c8fe775c5f082c1f83cf740ea4 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 5 Feb 2025 17:26:33 +0800 Subject: [PATCH 13/16] Fix doc --- docs/Whats-New.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 822fc4a3eb..558817cc47 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -318,6 +318,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) @@ -464,7 +465,6 @@ New: - Allow customizing extra tint intensity for Iron Curtain & Force Shield (by Starkku) - Option to enable parsing 8-bit RGB values from `[ColorAdd]` instead of RGB565 (by Starkku) - Customizing height and speed at which subterranean units travel (by Starkku) -- Grey cameo preview and cameo overlays (by CrimRecya) - Option for Warhead damage to penetrate Iron Curtain or Force Shield (by Starkku) - 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) From fc45d84412a5b954b703944c50c05e0a829fe506 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Thu, 6 Feb 2025 22:07:49 +0800 Subject: [PATCH 14/16] Rewrite to optimize --- docs/User-Interface.md | 14 +- src/Ext/BuildingType/Body.cpp | 128 +----------------- src/Ext/BuildingType/Body.h | 3 - src/Ext/BuildingType/Hooks.Upgrade.cpp | 21 ++- src/Ext/House/Body.cpp | 178 ++++++++++++++++++++++++- src/Ext/House/Body.h | 6 + src/Ext/House/Hooks.cpp | 50 ++++--- src/Ext/Scenario/Body.cpp | 6 +- src/Ext/Scenario/Body.h | 12 +- src/Ext/Sidebar/Body.cpp | 37 +++-- src/Ext/Sidebar/Hooks.cpp | 25 +--- src/Ext/TechnoType/Body.cpp | 112 +++++++++++++++- src/Ext/TechnoType/Body.h | 19 ++- src/Misc/Hooks.Ares.cpp | 7 + src/Misc/PhobosToolTip.cpp | 11 +- src/Utilities/AresAddressInit.cpp | 5 + src/Utilities/AresFunctions.h | 3 + 17 files changed, 405 insertions(+), 232 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index c3ba8a7a57..180f3e5f30 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -400,14 +400,14 @@ 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`, `RequiredHouses` and `ForbiddenHouses` should be satisfied). Defaults to `[AudioVisual]` -> `Cameo.AlwaysExist`. + - `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`. - - If `Cameo.AuxTechnos` is not set, in addition to basic conditions, the grey cameo will only show when `AIBasePlanningSide` condition is satisfied. Otherwise, the grey cameo will only show when at least one of these types is owned by you or its `TechnoLevel`, `Owner`, `RequiredHouses`, `ForbiddenHouses`, `Cameo.AuxTechnos` (use `AIBasePlanningSide` if not set) and `Cameo.NegTechnos` (if set) conditions are satisfied. - - If `Cameo.NegTechnos` is set, the grey cameo will not show when you have a techno in one of these types. + - `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`: @@ -426,8 +426,8 @@ Cameo.OverlayPalette=palette.pal ; filename - including the .pal extension [SOMETECHNO] ; TechnoType Cameo.AlwaysExist= ; boolean -Cameo.AuxTechnos= ; List of TechnoTypes -Cameo.NegTechnos= ; List of TechnoTypes +Cameo.RequiredHouses= ; list of house types +Cameo.OverrideTechnos= ; List of TechnoTypes UIDescription.Unbuildable= ; CSF entry key [SOMEBUILDING] ; BuildingType diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 3bf7352e51..60c157c6bf 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -67,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; @@ -117,116 +103,6 @@ int BuildingTypeExt::GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* return isUpgrade ? result : -1; } -bool BuildingTypeExt::ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const pTypeExt) -{ - const auto pType = pTypeExt->OwnerObject(); - const auto techLevel = pType->TechLevel; - - if (techLevel <= 0 || techLevel > Game::TechLevel) - return false; - - const auto pHouse = HouseClass::CurrentPlayer(); - - if (!pHouse->InOwners(pType)) - return false; - - if (!pHouse->InRequiredHouses(pType)) - return false; - - if (pHouse->InForbiddenHouses(pType)) - return false; - - const auto& pNegTypes = pTypeExt->Cameo_NegTechnos; - - if (pNegTypes.size()) - { - for (const auto& pNegType : pNegTypes) - { - if (pHouse->CountOwnedAndPresent(pNegType)) - return false; - else if (pNegType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pNegType), pHouse) > 0) - return false; - } - } - - const auto& pAuxTypes = pTypeExt->Cameo_AuxTechnos; - - if (!pAuxTypes.size()) - { - const auto sideIndex = pType->AIBasePlanningSide; - - return (sideIndex == -1 || sideIndex == pHouse->Type->SideIndex); - } - - for (const auto& pAuxType : pAuxTypes) - { - const auto pAuxTypeExt = TechnoTypeExt::ExtMap.Find(pAuxType); - - if (!pAuxTypeExt->CameoCheckMutex) - { - if (pHouse->CountOwnedAndPresent(pAuxType)) - return true; - else if (pAuxType->WhatAmI() == AbstractType::BuildingType && BuildingTypeExt::GetUpgradesAmount(static_cast(pAuxType), pHouse) > 0) - return true; - - pAuxTypeExt->CameoCheckMutex = true; - const auto exist = BuildingTypeExt::ShouldExistGreyCameo(pAuxTypeExt); - pAuxTypeExt->CameoCheckMutex = false; - - if (exist) - return true; - } - } - - return false; -} - -// Check the cameo change -CanBuildResult BuildingTypeExt::CheckAlwaysExistCameo(const TechnoTypeClass* const pType, CanBuildResult canBuild) -{ - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - - if (pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) - { - auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; - - if (canBuild == CanBuildResult::Unbuildable) // Unbuildable + Satisfy basic limitations = Change it to TemporarilyUnbuildable - { - pTypeExt->CameoCheckMutex = true; - const auto exist = BuildingTypeExt::ShouldExistGreyCameo(pTypeExt); - pTypeExt->CameoCheckMutex = false; - - if (exist) - { - if (std::find(vec.begin(), vec.end(), pTypeExt) == vec.end()) // … + Not in the list = Need to add it into list - { - vec.push_back(pTypeExt); - SidebarClass::Instance->SidebarNeedsRepaint(); - const EventClass event - ( - HouseClass::CurrentPlayer->ArrayIndex, - EventType::AbandonAll, - static_cast(pType->WhatAmI()), - pType->GetArrayIndex(), - pType->Naval - ); - EventClass::AddEvent(event); - } - - canBuild = CanBuildResult::TemporarilyUnbuildable; - } - } - else if (std::find(vec.begin(), vec.end(), pTypeExt) != vec.end()) // Not Unbuildable + In the list = remove it from the list and play EVA - { - vec.erase(std::remove(vec.begin(), vec.end(), pTypeExt), vec.end()); - SidebarClass::Instance->SidebarNeedsRepaint(); - VoxClass::Play(&Make_Global(0x83FA64)); // 0x83FA64 -> EVA_NewConstructionOptions - } - } - - return canBuild; -} - void BuildingTypeExt::ExtData::Initialize() { } @@ -279,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"); @@ -288,8 +166,6 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Units_RepairPercent.Read(exINI, pSection, "Units.RepairPercent"); this->Units_UseRepairCost.Read(exINI, pSection, "Units.UseRepairCost"); - this->Cameo_ShouldCount.Read(exINI, pSection, "Cameo.ShouldCount"); - this->NoBuildAreaOnBuildup.Read(exINI, pSection, "NoBuildAreaOnBuildup"); this->Adjacent_Allowed.Read(exINI, pSection, "Adjacent.Allowed"); this->Adjacent_Disallowed.Read(exINI, pSection, "Adjacent.Disallowed"); diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 6dbdf2257a..dcda13b03d 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -174,8 +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); - static bool ShouldExistGreyCameo(const TechnoTypeExt::ExtData* const pTypeExt); - static CanBuildResult CheckAlwaysExistCameo(const TechnoTypeClass* const pType, CanBuildResult canBuild); }; diff --git a/src/Ext/BuildingType/Hooks.Upgrade.cpp b/src/Ext/BuildingType/Hooks.Upgrade.cpp index 5d8c32b5d6..d4fc06883b 100644 --- a/src/Ext/BuildingType/Hooks.Upgrade.cpp +++ b/src/Ext/BuildingType/Hooks.Upgrade.cpp @@ -87,21 +87,18 @@ CanBuildResult CheckBuildLimit(HouseClass const* const pHouse, BuildingTypeClass 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(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 (canBuild == CanBuildResult::Buildable) { - if (auto const pBuilding = abstract_cast(pItem)) + if (auto const pBuilding = abstract_cast(pItem)) { - if (auto pBuildingExt = BuildingTypeExt::ExtMap.Find(pBuilding)) - { - if (pBuildingExt->PowersUp_Buildings.size() > 0) - canBuild = CheckBuildLimit(pThis, pBuilding, includeInProduction); - } + if (BuildingTypeExt::ExtMap.Find(pBuilding)->PowersUp_Buildings.size() > 0) + canBuild = CheckBuildLimit(pThis, pBuilding, includeInProduction); } } @@ -113,8 +110,8 @@ DEFINE_HOOK(0x4F8361, HouseClass_CanBuild_UpgradesInteraction, 0x5) canBuild = CanBuildResult::TemporarilyUnbuildable; } - if (!buildLimitOnly && includeInProduction && pThis == HouseClass::CurrentPlayer()) // Eliminate any non-producible calls to change the list safely - canBuild = BuildingTypeExt::CheckAlwaysExistCameo(pItem, canBuild); + 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 eeb60e701d..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,39 +336,47 @@ 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); } - else if (pThis == HouseClass::CurrentPlayer) - { - GET(int*, pAddress, ESP); - if (*pAddress == 0x6A5FED || *pAddress == 0x6A97EF || *pAddress == 0x6AB65B) - { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + return 0; +} - // The types exist in the list means that they are not buildable now - if (pTypeExt && pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) - { - auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; +DEFINE_HOOK(0x4F9286, HouseClass_Update_RecheckOwnerBitfield, 0x6) +{ + enum { SkipLoop = 0x4F92DD, StartLoop = 0x4F928C }; - if (std::find(vec.begin(), vec.end(), pTypeExt) != vec.end()) - R->EAX(true); - } - } - } + GET(const int, buildingCount, EBP); - return 0; + 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 diff --git a/src/Ext/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index e01cd05076..d9ea438ecc 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -162,7 +162,11 @@ void ScenarioExt::ExtData::Serialize(T& Stm) .Process(this->BriefingTheme) .Process(this->AutoDeathObjects) .Process(this->TransportReloaders) - .Process(this->OwnedExistCameoTechnoTypes) + .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 38f0352b82..61a1682120 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -36,7 +36,11 @@ class ScenarioExt std::vector AutoDeathObjects; std::vector TransportReloaders; // Objects that can reload ammo in limbo - std::vector OwnedExistCameoTechnoTypes; + DWORD OwnerBitfield_BuildingType; + DWORD OwnerBitfield_InfantryType; + DWORD OwnerBitfield_VehicleType; + DWORD OwnerBitfield_NavyType; + DWORD OwnerBitfield_AircraftType; ExtData(ScenarioClass* OwnerObject) : Extension(OwnerObject) , ShowBriefing { false } @@ -45,7 +49,11 @@ class ScenarioExt , Variables { } , AutoDeathObjects {} , TransportReloaders {} - , OwnedExistCameoTechnoTypes {} + , 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/Body.cpp b/src/Ext/Sidebar/Body.cpp index 1de4289841..8e42763e09 100644 --- a/src/Ext/Sidebar/Body.cpp +++ b/src/Ext/Sidebar/Body.cpp @@ -29,7 +29,7 @@ bool __stdcall SidebarExt::AresTabCameo_RemoveCameo(BuildType* pItem) { const auto pFactory = pTechnoType->FindFactory(true, false, false, pCurrent); - if (pFactory && pFactory->Owner->CanBuild(pTechnoType, false, true) != CanBuildResult::Unbuildable) + if (pFactory && pCurrent->CanBuild(pTechnoType, false, true) != CanBuildResult::Unbuildable) return false; } else @@ -40,32 +40,27 @@ bool __stdcall SidebarExt::AresTabCameo_RemoveCameo(BuildType* pItem) return false; } - if (pItem->CurrentFactory) + // Here we just raise AbandonAll without find factory, rather than check factory and Abandon then find factory and AbandonAll + if (pTechnoType) { - EventClass event = EventClass(pCurrent->ArrayIndex, EventType::Abandon, static_cast(pItem->ItemType), pItem->ItemIndex, pTechnoType && pTechnoType->Naval); + const EventClass event + ( + pCurrent->ArrayIndex, + EventType::AbandonAll, + static_cast(pItem->ItemType), + pItem->ItemIndex, + 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); - } + const auto pDisplay = DisplayClass::Instance(); + pDisplay->SetActiveFoundation(nullptr); + pDisplay->CurrentBuilding = nullptr; + pDisplay->CurrentBuildingType = nullptr; + pDisplay->CurrentBuildingOwnerArrayIndex = -1; } return true; diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index 5cce58f86c..a30e31799b 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -128,20 +128,15 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) auto frame = frames[2]; const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - if (frameSize > 3 && pTypeExt && pTypeExt->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + if (frameSize > 3 && pTypeExt && pTypeExt->IsGreyCameoForCurrentPlayer) { - auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; - - if (std::find(vec.begin(), vec.end(), pTypeExt) != vec.end()) + if (const auto CameoPCX = pTypeExt->GreyCameoPCX.GetSurface()) { - if (const auto CameoPCX = pTypeExt->GreyCameoPCX.GetSurface()) - { - auto drawRect = RectangleStruct { destX, destY, 60, 48 }; - PCX::Instance->BlitToSurface(&drawRect, DSurface::Sidebar, CameoPCX); - } - - frame = frames[3]; + auto drawRect = RectangleStruct { destX, destY, 60, 48 }; + PCX::Instance->BlitToSurface(&drawRect, DSurface::Sidebar, CameoPCX); } + + frame = frames[3]; } if (frame >= 0) @@ -182,13 +177,7 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) if ((frameSize && frames[0] >= 0) || statistics) { - const auto pHouse = HouseClass::CurrentPlayer(); - auto count = BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse); - - if (count == -1) - count = pHouse->CountOwnedAndPresent(pBuildingType); - - if (count > 0) + if (const auto count = HouseExt::CountOwnedPresentWithDeployOrUpgrade(HouseClass::CurrentPlayer(), pBuildingType, true)) { if (frames[0] >= 0) { diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index e3a9c53af8..826d2dd04b 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,103 @@ 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) + { + auto CheckOverrideTechnos = [pTypeExt]() + { + const auto& pAuxTypes = pTypeExt->Cameo_OverrideTechnos; + + if (pAuxTypes.size()) + { + for (const auto& pAuxType : pAuxTypes) + { + if (HouseExt::CountOwnedPresentExt(HouseClass::CurrentPlayer, pAuxType, true, true)) + return true; + } + } + + return false; + }; + + if (pTypeExt->IsMetTheEssentialConditions && (CheckOverrideTechnos() || HouseExt::CheckOwnerBitfieldForCurrentPlayer(pType))) + { + if (!pTypeExt->IsGreyCameoForCurrentPlayer) + { + pTypeExt->IsGreyCameoForCurrentPlayer = true; + ForceRedrawSidebar(); + + if (const auto pBldType = abstract_cast(pType)) + { + 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; + } + } + + const EventClass event + ( + HouseClass::CurrentPlayer->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 @@ -456,8 +556,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->BuildLimitGroup_ExtraLimit_MaxNum.Read(exINI, pSection, "BuildLimitGroup.ExtraLimit.MaxNum"); this->Cameo_AlwaysExist.Read(exINI, pSection, "Cameo.AlwaysExist"); - this->Cameo_AuxTechnos.Read(exINI, pSection, "Cameo.AuxTechnos"); - this->Cameo_NegTechnos.Read(exINI, pSection, "Cameo.NegTechnos"); + 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"); @@ -839,9 +939,11 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->BuildLimitGroup_ExtraLimit_MaxNum) .Process(this->Cameo_AlwaysExist) - .Process(this->Cameo_AuxTechnos) - .Process(this->Cameo_NegTechnos) - .Process(this->CameoCheckMutex) + .Process(this->Cameo_OverrideTechnos) + .Process(this->Cameo_RequiredHouses) + .Process(this->IsMetTheEssentialConditions) + .Process(this->IsGreyCameoForCurrentPlayer) + .Process(this->IsGreyCameoAbandonedProduct) .Process(this->UIDescription_Unbuildable) .Process(this->GreyCameoPCX) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 9780402e87..0ffb7b6906 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -227,9 +227,11 @@ class TechnoTypeExt Valueable BuildLimitGroup_ExtraLimit_MaxNum; Nullable Cameo_AlwaysExist; - ValueableVector Cameo_AuxTechnos; - ValueableVector Cameo_NegTechnos; - bool CameoCheckMutex; // Not read from ini + 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; @@ -461,9 +463,11 @@ class TechnoTypeExt , BuildLimitGroup_ExtraLimit_MaxNum { 0 } , Cameo_AlwaysExist {} - , Cameo_AuxTechnos {} - , Cameo_NegTechnos {} - , CameoCheckMutex { false } + , Cameo_OverrideTechnos {} + , Cameo_RequiredHouses { 0xFFFFFFFF } + , IsMetTheEssentialConditions { false } + , IsGreyCameoForCurrentPlayer { false } + , IsGreyCameoAbandonedProduct { true } , UIDescription_Unbuildable {} , GreyCameoPCX {} @@ -515,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 71f1e0d31d..78567e567b 100644 --- a/src/Misc/PhobosToolTip.cpp +++ b/src/Misc/PhobosToolTip.cpp @@ -166,15 +166,10 @@ void PhobosToolTip::HelpText_Techno(TechnoTypeClass* pType) if (auto pDesc = this->GetUIDescription(pData)) oss << L"\n" << pDesc; - if (pData->Cameo_AlwaysExist.Get(RulesExt::Global()->Cameo_AlwaysExist)) + if (pData->IsGreyCameoForCurrentPlayer) { - auto& vec = ScenarioExt::Global()->OwnedExistCameoTechnoTypes; - - if (std::find(vec.begin(), vec.end(), pData) != vec.end()) - { - if (auto pExDesc = this->GetUnbuildableUIDescription(pData)) - oss << L"\n" << pExDesc; - } + if (auto pExDesc = this->GetUnbuildableUIDescription(pData)) + oss << L"\n" << pExDesc; } this->TextBuffer = oss.str(); 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; From 6b5f9b5061de0c75d9c1edc2951e91788a16aab3 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 7 Feb 2025 13:13:49 +0800 Subject: [PATCH 15/16] Optimize --- src/Ext/TechnoType/Body.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 826d2dd04b..0799f68ec9 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -296,7 +296,8 @@ CanBuildResult TechnoTypeExt::CheckAlwaysExistCameo(TechnoTypeClass* pType, CanB if (canBuild == CanBuildResult::Unbuildable) { - auto CheckOverrideTechnos = [pTypeExt]() + const auto pCurrent = HouseClass::CurrentPlayer(); + auto CheckOverrideTechnos = [pCurrent, pTypeExt]() { const auto& pAuxTypes = pTypeExt->Cameo_OverrideTechnos; @@ -304,7 +305,7 @@ CanBuildResult TechnoTypeExt::CheckAlwaysExistCameo(TechnoTypeClass* pType, CanB { for (const auto& pAuxType : pAuxTypes) { - if (HouseExt::CountOwnedPresentExt(HouseClass::CurrentPlayer, pAuxType, true, true)) + if (HouseExt::CountOwnedPresentExt(pCurrent, pAuxType, true, true)) return true; } } @@ -318,9 +319,11 @@ CanBuildResult TechnoTypeExt::CheckAlwaysExistCameo(TechnoTypeClass* pType, CanB { 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 @@ -333,15 +336,18 @@ CanBuildResult TechnoTypeExt::CheckAlwaysExistCameo(TechnoTypeClass* pType, CanB } } - const EventClass event - ( - HouseClass::CurrentPlayer->ArrayIndex, - EventType::AbandonAll, - static_cast(pType->WhatAmI()), - pType->GetArrayIndex(), - pType->Naval - ); - EventClass::AddEvent(event); + 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; From c2d33feee7657b08cf8841a883cb386ae9d3e68f Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 8 Feb 2025 21:45:55 +0800 Subject: [PATCH 16/16] Fix a FE caused by no `Cameo.OverlayFrames` --- src/Ext/Sidebar/Hooks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index a30e31799b..31b280543f 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -179,7 +179,7 @@ DEFINE_HOOK(0x6A9BC5, StripClass_Draw_DrawGreyCameoExtraCover, 0x6) { if (const auto count = HouseExt::CountOwnedPresentWithDeployOrUpgrade(HouseClass::CurrentPlayer(), pBuildingType, true)) { - if (frames[0] >= 0) + if (frameSize && frames[0] >= 0) { DSurface::Sidebar->DrawSHP( pRulesExt->Cameo_OverlayPalette.GetOrDefaultConvert(FileSystem::PALETTE_PAL),