diff --git a/CREDITS.md b/CREDITS.md index 96eed675df..3b6c2cffc2 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -390,6 +390,7 @@ This page lists all the individual contributions to the project by their author. - Enhanced Bombard trajectory - No turret unit turn to the target - Damage multiplier for different houses + - 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 5d1fad46db..6a880e1f2d 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -37,15 +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%). - - `CanSee` and `CanSee.Observer` can limit visibility to specific players. + - 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. - - 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. + - 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 `%` (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 @@ -60,7 +61,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|IronCurtain|TemporalLife) Offset=0,0 ; integers - horizontal, vertical Offset.ShieldDelta= ; integers - horizontal, vertical Align=right ; Text alignment enumeration (left|right|center/centre) @@ -71,7 +72,9 @@ Percentage=false ; boolean HideMaxValue=false ; boolean VisibleToHouses=owner ; Affected house enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) VisibleToHouses.Observer=true ; boolean -ValueScaleDivisor=1 ; integer +VisibleInSpecialState=true ; boolean +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 @@ -81,6 +84,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 [SOMETECHNO] ; TechnoType DigitalDisplay.Disable=false ; boolean diff --git a/docs/Whats-New.md b/docs/Whats-New.md index d4338688f6..cb6150f40e 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -327,6 +327,7 @@ New: - No turret unit turn to the target (by CrimRecya & TaranDahl) - Damage multiplier for different houses (by CrimRecya) - Customizable duration for electric bolts (by Starkku) +- Several new Infotypes, no display in specific status and a new single frame display method (by CrimRecya) Vanilla fixes: - Prevent the units with locomotors that cause problems from entering the tank bunker (by TaranDahl) diff --git a/src/Ext/Techno/Body.Internal.cpp b/src/Ext/Techno/Body.Internal.cpp index e643505f45..8990e072d1 100644 --- a/src/Ext/Techno/Body.Internal.cpp +++ b/src/Ext/Techno/Body.Internal.cpp @@ -144,6 +144,18 @@ CoordStruct TechnoExt::GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& return FLH; } +void TechnoExt::ExtData::InitializeDisplayInfo() +{ + const auto pThis = this->OwnerObject(); + const auto pPrimary = pThis->GetWeapon(0)->WeaponType; + pThis->RearmTimer.Start(0); + + 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() { if (auto pTypeExt = this->TypeExtData) diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index ad07a722a8..fb5ef49198 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -2,7 +2,10 @@ #include #include - +#include +#include +#include +#include #include void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, RectangleStruct* pBounds) @@ -331,18 +334,23 @@ 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 = -1; + int maxValue = 0; GetValuesForDisplay(pThis, pDisplayType->InfoType, value, maxValue); - if (value == -1 || maxValue == -1) + 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; @@ -375,54 +383,51 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } case DisplayInfoType::Shield: { - if (pExt->Shield == nullptr || 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 == nullptr) + 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 == nullptr || pType->Spawns == nullptr || 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; @@ -438,13 +443,12 @@ 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); if (!pBuildingType->CanBeOccupied) return; - value = pBuilding->Occupants.Count; + value = static_cast(pThis)->Occupants.Count; maxValue = pBuildingType->MaxNumberOccupants; break; } @@ -453,10 +457,169 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType if (!pType->IsGattling) return; - value = pThis->CurrentGattlingStage; + value = pThis->GattlingValue ? pThis->CurrentGattlingStage + 1 : 0; maxValue = pType->WeaponStages; break; } + case DisplayInfoType::ROF: + { + if (!pThis->IsArmed()) + return; + + const auto& timer = pThis->RearmTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + break; + } + case DisplayInfoType::Reload: + { + if (pType->Ammo <= 0) + return; + + value = (pThis->Ammo >= pType->Ammo) ? 0 : pThis->ReloadTimer.GetTimeLeft(); + maxValue = (pThis->Ammo || pType->EmptyReload <= 0) ? pType->Reload : pType->EmptyReload; + break; + } + case DisplayInfoType::SpawnTimer: + { + const auto pSpawnManager = pThis->SpawnManager; + + if (!pSpawnManager || !pType->Spawns || pType->SpawnsNumber <= 0) + return; + + value = 0; + + for (int i = 0; i < pType->SpawnsNumber; i++) + { + const auto pSpawnNode = pSpawnManager->SpawnedNodes[i]; + + if (pSpawnNode->Status != SpawnNodeStatus::Dead) + continue; + + const auto thisValue = pSpawnNode->SpawnTimer.GetTimeLeft(); + + if (thisValue < value || !value) + value = thisValue; + } + + maxValue = pSpawnManager->RegenRate; + break; + } + case DisplayInfoType::GattlingTimer: + { + if (!pType->IsGattling) + return; + + const auto thisStage = pThis->CurrentGattlingStage; + const auto& stage = pThis->Veterancy.IsElite() ? pType->EliteStage : pType->WeaponStage; + + value = pThis->GattlingValue; + maxValue = stage[thisStage]; + + if (thisStage > 0) + { + value -= stage[thisStage - 1]; + maxValue -= stage[thisStage - 1]; + } + + break; + } + case DisplayInfoType::ProduceCash: + { + if (pThis->WhatAmI() != AbstractType::Building || static_cast(pType)->ProduceCashAmount <= 0) + return; + + const auto& timer = static_cast(pThis)->CashProductionTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + break; + } + case DisplayInfoType::PassengerKill: + { + if (!pExt->TypeExtData->PassengerDeletionType) + return; + + const auto& timer = pExt->PassengerDeletionTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + break; + } + case DisplayInfoType::AutoDeath: + { + const auto pTypeExt = pExt->TypeExtData; + + if (!pTypeExt->AutoDeath_Behavior.isset()) + return; + + if (pTypeExt->AutoDeath_AfterDelay > 0) + { + const auto& timer = pExt->AutoDeathTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + } + else if (pTypeExt->AutoDeath_OnAmmoDepletion) + { + value = pThis->Ammo; + maxValue = pType->Ammo; + } + + break; + } + case DisplayInfoType::SuperWeapon: + { + if (pThis->WhatAmI() != AbstractType::Building) + return; + + const auto pHouse = pThis->Owner; + const auto pBuildingType = static_cast(pType); + + if (pBuildingType->SuperWeapon != -1) + { + const auto& timer = pHouse->Supers.GetItem(pBuildingType->SuperWeapon)->RechargeTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + } + else if (pBuildingType->SuperWeapon2 != -1) + { + 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; + } + } + + break; + } + case DisplayInfoType::IronCurtain: + { + if (!pThis->IsIronCurtained()) + return; + + const auto& timer = pThis->IronCurtainTimer; + value = timer.GetTimeLeft(); + maxValue = timer.TimeLeft; + break; + } + case DisplayInfoType::TemporalLife: + { + const auto pTemporal = pThis->TemporalTargetingMe; + + if (!pTemporal) + return; + + value = pTemporal->WarpRemaining; + maxValue = pType->Strength * 10; + break; + } default: { value = pThis->Health; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a39438171e..2a38a27a3d 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -119,6 +119,7 @@ class TechnoExt void UpdateSelfOwnedAttachEffects(); 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; virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 8da8a144de..a11332bb1f 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -141,6 +141,7 @@ DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2) pExt->CurrentShieldType = pExt->TypeExtData->ShieldType; pExt->InitializeLaserTrails(); pExt->InitializeAttachEffects(); + pExt->InitializeDisplayInfo(); if (pExt->TypeExtData->Harvester_Counted) HouseExt::ExtMap.Find(pThis->Owner)->OwnedCountedHarvesters.push_back(pThis); diff --git a/src/New/Type/DigitalDisplayTypeClass.cpp b/src/New/Type/DigitalDisplayTypeClass.cpp index 7ab6fcd8b9..74bc9ba9c2 100644 --- a/src/New/Type/DigitalDisplayTypeClass.cpp +++ b/src/New/Type/DigitalDisplayTypeClass.cpp @@ -29,12 +29,15 @@ 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"); 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) @@ -72,10 +75,20 @@ 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 (ValueAsTimer) + { + const int minute = value / 60; - if (Percentage.Get()) + 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); + } + else if (Percentage) { - 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)) @@ -87,7 +100,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 = DSurface::Composite->GetRect(); rect.Height -= 32; // account for bottom bar @@ -106,14 +118,44 @@ 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) { - std::string valueString(std::move(Percentage ? - GeneralUtils::IntToDigits(static_cast(static_cast(value) / maxValue * 100)) : - GeneralUtils::IntToDigits(value) - )); - std::string maxValueString(!Percentage && !HideMaxValue.Get(isInfantry) ? - std::move(GeneralUtils::IntToDigits(maxValue)) : - "" - ); + double ratio = static_cast(value) / 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() : @@ -121,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 @@ -137,13 +174,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 +205,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 +236,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,12 +267,15 @@ 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) .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 2e373bcc80..8747066290 100644 --- a/src/New/Type/DigitalDisplayTypeClass.h +++ b/src/New/Type/DigitalDisplayTypeClass.h @@ -18,12 +18,15 @@ class DigitalDisplayTypeClass final : public Enumerable Valueable Shape; CustomPalette Palette; Nullable> Shape_Spacing; + 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 }) @@ -36,12 +39,15 @@ class DigitalDisplayTypeClass final : public Enumerable , Shape(nullptr) , Palette() , Shape_Spacing() + , 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); diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index 781a5a2403..4ed11f9233 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -276,7 +276,17 @@ 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, + IronCurtain = 18, + TemporalLife = 19 }; class MouseCursorHotSpotX diff --git a/src/Utilities/TemplateDef.h b/src/Utilities/TemplateDef.h index 38ece7ce3e..4ffc010453 100644 --- a/src/Utilities/TemplateDef.h +++ b/src/Utilities/TemplateDef.h @@ -1251,6 +1251,46 @@ 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 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");