From 0983bf8e008c5008f356562ef63700eb1e7ff4e2 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 23 Jun 2024 20:24:00 +0800 Subject: [PATCH 01/19] Several new Infotypes and a new single frame display method --- CREDITS.md | 2 + docs/User-Interface.md | 5 +- docs/Whats-New.md | 1 + src/Ext/Techno/Body.Visuals.cpp | 146 +++++++++++++++++++++++ src/New/Type/DigitalDisplayTypeClass.cpp | 43 +++++-- src/New/Type/DigitalDisplayTypeClass.h | 2 + src/Utilities/Enum.h | 10 +- src/Utilities/TemplateDef.h | 32 +++++ 8 files changed, 230 insertions(+), 11 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index b20884ea40..763094c4df 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -331,6 +331,8 @@ This page lists all the individual contributions to the project by their author. - Re-enable the Veinhole Monster and Weeds from TS - Recreate the weed-charging of SWs like the TS Chemical Missile - Allow to change the speed of gas particles +- **CrimRecya** + - Several new Infotypes and a new single frame display method - **Ares developers** - YRpp and Syringe which are used, save/load, project foundation and generally useful code from Ares - unfinished RadTypes code diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 3233a735b1..bb0b748fc2 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -40,7 +40,7 @@ IngameScore.LoseTheme= ; Soundtrack theme ID - `CanSee` and `CanSee.Observer` can limit visibility to specific players. - The digits can be either a custom shape (.shp) or text drawn using the game font. This depends on whether `Shape` is set. - `Text.Color`, `Text.Color.ConditionYellow` and `Text.Color.ConditionRed` allow customization of the font color. `Text.Background=yes` will additionally draw a black rectangle background. - - When using shapes, a custom palette can be specified with `Palette`. `Shape.Spacing` controls pixel buffer between characters. + - When using shapes, a custom palette can be specified with `Palette`. `Shape.Spacing` controls pixel buffer between characters. If `Shape.PercentageFrame` set to true, it will only draw one frame that corresponds to total frames by percentage. - Frames 0-9 will be used as digits when the owner's health bar is green, 10-19 when yellow, 20-29 when red. For `/` and `%` characters, frame numbers are 30-31, 32-33, 34-35, respectively. - Default `Offset.ShieldDelta` for `InfoType=Shield` is `0,-10`, `0,0` for others. - Default `Shape.Spacing` for buildings is `4,-2`, `4,0` for others. @@ -58,7 +58,7 @@ Aircraft.DefaultDigitalDisplayTypes= ; list of DigitalDisplayTypes [SOMEDIGITALDISPLAYTYPE] ; DigitalDisplayType ; Generic -InfoType=Health ; Displayed value enumeration (health|shield|ammo|mindcontrol|spawns|passengers|tiberium|experience|occupants|gattlingstage) +InfoType=Health ; Displayed value enumeration (health|shield|ammo|mindcontrol|spawns|passengers|tiberium|experience|occupants|gattlingstage|ROF|Reload|SpawnTimer|GattlingTimer|ProduceCash|PassengerKill|AutoDeath|SuperWeapon) Offset=0,0 ; integers - horizontal, vertical Offset.ShieldDelta= ; integers - horizontal, vertical Align=right ; Text alignment enumeration (left|right|center/centre) @@ -78,6 +78,7 @@ Text.Background=false ; boolean Shape= ; filename with .shp extension, if not present, game-drawn text will be used instead Palette=palette.pal ; filename with .pal extension Shape.Spacing= ; integers - horizontal, vertical spacing between digits +Shape.PercentageFrame=false ; boolean [SOMETECHNOTYPE] DigitalDisplay.Disable=false ; boolean diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 6c5e226c26..4071eddd3c 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -408,6 +408,7 @@ New: - AttachEffect types with new features like custom tint and weapon range modifier (by Starkku) - Force shield effect sync on deploy & vs. organic targets effect customization to complement the Iron Curtain ones (by Starkku) - Map trigger action 41 (Play animation at waypoint) now uses additional parameter to determine if animation can play sound, deal damage etc. (by Starkku) +- Several new Infotypes and a new single frame display method(by CrimRecya) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 8de43cd091..da8fdc4989 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -448,6 +449,151 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType maxValue = pType->WeaponStages; break; } + case DisplayInfoType::ROF: + { + if (!pThis->GetWeapon(0) || !pThis->GetWeapon(0)->WeaponType) + return; + + value = pThis->RearmTimer.GetTimeLeft(); + maxValue = pThis->ChargeTurretDelay; + break; + } + case DisplayInfoType::Reload: + { + if (pType->Ammo <= 0) + return; + + value = pThis->ReloadTimer.GetTimeLeft(); + maxValue = pThis->ReloadTimer.TimeLeft; + break; + } + case DisplayInfoType::SpawnTimer: + { + if (pThis->SpawnManager == nullptr || pType->Spawns == nullptr || pType->SpawnsNumber <= 0) + return; + + value = 0; + maxValue = pThis->SpawnManager->RegenRate; + + for (int i = 0; i < pType->SpawnsNumber; i++) + { + if (pThis->SpawnManager->SpawnedNodes[i]->Status == SpawnNodeStatus::Dead) + { + int thisValue = pThis->SpawnManager->SpawnedNodes[i]->SpawnTimer.GetTimeLeft(); + + if (thisValue < value) + value = thisValue; + } + } + + break; + } + case DisplayInfoType::GattlingTimer: + { + if (!pType->IsGattling) + return; + + const int thisStage = pThis->CurrentGattlingStage; + + if (pThis->Veterancy.IsElite()) + { + if (thisStage > 0) + { + value = pThis->GattlingValue - pType->EliteStage[thisStage - 1]; + maxValue = pType->EliteStage[thisStage] - pType->EliteStage[thisStage - 1]; + } + else + { + value = pThis->GattlingValue; + maxValue = pType->EliteStage[thisStage]; + } + } + else + { + if (thisStage > 0) + { + value = pThis->GattlingValue - pType->WeaponStage[thisStage - 1]; + maxValue = pType->WeaponStage[thisStage] - pType->WeaponStage[thisStage - 1]; + } + else + { + value = pThis->GattlingValue; + maxValue = pType->WeaponStage[thisStage]; + } + } + + break; + } + case DisplayInfoType::ProduceCash: + { + if (pThis->WhatAmI() != AbstractType::Building) + return; + + const auto pBuildingType = abstract_cast(pType); + const auto pBuilding = abstract_cast(pThis); + + if (pBuildingType->ProduceCashAmount <= 0) + return; + + value = pBuilding->CashProductionTimer.GetTimeLeft(); + maxValue = pBuilding->CashProductionTimer.TimeLeft; + break; + } + case DisplayInfoType::PassengerKill: + { + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + if (!pTypeExt || !pTypeExt->PassengerDeletionType) + return; + + value = pExt->PassengerDeletionTimer.GetTimeLeft(); + maxValue = pExt->PassengerDeletionTimer.TimeLeft; + break; + } + case DisplayInfoType::AutoDeath: + { + const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + if (!pTypeExt || !pTypeExt->AutoDeath_Behavior.isset()) + return; + + if (pTypeExt->AutoDeath_AfterDelay > 0) + { + value = pExt->AutoDeathTimer.GetTimeLeft(); + maxValue = pExt->AutoDeathTimer.TimeLeft; + } + else if (pTypeExt->AutoDeath_OnAmmoDepletion && pType->Ammo > 0) + { + value = pThis->Ammo; + maxValue = pType->Ammo; + } + + break; + } + case DisplayInfoType::SuperWeapon: + { + if (pThis->WhatAmI() != AbstractType::Building || !pThis->Owner) + return; + + const auto pBuildingType = abstract_cast(pType); + const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuildingType); + SuperClass* pSuper = nullptr; + + if (pBuildingType->SuperWeapon != -1) + pSuper = pThis->Owner->Supers.GetItem(pBuildingType->SuperWeapon); + else if (pBuildingType->SuperWeapon2 != -1) + pSuper = pThis->Owner->Supers.GetItem(pBuildingType->SuperWeapon2); + else if (pBuildingTypeExt->SuperWeapons.size() > 0) + pSuper = pThis->Owner->Supers.GetItem(pBuildingTypeExt->SuperWeapons[0]); + + if (pSuper) + { + value = pSuper->RechargeTimer.GetTimeLeft(); + maxValue = pSuper->RechargeTimer.TimeLeft; + } + + break; + } default: { value = pThis->Health; diff --git a/src/New/Type/DigitalDisplayTypeClass.cpp b/src/New/Type/DigitalDisplayTypeClass.cpp index 8d9929c9c1..91b76e2a82 100644 --- a/src/New/Type/DigitalDisplayTypeClass.cpp +++ b/src/New/Type/DigitalDisplayTypeClass.cpp @@ -29,6 +29,7 @@ void DigitalDisplayTypeClass::LoadFromINI(CCINIClass* pINI) this->Shape.Read(exINI, section, "Shape"); this->Palette.LoadFromINI(pINI, section, "Palette"); this->Shape_Spacing.Read(exINI, section, "Shape.Spacing"); + this->Shape_PercentageFrame.Read(exINI, section, "Shape.PercentageFrame"); this->Percentage.Read(exINI, section, "Percentage"); this->HideMaxValue.Read(exINI, section, "HideMaxValue"); this->VisibleToHouses_Observer.Read(exINI, section, "VisibleToHouses.Observer"); @@ -71,10 +72,11 @@ void DigitalDisplayTypeClass::Draw(Point2D position, int length, int value, int void DigitalDisplayTypeClass::DisplayText(Point2D& position, int length, int value, int maxValue, bool isBuilding, bool isInfantry, bool hasShield) { wchar_t text[0x20]; + double ratio = static_cast(value) / maxValue; if (Percentage.Get()) { - swprintf_s(text, L"%d", static_cast((static_cast(value) / maxValue) * 100)); + swprintf_s(text, L"%d", static_cast(ratio * 100)); wcscat_s(text, L"%%"); } else if (HideMaxValue.Get(isInfantry)) @@ -86,7 +88,6 @@ void DigitalDisplayTypeClass::DisplayText(Point2D& position, int length, int val swprintf_s(text, L"%d/%d", value, maxValue); } - double ratio = static_cast(value) / maxValue; COLORREF color = Drawing::RGB_To_Int(Text_Color.Get(ratio)); RectangleStruct rect = { 0, 0, 0, 0 }; DSurface::Composite->GetRect(&rect); @@ -106,8 +107,9 @@ void DigitalDisplayTypeClass::DisplayText(Point2D& position, int length, int val void DigitalDisplayTypeClass::DisplayShape(Point2D& position, int length, int value, int maxValue, bool isBuilding, bool isInfantry, bool hasShield) { + double ratio = static_cast(value) / maxValue; std::string valueString(std::move(Percentage ? - GeneralUtils::IntToDigits(static_cast(static_cast(value) / maxValue * 100)) : + GeneralUtils::IntToDigits(static_cast(ratio * 100)) : GeneralUtils::IntToDigits(value) )); std::string maxValueString(!Percentage && !HideMaxValue.Get(isInfantry) ? @@ -137,13 +139,25 @@ void DigitalDisplayTypeClass::DisplayShape(Point2D& position, int length, int va } case TextAlign::Center: { - position.X -= static_cast(valueString.length()) * spacing.X / 2; - position.Y += static_cast(valueString.length()) * spacing.Y / 2; + if (Shape_PercentageFrame) + { + position.X -= static_cast(Shape->Width) / 2; + } + else + { + position.X -= static_cast(valueString.length()) * spacing.X / 2; + position.Y += static_cast(valueString.length()) * spacing.Y / 2; + } + break; } case TextAlign::Right: { - position.X -= spacing.X; + if (Shape_PercentageFrame) + position.X -= static_cast(Shape->Width); + else + position.X -= spacing.X; + break; } } @@ -156,7 +170,6 @@ void DigitalDisplayTypeClass::DisplayShape(Point2D& position, int length, int va const int redExtraFrame = 34; int numberBaseFrame = greenBaseFrame; int extraBaseFrame = greenExtraFrame; - double ratio = static_cast(value) / maxValue; if (ratio > RulesClass::Instance->ConditionYellow) numberBaseFrame = greenBaseFrame; @@ -188,7 +201,20 @@ void DigitalDisplayTypeClass::DisplayShape(Point2D& position, int length, int va RectangleStruct rect = DSurface::Composite->GetRect(); rect.Height -= 32; // account for bottom bar - ShapeTextPrinter::PrintShape(valueString.c_str(), shapeTextPrintData, position, rect, DSurface::Composite); + if (Shape_PercentageFrame) + { + DSurface::Composite->DrawSHP + ( + const_cast(Palette.GetOrDefaultConvert(FileSystem::PALETTE_PAL)), + const_cast(Shape.Get()), + static_cast(Math::clamp(ratio, 0, 1) * (Shape->Frames - 1) + 0.5), + &position, &rect, BlitterFlags::None, 0, 0, ZGradient::Ground, 1000, 0, nullptr, 0, 0, 0 + ); + } + else + { + ShapeTextPrinter::PrintShape(valueString.c_str(), shapeTextPrintData, position, rect, DSurface::Composite); + } } @@ -206,6 +232,7 @@ void DigitalDisplayTypeClass::Serialize(T& Stm) .Process(this->Shape) .Process(this->Palette) .Process(this->Shape_Spacing) + .Process(this->Shape_PercentageFrame) .Process(this->Percentage) .Process(this->HideMaxValue) .Process(this->VisibleToHouses_Observer) diff --git a/src/New/Type/DigitalDisplayTypeClass.h b/src/New/Type/DigitalDisplayTypeClass.h index 9aad493d3b..c29e01ba2c 100644 --- a/src/New/Type/DigitalDisplayTypeClass.h +++ b/src/New/Type/DigitalDisplayTypeClass.h @@ -18,6 +18,7 @@ class DigitalDisplayTypeClass final : public Enumerable Valueable Shape; CustomPalette Palette; Nullable> Shape_Spacing; + Valueable Shape_PercentageFrame; Valueable Percentage; Nullable HideMaxValue; Valueable VisibleToHouses_Observer; @@ -35,6 +36,7 @@ class DigitalDisplayTypeClass final : public Enumerable , Shape(nullptr) , Palette() , Shape_Spacing() + , Shape_PercentageFrame(false) , Percentage(false) , HideMaxValue() , VisibleToHouses_Observer(true) diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index 06a992b2a6..f72de1d6fd 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -273,7 +273,15 @@ enum class DisplayInfoType : BYTE Tiberium = 6, Experience = 7, Occupants = 8, - GattlingStage = 9 + GattlingStage = 9, + ROF = 10, + Reload = 11, + SpawnTimer = 12, + GattlingTimer = 13, + ProduceCash = 14, + PassengerKill = 15, + AutoDeath = 16, + SuperWeapon = 17 }; class MouseCursorHotSpotX diff --git a/src/Utilities/TemplateDef.h b/src/Utilities/TemplateDef.h index e7497d8be1..5fb05bdfff 100644 --- a/src/Utilities/TemplateDef.h +++ b/src/Utilities/TemplateDef.h @@ -1346,6 +1346,38 @@ if(_strcmpi(parser.value(), #name) == 0){ value = __uuidof(name ## LocomotionCla { value = DisplayInfoType::GattlingStage; } + else if (_strcmpi(str, "rof") == 0) + { + value = DisplayInfoType::ROF; + } + else if (_strcmpi(str, "reload") == 0) + { + value = DisplayInfoType::Reload; + } + else if (_strcmpi(str, "spawntimer") == 0) + { + value = DisplayInfoType::SpawnTimer; + } + else if (_strcmpi(str, "gattlingtimer") == 0) + { + value = DisplayInfoType::GattlingTimer; + } + else if (_strcmpi(str, "producecash") == 0) + { + value = DisplayInfoType::ProduceCash; + } + else if (_strcmpi(str, "passengerkill") == 0) + { + value = DisplayInfoType::PassengerKill; + } + else if (_strcmpi(str, "autodeath") == 0) + { + value = DisplayInfoType::AutoDeath; + } + else if (_strcmpi(str, "superweapon") == 0) + { + value = DisplayInfoType::SuperWeapon; + } else { Debug::INIParseFailed(pSection, pKey, str, "Display info type is invalid"); From 6ff1802ab8b806f6fe35efe3f2734ab5a7cfce10 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Tue, 25 Jun 2024 20:04:05 +0800 Subject: [PATCH 02/19] Little modify --- src/Ext/Techno/Body.Visuals.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index da8fdc4989..a9d095b718 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -330,11 +330,11 @@ void TechnoExt::ProcessDigitalDisplays(TechnoClass* pThis) continue; int value = -1; - int maxValue = -1; + int maxValue = 0; GetValuesForDisplay(pThis, pDisplayType->InfoType, value, maxValue); - if (value == -1 || maxValue == -1) + if (value == -1 || maxValue == 0) continue; const bool isBuilding = pThis->WhatAmI() == AbstractType::Building; @@ -445,7 +445,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pType->IsGattling) return; - value = pThis->CurrentGattlingStage; + value = pThis->GattlingValue == 0 ? 0 : pThis->CurrentGattlingStage + 1; maxValue = pType->WeaponStages; break; } From 2b79461b36877d0599afd660b7f7408f33994233 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 28 Jun 2024 12:50:25 +0800 Subject: [PATCH 03/19] Initialize info --- src/Ext/Techno/Body.Internal.cpp | 24 ++++++++++++++++++++++++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.cpp | 1 + 3 files changed, 26 insertions(+) diff --git a/src/Ext/Techno/Body.Internal.cpp b/src/Ext/Techno/Body.Internal.cpp index 6a5c22f16d..634892206c 100644 --- a/src/Ext/Techno/Body.Internal.cpp +++ b/src/Ext/Techno/Body.Internal.cpp @@ -148,6 +148,30 @@ CoordStruct TechnoExt::GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& return FLH; } +void TechnoExt::ExtData::InitializeDisplayInfo() +{ + auto const pThis = this->OwnerObject(); + auto const pType = pThis->GetTechnoType(); + auto const pPrimary = pThis->GetWeapon(0); + auto const pSecondary = pThis->GetWeapon(1); + + if (pPrimary && pPrimary->WeaponType && pType->LandTargeting != LandTargetingType::Land_Not_OK) + pThis->ChargeTurretDelay = pPrimary->WeaponType->ROF; + else if (pSecondary && pSecondary->WeaponType) + pThis->ChargeTurretDelay = pSecondary->WeaponType->ROF; + + if (pType->Ammo > 0) + pThis->ReloadTimer.TimeLeft = pType->Reload; + + if (auto pTypeExt = this->TypeExtData) + { + auto pDelType = pTypeExt->PassengerDeletionType.get(); + + if (pDelType && !pThis->Passengers.GetFirstPassenger()) + this->PassengerDeletionTimer.TimeLeft = pDelType->Rate; + } +} + void TechnoExt::ExtData::InitializeAttachEffects() { if (auto pTypeExt = this->TypeExtData) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 08e53e1ba4..550ea13c1d 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -115,6 +115,7 @@ class TechnoExt void InitializeAttachEffects(); bool HasAttachedEffects(std::vector attachEffectTypes, bool requireAll, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource, std::vector const& minCounts, std::vector const& maxCounts) const; int GetAttachedEffectCumulativeCount(AttachEffectTypeClass* pAttachEffectType, bool ignoreSameSource = false, TechnoClass* pInvoker = nullptr, AbstractClass* pSource = nullptr) const; + void InitializeDisplayInfo(); virtual ~ExtData() override; diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 6359c2cc22..aa5ed27a96 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -75,6 +75,7 @@ DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2) pExt->CurrentShieldType = pExt->TypeExtData->ShieldType; pExt->InitializeLaserTrails(); pExt->InitializeAttachEffects(); + pExt->InitializeDisplayInfo(); return 0; } From 91cfb867bf1e5682a50e830cf871db83c9dc2e33 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 14 Jul 2024 22:36:30 +0800 Subject: [PATCH 04/19] Fix reload timer not always stop when no ammo --- src/Ext/Techno/Body.Visuals.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 7e14013a91..c810de355d 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -473,7 +473,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (pType->Ammo <= 0) return; - value = pThis->ReloadTimer.GetTimeLeft(); + value = (pThis->Ammo >= pType->Ammo) ? 0 : pThis->ReloadTimer.GetTimeLeft(); maxValue = pThis->ReloadTimer.TimeLeft; break; } From 5678e4b261274fcb2d0b4519ff7019707721ca4e Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Tue, 3 Sep 2024 14:17:20 +0800 Subject: [PATCH 05/19] Update Body.Visuals.cpp --- src/Ext/Techno/Body.Visuals.cpp | 68 ++++++++++++++------------------- 1 file changed, 28 insertions(+), 40 deletions(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index c810de355d..4f5f8d80ef 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -479,23 +479,23 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } case DisplayInfoType::SpawnTimer: { - if (pThis->SpawnManager == nullptr || pType->Spawns == nullptr || pType->SpawnsNumber <= 0) + if (!pThis->SpawnManager || !pType->Spawns || pType->SpawnsNumber <= 0) return; value = 0; - maxValue = pThis->SpawnManager->RegenRate; for (int i = 0; i < pType->SpawnsNumber; i++) { - if (pThis->SpawnManager->SpawnedNodes[i]->Status == SpawnNodeStatus::Dead) - { - int thisValue = pThis->SpawnManager->SpawnedNodes[i]->SpawnTimer.GetTimeLeft(); + if (pThis->SpawnManager->SpawnedNodes[i]->Status != SpawnNodeStatus::Dead) + continue; - if (thisValue < value) - value = thisValue; - } + const int thisValue = pThis->SpawnManager->SpawnedNodes[i]->SpawnTimer.GetTimeLeft(); + + if (thisValue < value || !value) + value = thisValue; } + maxValue = pThis->SpawnManager->RegenRate; break; } case DisplayInfoType::GattlingTimer: @@ -504,34 +504,25 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType return; const int thisStage = pThis->CurrentGattlingStage; + Point2D values = Point2D::Empty; if (pThis->Veterancy.IsElite()) { if (thisStage > 0) - { - value = pThis->GattlingValue - pType->EliteStage[thisStage - 1]; - maxValue = pType->EliteStage[thisStage] - pType->EliteStage[thisStage - 1]; - } + values = Point2D{ (pThis->GattlingValue - pType->EliteStage[thisStage - 1]), (pType->EliteStage[thisStage] - pType->EliteStage[thisStage - 1]) }; else - { - value = pThis->GattlingValue; - maxValue = pType->EliteStage[thisStage]; - } + values = Point2D{ pThis->GattlingValue, pType->EliteStage[thisStage] }; } else { if (thisStage > 0) - { - value = pThis->GattlingValue - pType->WeaponStage[thisStage - 1]; - maxValue = pType->WeaponStage[thisStage] - pType->WeaponStage[thisStage - 1]; - } + values = Point2D{ (pThis->GattlingValue - pType->WeaponStage[thisStage - 1]), (pType->WeaponStage[thisStage] - pType->WeaponStage[thisStage - 1]) }; else - { - value = pThis->GattlingValue; - maxValue = pType->WeaponStage[thisStage]; - } + values = Point2D{ pThis->GattlingValue, pType->WeaponStage[thisStage] }; } + value = values.X; + maxValue = values.Y; break; } case DisplayInfoType::ProduceCash: @@ -539,8 +530,8 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (pThis->WhatAmI() != AbstractType::Building) return; - const auto pBuildingType = abstract_cast(pType); - const auto pBuilding = abstract_cast(pThis); + const auto pBuildingType = static_cast(pType); + const auto pBuilding = static_cast(pThis); if (pBuildingType->ProduceCashAmount <= 0) return; @@ -567,17 +558,15 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pTypeExt || !pTypeExt->AutoDeath_Behavior.isset()) return; + Point2D values = Point2D::Empty; + if (pTypeExt->AutoDeath_AfterDelay > 0) - { - value = pExt->AutoDeathTimer.GetTimeLeft(); - maxValue = pExt->AutoDeathTimer.TimeLeft; - } + values = Point2D{ pExt->AutoDeathTimer.GetTimeLeft(), pExt->AutoDeathTimer.TimeLeft }; else if (pTypeExt->AutoDeath_OnAmmoDepletion && pType->Ammo > 0) - { - value = pThis->Ammo; - maxValue = pType->Ammo; - } + values = Point2D{ pThis->Ammo, pType->Ammo }; + value = values.X; + maxValue = values.Y; break; } case DisplayInfoType::SuperWeapon: @@ -585,7 +574,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (pThis->WhatAmI() != AbstractType::Building || !pThis->Owner) return; - const auto pBuildingType = abstract_cast(pType); + const auto pBuildingType = static_cast(pType); const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuildingType); SuperClass* pSuper = nullptr; @@ -596,12 +585,11 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType else if (pBuildingTypeExt->SuperWeapons.size() > 0) pSuper = pThis->Owner->Supers.GetItem(pBuildingTypeExt->SuperWeapons[0]); - if (pSuper) - { - value = pSuper->RechargeTimer.GetTimeLeft(); - maxValue = pSuper->RechargeTimer.TimeLeft; - } + if (!pSuper) + return; + value = pSuper->RechargeTimer.GetTimeLeft(); + maxValue = pSuper->RechargeTimer.TimeLeft; break; } default: From 68cf051cf11c8c22d7c800a18006282097b3cd0a Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Tue, 3 Sep 2024 15:55:40 +0800 Subject: [PATCH 06/19] New function --- CREDITS.md | 2 +- docs/User-Interface.md | 5 ++-- docs/Whats-New.md | 2 +- src/Ext/Techno/Body.Visuals.cpp | 29 ++++++++++++++++++++++-- src/New/Type/DigitalDisplayTypeClass.cpp | 2 ++ src/New/Type/DigitalDisplayTypeClass.h | 2 ++ src/Utilities/Enum.h | 4 +++- src/Utilities/TemplateDef.h | 8 +++++++ 8 files changed, 47 insertions(+), 7 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 3e1e9c552f..6b671dc81a 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -354,7 +354,7 @@ This page lists all the individual contributions to the project by their author. - Recreate the weed-charging of SWs like the TS Chemical Missile - Allow to change the speed of gas particles - **CrimRecya** - - Several new Infotypes and a new single frame display method + - Several new Infotypes, no display in specific status and a new single frame display method - **Ollerus** - Build limit group enhancement - Customizable rocker amplitude diff --git a/docs/User-Interface.md b/docs/User-Interface.md index d0b4ea5c9a..98515188be 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -45,7 +45,7 @@ IngameScore.LoseTheme= ; Soundtrack theme ID - Default `Offset.ShieldDelta` for `InfoType=Shield` is `0,-10`, `0,0` for others. - Default `Shape.Spacing` for buildings is `4,-2`, `4,0` for others. - `ValueScaleDivisor` can be used to adjust scale of displayed values. Both the current & maximum value will be divided by the integer number given, if higher than 1. - + In `rulesmd.ini`: ```ini [DigitalDisplayTypes] @@ -59,7 +59,7 @@ Aircraft.DefaultDigitalDisplayTypes= ; list of DigitalDisplayTypes [SOMEDIGITALDISPLAYTYPE] ; DigitalDisplayType ; Generic -InfoType=Health ; Displayed value enumeration (health|shield|ammo|mindcontrol|spawns|passengers|tiberium|experience|occupants|gattlingstage|ROF|Reload|SpawnTimer|GattlingTimer|ProduceCash|PassengerKill|AutoDeath|SuperWeapon) +InfoType=Health ; Displayed value enumeration (health|shield|ammo|mindcontrol|spawns|passengers|tiberium|experience|occupants|gattlingstage|ROF|Reload|SpawnTimer|GattlingTimer|ProduceCash|PassengerKill|AutoDeath|SuperWeapon|IronCurtain|TemporalLife) Offset=0,0 ; integers - horizontal, vertical Offset.ShieldDelta= ; integers - horizontal, vertical Align=right ; Text alignment enumeration (left|right|center/centre) @@ -70,6 +70,7 @@ Percentage=false ; boolean HideMaxValue=false ; boolean VisibleToHouses=owner ; Affected house enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) VisibleToHouses.Observer=true ; boolean +VisibleInSpecialState=true ; boolean ValueScaleDivisor=1 ; integer ; Text Text.Color=0,255,0 ; integers - Red, Green, Blue diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 54b73df387..bc163c936e 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -414,7 +414,7 @@ New: - AttachEffect types with new features like custom tint and weapon range modifier (by Starkku) - Force shield effect sync on deploy & vs. organic targets effect customization to complement the Iron Curtain ones (by Starkku) - Map trigger action 41 (Play animation at waypoint) now uses additional parameter to determine if animation can play sound, deal damage etc. (by Starkku) -- Several new Infotypes and a new single frame display method(by CrimRecya) +- Several new Infotypes, no display in specific status and a new single frame display method (by CrimRecya) - Allow restricting how many times per frame a single radiation site can damage a building (by Starkku) - Allow explicitly setting the superweapons AI uses for Chronoshift script actions (by Starkku) - Allow customizing Aircraft weapon strafing regardless of `ROT` and `Strafing.Shots` values beyond 5 (by Trsdy) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 49492aee9d..d8ccc1e6d3 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -336,6 +336,9 @@ void TechnoExt::ProcessDigitalDisplays(TechnoClass* pThis) if (!HouseClass::IsCurrentPlayerObserver() && !EnumFunctions::CanTargetHouse(pDisplayType->VisibleToHouses, pThis->Owner, HouseClass::CurrentPlayer)) continue; + if (!pDisplayType->VisibleInSpecialState && (pThis->TemporalTargetingMe || pThis->IsIronCurtained())) + continue; + int value = -1; int maxValue = 0; @@ -591,8 +594,30 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pSuper) return; - value = pSuper->RechargeTimer.GetTimeLeft(); - maxValue = pSuper->RechargeTimer.TimeLeft; + value = (pSuper->RechargeTimer.GetTimeLeft() + 14) / 15; + maxValue = pSuper->RechargeTimer.TimeLeft / 15; + break; + } + case DisplayInfoType::IronCurtain: + { + if (!pThis->IsIronCurtained()) + return; + + const CDTimerClass* const timer = &pThis->IronCurtainTimer; + + value = (timer->GetTimeLeft() + 14) / 15; + maxValue = timer->TimeLeft / 15; + break; + } + case DisplayInfoType::TemporalLife: + { + const TemporalClass* const pTemporal = pThis->TemporalTargetingMe; + + if (!pTemporal) + return; + + value = (pTemporal->WarpRemaining + 14) / 15; + maxValue = (pType->Strength * 10) / 15; break; } default: diff --git a/src/New/Type/DigitalDisplayTypeClass.cpp b/src/New/Type/DigitalDisplayTypeClass.cpp index 67074c79c5..8af8a25ac9 100644 --- a/src/New/Type/DigitalDisplayTypeClass.cpp +++ b/src/New/Type/DigitalDisplayTypeClass.cpp @@ -21,6 +21,7 @@ void DigitalDisplayTypeClass::LoadFromINI(CCINIClass* pINI) this->Text_Color.Read(exINI, section, "Text.Color.%s"); this->Text_Background.Read(exINI, section, "Text.Background"); + this->VisibleInSpecialState.Read(exINI, section, "VisibleInSpecialState"); this->Offset.Read(exINI, section, "Offset"); this->Offset_ShieldDelta.Read(exINI, section, "Offset.ShieldDelta"); this->Align.Read(exINI, section, "Align"); @@ -224,6 +225,7 @@ void DigitalDisplayTypeClass::Serialize(T& Stm) Stm .Process(this->Text_Color) .Process(this->Text_Background) + .Process(this->VisibleInSpecialState) .Process(this->Offset) .Process(this->Offset_ShieldDelta) .Process(this->Align) diff --git a/src/New/Type/DigitalDisplayTypeClass.h b/src/New/Type/DigitalDisplayTypeClass.h index 96b6c06ad4..e38e3c0bcd 100644 --- a/src/New/Type/DigitalDisplayTypeClass.h +++ b/src/New/Type/DigitalDisplayTypeClass.h @@ -10,6 +10,7 @@ class DigitalDisplayTypeClass final : public Enumerable public: Damageable Text_Color; Valueable Text_Background; + Valueable VisibleInSpecialState; Valueable> Offset; Nullable> Offset_ShieldDelta; Valueable Align; @@ -29,6 +30,7 @@ class DigitalDisplayTypeClass final : public Enumerable DigitalDisplayTypeClass(const char* pTitle = NONE_STR) : Enumerable(pTitle) , Text_Color({ 0, 255, 0 }, { 255,255,0 }, { 255,0,0 }) , Text_Background(false) + , VisibleInSpecialState(true) , Offset({ 0, 0 }) , Offset_ShieldDelta() , Align(TextAlign::Right) diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index dd6a32f79c..59f439f0fa 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -283,7 +283,9 @@ enum class DisplayInfoType : BYTE ProduceCash = 14, PassengerKill = 15, AutoDeath = 16, - SuperWeapon = 17 + SuperWeapon = 17, + IronCurtain = 18, + TemporalLife = 19 }; class MouseCursorHotSpotX diff --git a/src/Utilities/TemplateDef.h b/src/Utilities/TemplateDef.h index f4de26c774..0a4653d75b 100644 --- a/src/Utilities/TemplateDef.h +++ b/src/Utilities/TemplateDef.h @@ -1391,6 +1391,14 @@ if(_strcmpi(parser.value(), #name) == 0){ value = __uuidof(name ## LocomotionCla { value = DisplayInfoType::SuperWeapon; } + else if (_strcmpi(str, "ironcurtain") == 0) + { + value = DisplayInfoType::IronCurtain; + } + else if (_strcmpi(str, "temporallife") == 0) + { + value = DisplayInfoType::TemporalLife; + } else { Debug::INIParseFailed(pSection, pKey, str, "Display info type is invalid"); From 28f7b4f0edd2dd60c4980c2546c2335976284e73 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Tue, 3 Sep 2024 16:11:33 +0800 Subject: [PATCH 07/19] Update docs --- docs/User-Interface.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 98515188be..d55dbe5a03 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -37,7 +37,8 @@ IngameScore.LoseTheme= ; Soundtrack theme ID - `Anchor.Horizontal` and `Anchor.Vertical` set the anchor point from which the display is drawn (depending on `Align`) relative to unit's center/selection box. For buildings, `Anchor.Building` is used instead. - `Offset` and `Offset.ShieldDelta` (the latter applied when a shield is active) can be used to further modify the position. - By default, values are displayed in `current/maximum` format (i.e. 20/40). `HideMaxValue=yes` will make the counter show only the current value (i.e. 20). `Percentage=yes` changes the format to `percent%` (i.e. 50%). - - `CanSee` and `CanSee.Observer` can limit visibility to specific players. + - `VisibleToHouses` and `VisibleToHouses.Observer` can limit visibility to specific players. + - `VisibleInSpecialState` controls whether this display type will show when the owner is in ironcurtain or is attacked by a temporal weapon. - The digits can be either a custom shape (.shp) or text drawn using the game font. This depends on whether `Shape` is set. - `Text.Color`, `Text.Color.ConditionYellow` and `Text.Color.ConditionRed` allow customization of the font color. `Text.Background=yes` will additionally draw a black rectangle background. - When using shapes, a custom palette can be specified with `Palette`. `Shape.Spacing` controls pixel buffer between characters. If `Shape.PercentageFrame` set to true, it will only draw one frame that corresponds to total frames by percentage. From fe88dec26a00498f71914877c27ad7a9d498b10d Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Tue, 3 Sep 2024 16:28:32 +0800 Subject: [PATCH 08/19] Fix max value --- src/Ext/Techno/Body.Visuals.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index d8ccc1e6d3..266a4db9f8 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -595,7 +595,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType return; value = (pSuper->RechargeTimer.GetTimeLeft() + 14) / 15; - maxValue = pSuper->RechargeTimer.TimeLeft / 15; + maxValue = (pSuper->RechargeTimer.TimeLeft + 14) / 15; break; } case DisplayInfoType::IronCurtain: @@ -606,7 +606,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType const CDTimerClass* const timer = &pThis->IronCurtainTimer; value = (timer->GetTimeLeft() + 14) / 15; - maxValue = timer->TimeLeft / 15; + maxValue = (timer->TimeLeft + 14) / 15; break; } case DisplayInfoType::TemporalLife: @@ -617,7 +617,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType return; value = (pTemporal->WarpRemaining + 14) / 15; - maxValue = (pType->Strength * 10) / 15; + maxValue = (pType->Strength * 10 + 14) / 15; break; } default: From 27fc196ccbbbac63c6edd3f9338f7598fdbc39a1 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 6 Sep 2024 18:21:38 +0800 Subject: [PATCH 09/19] Leave these to customization --- src/Ext/Techno/Body.Visuals.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 266a4db9f8..effcce71a3 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -594,8 +594,8 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pSuper) return; - value = (pSuper->RechargeTimer.GetTimeLeft() + 14) / 15; - maxValue = (pSuper->RechargeTimer.TimeLeft + 14) / 15; + value = pSuper->RechargeTimer.GetTimeLeft(); + maxValue = pSuper->RechargeTimer.TimeLeft; break; } case DisplayInfoType::IronCurtain: @@ -605,8 +605,8 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType const CDTimerClass* const timer = &pThis->IronCurtainTimer; - value = (timer->GetTimeLeft() + 14) / 15; - maxValue = (timer->TimeLeft + 14) / 15; + value = timer->GetTimeLeft(); + maxValue = timer->TimeLeft; break; } case DisplayInfoType::TemporalLife: @@ -616,8 +616,8 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pTemporal) return; - value = (pTemporal->WarpRemaining + 14) / 15; - maxValue = (pType->Strength * 10 + 14) / 15; + value = pTemporal->WarpRemaining; + maxValue = pType->Strength * 10; break; } default: From 23006a2dd4e38fa8344a6e6ee12c54e3263bbdda Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Thu, 24 Oct 2024 02:56:19 +0800 Subject: [PATCH 10/19] Change reload max value --- src/Ext/Techno/Body.Internal.cpp | 3 --- src/Ext/Techno/Body.Visuals.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Ext/Techno/Body.Internal.cpp b/src/Ext/Techno/Body.Internal.cpp index 25fa35e52f..2be2ead101 100644 --- a/src/Ext/Techno/Body.Internal.cpp +++ b/src/Ext/Techno/Body.Internal.cpp @@ -160,9 +160,6 @@ void TechnoExt::ExtData::InitializeDisplayInfo() else if (pSecondary && pSecondary->WeaponType) pThis->ChargeTurretDelay = pSecondary->WeaponType->ROF; - if (pType->Ammo > 0) - pThis->ReloadTimer.TimeLeft = pType->Reload; - if (auto pTypeExt = this->TypeExtData) { auto pDelType = pTypeExt->PassengerDeletionType.get(); diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 3f4ee616db..d879a66cf1 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -476,7 +476,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType return; value = (pThis->Ammo >= pType->Ammo) ? 0 : pThis->ReloadTimer.GetTimeLeft(); - maxValue = pThis->ReloadTimer.TimeLeft; + maxValue = (pThis->Ammo || pType->EmptyReload <= 0) ? pType->Reload : pType->EmptyReload; break; } case DisplayInfoType::SpawnTimer: From 9d39b7131470eb23ac604047d494d4e298194b43 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 8 Nov 2024 00:11:34 +0800 Subject: [PATCH 11/19] Const auto --- src/Ext/Techno/Body.Visuals.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index d879a66cf1..3ad1d6bd10 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -2,8 +2,10 @@ #include #include +#include +#include #include - +#include #include void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, RectangleStruct* pBounds) @@ -379,7 +381,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } case DisplayInfoType::Shield: { - if (pExt->Shield == nullptr || pExt->Shield->IsBrokenAndNonRespawning()) + if (!pExt->Shield || pExt->Shield->IsBrokenAndNonRespawning()) return; value = pExt->Shield->GetHP(); @@ -397,7 +399,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } case DisplayInfoType::MindControl: { - if (pThis->CaptureManager == nullptr) + if (!pThis->CaptureManager) return; value = pThis->CaptureManager->ControlNodes.Count; @@ -406,7 +408,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } case DisplayInfoType::Spawns: { - if (pThis->SpawnManager == nullptr || pType->Spawns == nullptr || pType->SpawnsNumber <= 0) + if (!pThis->SpawnManager || !pType->Spawns || pType->SpawnsNumber <= 0) return; value = pThis->SpawnManager->CountAliveSpawns(); @@ -442,8 +444,8 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (pThis->WhatAmI() != AbstractType::Building) return; - const auto pBuildingType = abstract_cast(pType); - const auto pBuilding = abstract_cast(pThis); + const auto pBuildingType = static_cast(pType); + const auto pBuilding = static_cast(pThis); if (!pBuildingType->CanBeOccupied) return; @@ -491,7 +493,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (pThis->SpawnManager->SpawnedNodes[i]->Status != SpawnNodeStatus::Dead) continue; - const int thisValue = pThis->SpawnManager->SpawnedNodes[i]->SpawnTimer.GetTimeLeft(); + const auto thisValue = pThis->SpawnManager->SpawnedNodes[i]->SpawnTimer.GetTimeLeft(); if (thisValue < value || !value) value = thisValue; @@ -505,8 +507,8 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pType->IsGattling) return; - const int thisStage = pThis->CurrentGattlingStage; - Point2D values = Point2D::Empty; + const auto thisStage = pThis->CurrentGattlingStage; + auto values = Point2D::Empty; if (pThis->Veterancy.IsElite()) { @@ -560,7 +562,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pTypeExt || !pTypeExt->AutoDeath_Behavior.isset()) return; - Point2D values = Point2D::Empty; + auto values = Point2D::Empty; if (pTypeExt->AutoDeath_AfterDelay > 0) values = Point2D{ pExt->AutoDeathTimer.GetTimeLeft(), pExt->AutoDeathTimer.TimeLeft }; @@ -599,7 +601,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pThis->IsIronCurtained()) return; - const CDTimerClass* const timer = &pThis->IronCurtainTimer; + const auto timer = &pThis->IronCurtainTimer; value = timer->GetTimeLeft(); maxValue = timer->TimeLeft; @@ -607,7 +609,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } case DisplayInfoType::TemporalLife: { - const TemporalClass* const pTemporal = pThis->TemporalTargetingMe; + const auto pTemporal = pThis->TemporalTargetingMe; if (!pTemporal) return; From f260915c799474119d0b296bb54855d83898661c Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 9 Nov 2024 13:42:03 +0800 Subject: [PATCH 12/19] Const auto --- src/Ext/Techno/Body.Internal.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ext/Techno/Body.Internal.cpp b/src/Ext/Techno/Body.Internal.cpp index d17e69bf52..a18fa7d340 100644 --- a/src/Ext/Techno/Body.Internal.cpp +++ b/src/Ext/Techno/Body.Internal.cpp @@ -146,19 +146,19 @@ CoordStruct TechnoExt::GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& void TechnoExt::ExtData::InitializeDisplayInfo() { - auto const pThis = this->OwnerObject(); - auto const pType = pThis->GetTechnoType(); - auto const pPrimary = pThis->GetWeapon(0); - auto const pSecondary = pThis->GetWeapon(1); + const auto pThis = this->OwnerObject(); + const auto pType = pThis->GetTechnoType(); + const auto pPrimary = pThis->GetWeapon(0); + const auto pSecondary = pThis->GetWeapon(1); if (pPrimary && pPrimary->WeaponType && pType->LandTargeting != LandTargetingType::Land_Not_OK) pThis->ChargeTurretDelay = pPrimary->WeaponType->ROF; else if (pSecondary && pSecondary->WeaponType) pThis->ChargeTurretDelay = pSecondary->WeaponType->ROF; - if (auto pTypeExt = this->TypeExtData) + if (const auto pTypeExt = this->TypeExtData) { - auto pDelType = pTypeExt->PassengerDeletionType.get(); + const auto pDelType = pTypeExt->PassengerDeletionType.get(); if (pDelType && !pThis->Passengers.GetFirstPassenger()) this->PassengerDeletionTimer.TimeLeft = pDelType->Rate; From 3285b762af85d5ddfc9950f09d40f673d13c5020 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 14 Feb 2025 00:27:51 +0800 Subject: [PATCH 13/19] Core Co-Authored-By: Netsu_Negi <71598172+NetsuNegi@users.noreply.github.com> --- CREDITS.md | 1 + docs/Fixed-or-Improved-Logics.md | 1 + docs/Whats-New.md | 1 + src/Misc/Hooks.BugFixes.cpp | 15 +++++++++++++++ 4 files changed, 18 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index 24236c02d5..b55139e364 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -349,6 +349,7 @@ This page lists all the individual contributions to the project by their author. - Disguised units not using the correct palette if target has custom palette bugfix - Tunnel/Walk/Mech locomotor being stuck when moving too fast bugfix - Assign Super Weapon cameo to any sidebar tab + - Fix impassable invisible barrier created by chronosphere on uncrushable unit. - **Apollo** - Translucent SHP drawing patches - **ststl** - Customizable ShowTimer priority of superweapons diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index c4ed7a68cc..cdbaeb24ce 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -190,6 +190,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Allowed `AuxBuilding` to count building upgrades. - Fix the bug that parasite will vanish if it missed its target when its previous cell is occupied. - Prevent the units with locomotors that cause problems from entering the tank bunker. +- Fix an issue where a unit will leave an impassable invisible barrier in its original position when it is teleported by ChronoSphere onto an uncrushable unit and self destruct. ## Fixes / interactions with other extensions diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 645cc7e627..313b8498a9 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -330,6 +330,7 @@ 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) - Fix the bug that parasite will vanish if it missed its target when its previous cell is occupied (by 航味麻酱) - Prevent the units with locomotors that cause problems from entering the tank bunker (by TaranDahl) +- Fix an issue where a unit will leave an impassable invisible barrier in its original position when it is teleported by ChronoSphere onto an uncrushable unit and self destruct (by NetsuNegi) Phobos fixes: - Type conversion on Warheads and Superweapons will no longer recursively convert units if applicable conversion pairs are listed, and only first applicable pair takes effect (by Starkku) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index e13fd9bb60..cceca057ed 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -1089,6 +1089,21 @@ DEFINE_HOOK(0x743664, UnitClass_ReadFromINI_Follower3, 0x6) #pragma endregion +#pragma region TeleportLocomotionOccupationFix + +DEFINE_HOOK(0x71872C, TeleportLocomotionClass_MakeRoom_OccupationFix, 0x9) +{ + enum { SkipMarkOccupation = 0x71878F }; + + GET(const LocomotionClass* const, pLoco, EBP); + + const auto pFoot = pLoco->LinkedTo; + + return (pFoot && pFoot->IsAlive && pFoot->Health > 0 && !pFoot->IsSinking) ? 0 : SkipMarkOccupation; +} + +#pragma endregion + #pragma region StopEventFix DEFINE_HOOK(0x4C75DA, EventClass_RespondToEvent_Stop, 0x6) From 93b5f0689d120d0891a59ffa0ff11c2574b78ef8 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 14 Feb 2025 13:10:33 +0800 Subject: [PATCH 14/19] Check InLimbo --- src/Misc/Hooks.BugFixes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index cceca057ed..a9732a8f77 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -1099,7 +1099,7 @@ DEFINE_HOOK(0x71872C, TeleportLocomotionClass_MakeRoom_OccupationFix, 0x9) const auto pFoot = pLoco->LinkedTo; - return (pFoot && pFoot->IsAlive && pFoot->Health > 0 && !pFoot->IsSinking) ? 0 : SkipMarkOccupation; + return (pFoot && pFoot->IsAlive && !pFoot->InLimbo && pFoot->Health > 0 && !pFoot->IsSinking) ? 0 : SkipMarkOccupation; } #pragma endregion From 06664e94f92e68f8ad22554354f7160eea64e8d4 Mon Sep 17 00:00:00 2001 From: Fryone Date: Thu, 20 Feb 2025 09:57:57 +0300 Subject: [PATCH 15/19] [Minor] Fix Ammo features and tank bunkers. (#1420) - fixing ammo block inside tank bunkers; - partially fixing ammo adding/substracting inside tank bunkers still problems with converters --------- Co-authored-by: Coronia <2217891145@qq.com> --- docs/Fixed-or-Improved-Logics.md | 4 ++++ docs/Whats-New.md | 3 ++- src/Ext/Unit/Hooks.Unload.cpp | 7 ++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 543f7928e5..af0848949a 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1339,6 +1339,10 @@ In `rulesmd.ini`: Ammo.AddOnDeploy=0 ; integer ``` +```{warning} +Due to technical constraints, units that use `Convert.Deploy` from [Ares’ Type Conversion](https://ares-developers.github.io/Ares-docs/new/typeconversion.html) to change type with `Ammo.AddOnDeploy` will add or substract ammo despite of convertion success. This will also happen when unit exits tank bunker. +``` + ## Veinholes & Weeds ### Veinholes diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 5aab8b4fdd..3ecc07038d 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -332,7 +332,8 @@ Vanilla fixes: - Fix an issue where a unit will leave an impassable invisible barrier in its original position when it is teleported by ChronoSphere onto an uncrushable unit and self destruct (by NetsuNegi) Phobos fixes: -- Type conversion on Warheads and Superweapons will no longer recursively convert units if applicable conversion pairs are listed, and only first applicable pair takes effect (by Starkku) +- Fixed `Ammo.DeployUnlockMinimumAmount`/`Ammo.DeployUnlockMaximumAmount` behavior inside tank bunkers (by Fryone) +- Fixed `Ammo.AddOnDeploy` behavior inside tank bunkers for non-converters (by Fryone) Fixes / interactions with other extensions: - Allowed `AuxBuilding` and Ares' `SW.Aux/NegBuildings` to count building upgrades (by Ollerus) diff --git a/src/Ext/Unit/Hooks.Unload.cpp b/src/Ext/Unit/Hooks.Unload.cpp index d477d841b5..6fc79e01dd 100644 --- a/src/Ext/Unit/Hooks.Unload.cpp +++ b/src/Ext/Unit/Hooks.Unload.cpp @@ -23,7 +23,8 @@ void UnitDeployConvertHelpers::RemoveDeploying(REGISTERS* R) const bool canDeploy = pThis->CanDeploySlashUnload(); R->AL(canDeploy); - if (!canDeploy) + + if (!canDeploy || pThis->BunkerLinkedItem) return; const bool skipMinimum = pThisType->Ammo_DeployUnlockMinimumAmount < 0; @@ -46,7 +47,7 @@ void UnitDeployConvertHelpers::ChangeAmmo(REGISTERS* R) GET(UnitClass*, pThis, ECX); auto const pThisExt = TechnoTypeExt::ExtMap.Find(pThis->Type); - if (pThis->Deployed && !pThis->Deploying && pThisExt->Ammo_AddOnDeploy) + if (pThis->Deployed && !pThis->BunkerLinkedItem && !pThis->Deploying && pThisExt->Ammo_AddOnDeploy) { const int ammoCalc = std::max(pThis->Ammo + pThisExt->Ammo_AddOnDeploy, 0); pThis->Ammo = std::min(pThis->Type->Ammo, ammoCalc); @@ -60,7 +61,7 @@ void UnitDeployConvertHelpers::ChangeAmmoOnUnloading(REGISTERS* R) GET(UnitClass*, pThis, ESI); auto const pThisExt = TechnoTypeExt::ExtMap.Find(pThis->Type); - if (pThis->Type->IsSimpleDeployer && pThisExt->Ammo_AddOnDeploy && (pThis->Type->UnloadingClass == nullptr)) + if (pThis->Type->IsSimpleDeployer && !pThis->BunkerLinkedItem && pThisExt->Ammo_AddOnDeploy && (pThis->Type->UnloadingClass == nullptr)) { const int ammoCalc = std::max(pThis->Ammo + pThisExt->Ammo_AddOnDeploy, 0); pThis->Ammo = std::min(pThis->Type->Ammo, ammoCalc); From aa4e9525a3929c07e19957a76536c222c5d69b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:18:14 +0800 Subject: [PATCH 16/19] [Minor] Disallow other actions for building in planning mode (#1524) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --------- Co-authored-by: 绯红热茶 <335958461@qq.com> --- src/Ext/Building/Hooks.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index ae18f37b0f..aa7a789283 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #pragma region Update @@ -727,4 +728,19 @@ bool __fastcall BuildingTypeClass_CanUseWaypoint(BuildingTypeClass* pThis) } DEFINE_JUMP(VTABLE, 0x7E4610, GET_OFFSET(BuildingTypeClass_CanUseWaypoint)) +DEFINE_HOOK(0x4AE95E, DisplayClass_sub_4AE750_DisallowBuildingNonAttackPlanning, 0x5) +{ + enum { SkipGameCode = 0x4AE982 }; + + GET(ObjectClass* const, pObject, ECX); + LEA_STACK(CellStruct*, pCell, STACK_OFFSET(0x20, 0x8)); + + auto action = pObject->MouseOverCell(pCell); + + if (!PlanningNodeClass::PlanningModeActive || pObject->WhatAmI() != AbstractType::Building || action == Action::Attack) + pObject->CellClickedAction(action, pCell, pCell, false); + + return SkipGameCode; +} + #pragma endregion From 886cd122a666ced42f990f3c4107bc5532ebe829 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Tue, 25 Feb 2025 01:15:40 +0800 Subject: [PATCH 17/19] New `ValueAsTimer` --- docs/User-Interface.md | 9 +- src/Ext/Techno/Body.Internal.cpp | 21 +-- src/Ext/Techno/Body.Visuals.cpp | 176 ++++++++++++----------- src/New/Type/DigitalDisplayTypeClass.cpp | 71 ++++++--- src/New/Type/DigitalDisplayTypeClass.h | 14 +- 5 files changed, 163 insertions(+), 128 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index aca97910ad..55ccd69cb3 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -37,16 +37,16 @@ IngameScore.LoseTheme= ; Soundtrack theme ID - You can now configure various types of numerical counters to be displayed over Techno to represent its attributes, such as health points or shield points and can be turned on or off via a [new hotkey](#toggle-digital-display). - `Anchor.Horizontal` and `Anchor.Vertical` set the anchor point from which the display is drawn (depending on `Align`) relative to unit's center/selection box. For buildings, `Anchor.Building` is used instead. - `Offset` and `Offset.ShieldDelta` (the latter applied when a shield is active) can be used to further modify the position. - - By default, values are displayed in `current/maximum` format (i.e. 20/40). `HideMaxValue=yes` will make the counter show only the current value (i.e. 20). `Percentage=yes` changes the format to `percent%` (i.e. 50%). + - By default, values are displayed in `current/maximum` format (i.e. 20/40). `HideMaxValue=true` will make the counter show only the current value (i.e. 20), the default value is whether the techno is infantry or not. `Percentage=true` changes the format to `percent%` (i.e. 50%). `ValueAsTimer` controls whether the value will be displayed in the form of a timer (i.e. 5:00, 25:00 or 1:00:00). - `VisibleToHouses` and `VisibleToHouses.Observer` can limit visibility to specific players. - `VisibleInSpecialState` controls whether this display type will show when the owner is in ironcurtain or is attacked by a temporal weapon. - The digits can be either a custom shape (.shp) or text drawn using the game font. This depends on whether `Shape` is set. - `Text.Color`, `Text.Color.ConditionYellow` and `Text.Color.ConditionRed` allow customization of the font color. `Text.Background=yes` will additionally draw a black rectangle background. - When using shapes, a custom palette can be specified with `Palette`. `Shape.Spacing` controls pixel buffer between characters. If `Shape.PercentageFrame` set to true, it will only draw one frame that corresponds to total frames by percentage. - - Frames 0-9 will be used as digits when the owner's health bar is green, 10-19 when yellow, 20-29 when red. For `/` and `%` characters, frame numbers are 30-31, 32-33, 34-35, respectively. + - Frames 0-9 will be used as digits when the owner's health bar is green, 10-19 when yellow, 20-29 when red. For `/` and `%` (or `:` if set `ValueAsTimer` to true) characters, frame numbers are 30-31, 32-33, 34-35, respectively. - Default `Offset.ShieldDelta` for `InfoType=Shield` is `0,-10`, `0,0` for others. - Default `Shape.Spacing` for buildings is `4,-2`, `4,0` for others. - - `ValueScaleDivisor` can be used to adjust scale of displayed values. Both the current & maximum value will be divided by the integer number given, if higher than 1. + - `ValueScaleDivisor` can be used to adjust scale of displayed values. Both the current & maximum value will be divided by the integer number given, if higher than 1. Default to 1 (or 15 when set `ValueAsTimer` to true). In `rulesmd.ini`: ```ini @@ -73,7 +73,8 @@ HideMaxValue=false ; boolean VisibleToHouses=owner ; Affected house enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) VisibleToHouses.Observer=true ; boolean VisibleInSpecialState=true ; boolean -ValueScaleDivisor=1 ; integer +ValueScaleDivisor= ; integer +ValueAsTimer=false ; boolean ; Text Text.Color=0,255,0 ; integers - Red, Green, Blue Text.Color.ConditionYellow=255,255,0 ; integers - Red, Green, Blue diff --git a/src/Ext/Techno/Body.Internal.cpp b/src/Ext/Techno/Body.Internal.cpp index 0782795086..8990e072d1 100644 --- a/src/Ext/Techno/Body.Internal.cpp +++ b/src/Ext/Techno/Body.Internal.cpp @@ -147,22 +147,13 @@ CoordStruct TechnoExt::GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& void TechnoExt::ExtData::InitializeDisplayInfo() { const auto pThis = this->OwnerObject(); - const auto pType = pThis->GetTechnoType(); - const auto pPrimary = pThis->GetWeapon(0); - const auto pSecondary = pThis->GetWeapon(1); + const auto pPrimary = pThis->GetWeapon(0)->WeaponType; + pThis->RearmTimer.Start(0); - if (pPrimary && pPrimary->WeaponType && pType->LandTargeting != LandTargetingType::Land_Not_OK) - pThis->ChargeTurretDelay = pPrimary->WeaponType->ROF; - else if (pSecondary && pSecondary->WeaponType) - pThis->ChargeTurretDelay = pSecondary->WeaponType->ROF; - - if (const auto pTypeExt = this->TypeExtData) - { - const auto pDelType = pTypeExt->PassengerDeletionType.get(); - - if (pDelType && !pThis->Passengers.GetFirstPassenger()) - this->PassengerDeletionTimer.TimeLeft = pDelType->Rate; - } + if (pPrimary && pThis->GetTechnoType()->LandTargeting != LandTargetingType::Land_Not_OK) + pThis->RearmTimer.TimeLeft = pPrimary->ROF; + else if (const auto pSecondary = pThis->GetWeapon(1)->WeaponType) + pThis->RearmTimer.TimeLeft = pSecondary->ROF; } void TechnoExt::ExtData::InitializeAttachEffects() diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 3ad1d6bd10..19c35673ab 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -342,13 +342,15 @@ void TechnoExt::ProcessDigitalDisplays(TechnoClass* pThis) GetValuesForDisplay(pThis, pDisplayType->InfoType, value, maxValue); - if (value == -1 || maxValue == 0) + if (value <= -1 || maxValue <= 0) continue; - if (pDisplayType->ValueScaleDivisor > 1) + const auto divisor = pDisplayType->ValueScaleDivisor.Get(pDisplayType->ValueAsTimer ? 15 : 1); + + if (divisor > 1) { - value = Math::max(value / pDisplayType->ValueScaleDivisor, value != 0 ? 1 : 0); - maxValue = Math::max(maxValue / pDisplayType->ValueScaleDivisor, maxValue != 0 ? 1 : 0); + value = Math::max(value / divisor, value ? 1 : 0); + maxValue = Math::max(maxValue / divisor, 1); } const bool isBuilding = pThis->WhatAmI() == AbstractType::Building; @@ -381,54 +383,51 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } case DisplayInfoType::Shield: { - if (!pExt->Shield || pExt->Shield->IsBrokenAndNonRespawning()) + const auto pShield = pExt->Shield.get(); + + if (!pShield || pShield->IsBrokenAndNonRespawning()) return; - value = pExt->Shield->GetHP(); - maxValue = pExt->Shield->GetType()->Strength.Get(); + value = pShield->GetHP(); + maxValue = pShield->GetType()->Strength.Get(); break; } case DisplayInfoType::Ammo: { - if (pType->Ammo <= 0) - return; - value = pThis->Ammo; maxValue = pType->Ammo; break; } case DisplayInfoType::MindControl: { - if (!pThis->CaptureManager) + const auto pCaptureManager = pThis->CaptureManager; + + if (!pCaptureManager) return; - value = pThis->CaptureManager->ControlNodes.Count; - maxValue = pThis->CaptureManager->MaxControlNodes; + value = pCaptureManager->ControlNodes.Count; + maxValue = pCaptureManager->MaxControlNodes; break; } case DisplayInfoType::Spawns: { - if (!pThis->SpawnManager || !pType->Spawns || pType->SpawnsNumber <= 0) + const auto pSpawnManager = pThis->SpawnManager; + + if (!pSpawnManager || !pType->Spawns) return; - value = pThis->SpawnManager->CountAliveSpawns(); + value = pSpawnManager->CountAliveSpawns(); maxValue = pType->SpawnsNumber; break; } case DisplayInfoType::Passengers: { - if (pType->Passengers <= 0) - return; - value = pThis->Passengers.NumPassengers; maxValue = pType->Passengers; break; } case DisplayInfoType::Tiberium: { - if (pType->Storage <= 0) - return; - value = static_cast(pThis->Tiberium.GetTotalAmount()); maxValue = pType->Storage; break; @@ -445,12 +444,11 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType return; const auto pBuildingType = static_cast(pType); - const auto pBuilding = static_cast(pThis); if (!pBuildingType->CanBeOccupied) return; - value = pBuilding->Occupants.Count; + value = static_cast(pThis)->Occupants.Count; maxValue = pBuildingType->MaxNumberOccupants; break; } @@ -459,17 +457,18 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pType->IsGattling) return; - value = pThis->GattlingValue == 0 ? 0 : pThis->CurrentGattlingStage + 1; + value = pThis->GattlingValue ? pThis->CurrentGattlingStage + 1 : 0; maxValue = pType->WeaponStages; break; } case DisplayInfoType::ROF: { - if (!pThis->GetWeapon(0) || !pThis->GetWeapon(0)->WeaponType) + if (!pThis->IsArmed()) return; - value = pThis->RearmTimer.GetTimeLeft(); - maxValue = pThis->ChargeTurretDelay; + const auto& timer = pThis->RearmTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; break; } case DisplayInfoType::Reload: @@ -483,23 +482,27 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } case DisplayInfoType::SpawnTimer: { - if (!pThis->SpawnManager || !pType->Spawns || pType->SpawnsNumber <= 0) + const auto pSpawnManager = pThis->SpawnManager; + + if (!pSpawnManager || !pType->Spawns || pType->SpawnsNumber <= 0) return; value = 0; for (int i = 0; i < pType->SpawnsNumber; i++) { - if (pThis->SpawnManager->SpawnedNodes[i]->Status != SpawnNodeStatus::Dead) + const auto pSpawnNode = pSpawnManager->SpawnedNodes[i]; + + if (pSpawnNode->Status != SpawnNodeStatus::Dead) continue; - const auto thisValue = pThis->SpawnManager->SpawnedNodes[i]->SpawnTimer.GetTimeLeft(); + const auto thisValue = pSpawnNode->SpawnTimer.GetTimeLeft(); if (thisValue < value || !value) value = thisValue; } - maxValue = pThis->SpawnManager->RegenRate; + maxValue = pSpawnManager->RegenRate; break; } case DisplayInfoType::GattlingTimer: @@ -508,92 +511,96 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType return; const auto thisStage = pThis->CurrentGattlingStage; - auto values = Point2D::Empty; + const auto& stage = pThis->Veterancy.IsElite() ? pType->EliteStage : pType->WeaponStage; - if (pThis->Veterancy.IsElite()) - { - if (thisStage > 0) - values = Point2D{ (pThis->GattlingValue - pType->EliteStage[thisStage - 1]), (pType->EliteStage[thisStage] - pType->EliteStage[thisStage - 1]) }; - else - values = Point2D{ pThis->GattlingValue, pType->EliteStage[thisStage] }; - } - else + value = pThis->GattlingValue; + maxValue = stage[thisStage]; + + if (thisStage > 0) { - if (thisStage > 0) - values = Point2D{ (pThis->GattlingValue - pType->WeaponStage[thisStage - 1]), (pType->WeaponStage[thisStage] - pType->WeaponStage[thisStage - 1]) }; - else - values = Point2D{ pThis->GattlingValue, pType->WeaponStage[thisStage] }; + value -= stage[thisStage - 1]; + maxValue -= stage[thisStage - 1]; } - value = values.X; - maxValue = values.Y; break; } case DisplayInfoType::ProduceCash: { - if (pThis->WhatAmI() != AbstractType::Building) - return; - - const auto pBuildingType = static_cast(pType); - const auto pBuilding = static_cast(pThis); - - if (pBuildingType->ProduceCashAmount <= 0) + if (pThis->WhatAmI() != AbstractType::Building || static_cast(pType)->ProduceCashAmount <= 0) return; - value = pBuilding->CashProductionTimer.GetTimeLeft(); - maxValue = pBuilding->CashProductionTimer.TimeLeft; + const auto& timer = static_cast(pThis)->CashProductionTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; break; } case DisplayInfoType::PassengerKill: { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - - if (!pTypeExt || !pTypeExt->PassengerDeletionType) + if (!pExt->TypeExtData->PassengerDeletionType) return; - value = pExt->PassengerDeletionTimer.GetTimeLeft(); - maxValue = pExt->PassengerDeletionTimer.TimeLeft; + const auto& timer = pExt->PassengerDeletionTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; break; } case DisplayInfoType::AutoDeath: { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = pExt->TypeExtData; - if (!pTypeExt || !pTypeExt->AutoDeath_Behavior.isset()) + if (!pTypeExt->AutoDeath_Behavior.isset()) return; - auto values = Point2D::Empty; - if (pTypeExt->AutoDeath_AfterDelay > 0) - values = Point2D{ pExt->AutoDeathTimer.GetTimeLeft(), pExt->AutoDeathTimer.TimeLeft }; - else if (pTypeExt->AutoDeath_OnAmmoDepletion && pType->Ammo > 0) - values = Point2D{ pThis->Ammo, pType->Ammo }; + { + const auto& timer = pExt->AutoDeathTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + } + else if (pTypeExt->AutoDeath_OnAmmoDepletion) + { + value = pThis->Ammo; + maxValue = pType->Ammo; + } - value = values.X; - maxValue = values.Y; break; } case DisplayInfoType::SuperWeapon: { - if (pThis->WhatAmI() != AbstractType::Building || !pThis->Owner) + if (pThis->WhatAmI() != AbstractType::Building) + return; + + const auto pHouse = pThis->Owner; + + if (!pHouse) return; const auto pBuildingType = static_cast(pType); - const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuildingType); - SuperClass* pSuper = nullptr; if (pBuildingType->SuperWeapon != -1) - pSuper = pThis->Owner->Supers.GetItem(pBuildingType->SuperWeapon); + { + const auto& timer = pHouse->Supers.GetItem(pBuildingType->SuperWeapon)->RechargeTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + } else if (pBuildingType->SuperWeapon2 != -1) - pSuper = pThis->Owner->Supers.GetItem(pBuildingType->SuperWeapon2); - else if (pBuildingTypeExt->SuperWeapons.size() > 0) - pSuper = pThis->Owner->Supers.GetItem(pBuildingTypeExt->SuperWeapons[0]); - - if (!pSuper) - return; + { + const auto& timer = pHouse->Supers.GetItem(pBuildingType->SuperWeapon2)->RechargeTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + } + else + { + const auto& superWeapons = BuildingTypeExt::ExtMap.Find(pBuildingType)->SuperWeapons; + + if (superWeapons.size() > 0) + { + const auto& timer = pHouse->Supers.GetItem(superWeapons[0])->RechargeTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + } + } - value = pSuper->RechargeTimer.GetTimeLeft(); - maxValue = pSuper->RechargeTimer.TimeLeft; break; } case DisplayInfoType::IronCurtain: @@ -601,10 +608,9 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pThis->IsIronCurtained()) return; - const auto timer = &pThis->IronCurtainTimer; - - value = timer->GetTimeLeft(); - maxValue = timer->TimeLeft; + const auto& timer = pThis->IronCurtainTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; break; } case DisplayInfoType::TemporalLife: diff --git a/src/New/Type/DigitalDisplayTypeClass.cpp b/src/New/Type/DigitalDisplayTypeClass.cpp index 8af8a25ac9..68dc002014 100644 --- a/src/New/Type/DigitalDisplayTypeClass.cpp +++ b/src/New/Type/DigitalDisplayTypeClass.cpp @@ -21,7 +21,6 @@ void DigitalDisplayTypeClass::LoadFromINI(CCINIClass* pINI) this->Text_Color.Read(exINI, section, "Text.Color.%s"); this->Text_Background.Read(exINI, section, "Text.Background"); - this->VisibleInSpecialState.Read(exINI, section, "VisibleInSpecialState"); this->Offset.Read(exINI, section, "Offset"); this->Offset_ShieldDelta.Read(exINI, section, "Offset.ShieldDelta"); this->Align.Read(exINI, section, "Align"); @@ -33,10 +32,12 @@ void DigitalDisplayTypeClass::LoadFromINI(CCINIClass* pINI) this->Shape_PercentageFrame.Read(exINI, section, "Shape.PercentageFrame"); this->Percentage.Read(exINI, section, "Percentage"); this->HideMaxValue.Read(exINI, section, "HideMaxValue"); - this->VisibleToHouses_Observer.Read(exINI, section, "VisibleToHouses.Observer"); this->VisibleToHouses.Read(exINI, section, "VisibleToHouses"); + this->VisibleToHouses_Observer.Read(exINI, section, "VisibleToHouses.Observer"); + this->VisibleInSpecialState.Read(exINI, section, "VisibleInSpecialState"); this->InfoType.Read(exINI, section, "InfoType"); this->ValueScaleDivisor.Read(exINI, section, "ValueScaleDivisor"); + this->ValueAsTimer.Read(exINI, section, "ValueAsTimer"); } void DigitalDisplayTypeClass::Draw(Point2D position, int length, int value, int maxValue, bool isBuilding, bool isInfantry, bool hasShield) @@ -76,7 +77,16 @@ void DigitalDisplayTypeClass::DisplayText(Point2D& position, int length, int val wchar_t text[0x20]; double ratio = static_cast(value) / maxValue; - if (Percentage.Get()) + if (ValueAsTimer) + { + const int minute = value / 60; + + if (const int hour = minute / 60) + swprintf_s(text, L"%u:%02u:%02u", hour, minute % 60, value % 60); + else + swprintf_s(text, L"%u:%02u", minute % 60, value % 60); + } + if (Percentage) { swprintf_s(text, L"%d", static_cast(ratio * 100)); wcscat_s(text, L"%%"); @@ -109,14 +119,43 @@ void DigitalDisplayTypeClass::DisplayText(Point2D& position, int length, int val void DigitalDisplayTypeClass::DisplayShape(Point2D& position, int length, int value, int maxValue, bool isBuilding, bool isInfantry, bool hasShield) { double ratio = static_cast(value) / maxValue; - std::string valueString(std::move(Percentage ? - GeneralUtils::IntToDigits(static_cast(ratio * 100)) : - GeneralUtils::IntToDigits(value) - )); - std::string maxValueString(!Percentage && !HideMaxValue.Get(isInfantry) ? - std::move(GeneralUtils::IntToDigits(maxValue)) : - "" - ); + std::string valueString(""); + + if (!Shape_PercentageFrame) + { + if (!ValueAsTimer) + { + if (Percentage) + valueString += std::move(GeneralUtils::IntToDigits(static_cast(ratio * 100))) + '%'; + else if (HideMaxValue.Get(isInfantry)) + valueString += std::move(GeneralUtils::IntToDigits(value)); + else + valueString += std::move(GeneralUtils::IntToDigits(value)) + '/' + std::move(GeneralUtils::IntToDigits(maxValue)); + } + else + { + const int minute = value / 60; + const int hour = minute / 60; + + if (hour) + valueString += std::move(GeneralUtils::IntToDigits(hour)) + '%'; + + const int min = minute % 60; + + if (!(min / 10) && hour) + valueString += '0'; + + valueString += std::move(GeneralUtils::IntToDigits(min)) + '%'; + + const int sec = value % 60; + + if (!(sec / 10)) + valueString += '0'; + + valueString += std::move(GeneralUtils::IntToDigits(sec)); + } + } + Vector2D spacing = ( Shape_Spacing.isset() ? Shape_Spacing.Get() : @@ -124,11 +163,6 @@ void DigitalDisplayTypeClass::DisplayShape(Point2D& position, int length, int va ); const int pipsHeight = hasShield ? 4 : 0; - if (Percentage) - valueString.push_back('%'); - else if (!HideMaxValue.Get(isInfantry)) - valueString += '/' + maxValueString; - if (AnchorType.Vertical == VerticalPosition::Top) position.Y -= Shape->Height + pipsHeight; // upper of healthbar and shieldbar @@ -225,7 +259,6 @@ void DigitalDisplayTypeClass::Serialize(T& Stm) Stm .Process(this->Text_Color) .Process(this->Text_Background) - .Process(this->VisibleInSpecialState) .Process(this->Offset) .Process(this->Offset_ShieldDelta) .Process(this->Align) @@ -237,10 +270,12 @@ void DigitalDisplayTypeClass::Serialize(T& Stm) .Process(this->Shape_PercentageFrame) .Process(this->Percentage) .Process(this->HideMaxValue) - .Process(this->VisibleToHouses_Observer) .Process(this->VisibleToHouses) + .Process(this->VisibleToHouses_Observer) + .Process(this->VisibleInSpecialState) .Process(this->InfoType) .Process(this->ValueScaleDivisor) + .Process(this->ValueAsTimer) ; } diff --git a/src/New/Type/DigitalDisplayTypeClass.h b/src/New/Type/DigitalDisplayTypeClass.h index 3d478066a2..8747066290 100644 --- a/src/New/Type/DigitalDisplayTypeClass.h +++ b/src/New/Type/DigitalDisplayTypeClass.h @@ -10,7 +10,6 @@ class DigitalDisplayTypeClass final : public Enumerable public: Damageable Text_Color; Valueable Text_Background; - Valueable VisibleInSpecialState; Valueable> Offset; Nullable> Offset_ShieldDelta; Valueable Align; @@ -22,15 +21,16 @@ class DigitalDisplayTypeClass final : public Enumerable Valueable Shape_PercentageFrame; Valueable Percentage; Nullable HideMaxValue; - Valueable VisibleToHouses_Observer; Valueable VisibleToHouses; + Valueable VisibleToHouses_Observer; + Valueable VisibleInSpecialState; Valueable InfoType; - Valueable ValueScaleDivisor; + Nullable ValueScaleDivisor; + Valueable ValueAsTimer; DigitalDisplayTypeClass(const char* pTitle = NONE_STR) : Enumerable(pTitle) , Text_Color({ 0, 255, 0 }, { 255,255,0 }, { 255,0,0 }) , Text_Background(false) - , VisibleInSpecialState(true) , Offset({ 0, 0 }) , Offset_ShieldDelta() , Align(TextAlign::Right) @@ -42,10 +42,12 @@ class DigitalDisplayTypeClass final : public Enumerable , Shape_PercentageFrame(false) , Percentage(false) , HideMaxValue() - , VisibleToHouses_Observer(true) , VisibleToHouses(AffectedHouse::All) + , VisibleToHouses_Observer(true) + , VisibleInSpecialState(true) , InfoType(DisplayInfoType::Health) - , ValueScaleDivisor { 1 } + , ValueScaleDivisor() + , ValueAsTimer(false) { } void LoadFromINI(CCINIClass* pINI); From 783bf212bdcad194c7845aa7ffd10779bcd96fe9 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Tue, 25 Feb 2025 02:41:18 +0800 Subject: [PATCH 18/19] Fix a typo --- src/New/Type/DigitalDisplayTypeClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/New/Type/DigitalDisplayTypeClass.cpp b/src/New/Type/DigitalDisplayTypeClass.cpp index 68dc002014..74bc9ba9c2 100644 --- a/src/New/Type/DigitalDisplayTypeClass.cpp +++ b/src/New/Type/DigitalDisplayTypeClass.cpp @@ -86,7 +86,7 @@ void DigitalDisplayTypeClass::DisplayText(Point2D& position, int length, int val else swprintf_s(text, L"%u:%02u", minute % 60, value % 60); } - if (Percentage) + else if (Percentage) { swprintf_s(text, L"%d", static_cast(ratio * 100)); wcscat_s(text, L"%%"); From 29dcafe1bc9e21bd6a3d1fbdee4d0e331dc38f5f Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 26 Feb 2025 14:15:36 +0800 Subject: [PATCH 19/19] Remove useless --- src/Ext/Techno/Body.Visuals.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index 19c35673ab..fb5ef49198 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -571,10 +571,6 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType return; const auto pHouse = pThis->Owner; - - if (!pHouse) - return; - const auto pBuildingType = static_cast(pType); if (pBuildingType->SuperWeapon != -1)