diff --git a/CREDITS.md b/CREDITS.md index 4f3e8b3513..f92cc6854e 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -921,3 +921,4 @@ This page lists all the individual contributions to the project by their author. - **Chang_zhi**: - Interop export interface for accessing scenario local/global variables - Add `ClampToScreen` tag for `BannerType` to control whether banner position is clamped to the visible area +- **obsidianus** - Automatic conversion based on health diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 6fd4ac6bf9..00e3c041e4 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1545,6 +1545,30 @@ This feature has the same limitations as [Ares' Type Conversion](https://ares-de This feature requires Ares 3.0 or higher to function! When Ares 3.0+ is not detected, not all properties of a unit may be updated. ``` +### Automatic conversion based on health + +- Units can now be converted into another unit by health percentage. +- `Convert.Health.AbovePercent` determines the minimal health percentage at which a unit converts automatically. +- `Convert.Health.BelowPercent` determines the maximum health percentage at which a unit converts automatically. +- `Convert.Health` specify the new techno after the conversion. This unit must be of the same type of the original (infantry -> infantry, vehicle -> vehicle or aircraft -> aircraft). +- Setting a negative number will disable the HP check, and when both checks are disabled, conversion will not occur. + +In `rulesmd.ini`: +```ini +[SOMETECHNO] ; TechnoType, before conversion +Convert.Health.AbovePercent=-1.0 ; floating point value, percents or absolute +Convert.Health.BelowPercent=-1.0 ; floating point value, percents or absolute +Convert.Health= ; TechnoType, after conversion +``` + +```{warning} +This feature has the same limitations as [Ares' Type Conversion](https://ares-developers.github.io/Ares-docs/new/typeconversion.html). This feature does not support BuildingTypes. +``` + +```{warning} +This feature requires Ares 3.0 or higher to function! When Ares 3.0+ is not detected, not all properties of a unit may be updated. +``` + ### Automatic passenger deletion - Transports can erase passengers over time. Passengers are deleted in order of entering the transport, from first to last. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index ca707925a5..e40e61b614 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -653,6 +653,7 @@ HideShakeEffects=false ; boolean - [`600` Configure Drop Crate](AI-Scripting-and-Mapping.md#configure-drop-crate) (by FS-21) - [DeployFire supports buildings](New-or-Enhanced-Logics.md#deployfire-supports) (By FlyStar) - `OmniFire` supports buildings with `Turret=yes` (by FlyStar) +- [Automatic conversion based on heallth](New-or-Enhanced-Logics.md#automatic-conversion-based-on-heallth) (by obsidianus) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po index b9131b4ba1..65d2b46054 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po @@ -3626,6 +3626,24 @@ msgid "" "both checks are disabled, conversion will not occur." msgstr "设为负数将会禁用弹药数量检查,当两个检查都被禁用时将不会触发变形。" +msgid "Automatic conversion based on health" +msgstr "自动根据血量变形" + +msgid "Units can now be converted into another unit by health percentage." +msgstr "单位可以现在可以基于HP变为其他单位" + +msgid "`Convert.Health.AbovePercent` determines the minimal health percentage at which a unit converts automatically." +msgstr "`Convert.Health.AbovePercent`决定了触发单位自动变身的最小HP值" + +msgid "`Convert.Health.BelowPercent` determines the maximum health percentage at which a unit converts automatically." +msgstr "`Convert.Health.BelowPercent`决定了触发单位自动变身的最大HP值" + +msgid "`Convert.Health` specify the new techno after the conversion. This unit must be of the same type of the original (infantry -> infantry, vehicle -> vehicle or aircraft -> aircraft)." +msgstr "`Convert.Health` 指定变身后的单位. 类型需要和原单位相同 (步兵 → 步兵, 载具 → 载具 或 飞机 → 飞机)" + +msgid "Setting a negative number will disable the HP check, and when both checks are disabled, conversion will not occur." +msgstr "设置为负值会关闭对应的HP检查,当两项检查都被关闭则不会触发变身." + msgid "Automatic passenger deletion" msgstr "自动删除乘客" diff --git a/src/Ext/Foot/Body.cpp b/src/Ext/Foot/Body.cpp index 4114bb1abc..5299bdc7eb 100644 --- a/src/Ext/Foot/Body.cpp +++ b/src/Ext/Foot/Body.cpp @@ -813,6 +813,48 @@ void FootExt::UpdateTypeData(TechnoTypeClass* pCurrentType) } } +void FootExt::AmmoAutoConvertActions() +{ + const auto pTypeExt = this->TypeExtData; + + if (!pTypeExt->Ammo_AutoConvertType.isset()) + return; + + const int min = pTypeExt->Ammo_AutoConvertMinimumAmount; + const int max = pTypeExt->Ammo_AutoConvertMaximumAmount; + + if (min < 0 && max < 0) + return; + + if (pTypeExt->OwnerObject()->Ammo <= 0) + return; + + const auto pThis = this->OwnerObject(); + const int ammo = pThis->Ammo; + + if ((min < 0 || ammo >= min) && (max < 0 || ammo <= max)) + TechnoExt::ConvertToType(pThis, pTypeExt->Ammo_AutoConvertType); +} + +void FootExt::HealthAutoConvertActions() +{ + const auto pTypeExt = this->TypeExtData; + + if (!pTypeExt->Convert_Health.isset()) + return; + + const double min = pTypeExt->Convert_Health_AbovePercent; + const double max = pTypeExt->Convert_Health_BelowPercent; + + if (min < 0 && max < 0) + return; + + const auto pThis = this->OwnerObject(); + + if (TechnoExt::IsHealthInThreshold(pThis, min, max)) + TechnoExt::ConvertToType(pThis, pTypeExt->Convert_Health); +} + // ============================= // load / save diff --git a/src/Ext/Foot/Body.h b/src/Ext/Foot/Body.h index 0756b3bca9..4d97cfcab7 100644 --- a/src/Ext/Foot/Body.h +++ b/src/Ext/Foot/Body.h @@ -63,6 +63,8 @@ class FootExt : public TechnoExt void UpdateOnTunnelEnter(); void UpdateOnTunnelExit(); void UpdateTypeData(TechnoTypeClass* pCurrentType); + void HealthAutoConvertActions(); + void AmmoAutoConvertActions(); virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index f64b487144..e23b4836ab 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -134,32 +134,6 @@ void TechnoExt::ApplyInterceptor() } } -void TechnoExt::AmmoAutoConvertActions() -{ - const auto pTypeExt = this->TypeExtData; - - if (!pTypeExt->Ammo_AutoConvertType.isset()) - return; - - const int min = pTypeExt->Ammo_AutoConvertMinimumAmount; - const int max = pTypeExt->Ammo_AutoConvertMaximumAmount; - - if (min < 0 && max < 0) - return; - - if (pTypeExt->OwnerObject()->Ammo <= 0) - return; - - const auto pThis = this->OwnerObject(); - const int ammo = pThis->Ammo; - - if ((min < 0 || ammo >= min) && (max < 0 || ammo <= max)) - { - const auto pFoot = abstract_cast(pThis); - TechnoExt::ConvertToType(pFoot, pTypeExt->Ammo_AutoConvertType); - } -} - // TODO : Merge into new AttachEffects bool TechnoExt::CheckDeathConditions(bool isInLimbo) { diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index d8aaac7fa7..9fad2304f5 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -178,8 +178,6 @@ class TechnoExt : public RadioExt, public Detach::Listener int ApplyForceWeaponInRange(AbstractClass* pTarget); void ResetDelayedFireTimer(); void UpdateTintValues(); - - void AmmoAutoConvertActions(); void UpdateLastTargetCrd(); int GetSight(); diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 1f0a256ce3..52c290d1cf 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -46,6 +46,7 @@ DEFINE_HOOK(0x4DA54E, FootClass_AI, 0x6) pExt->UpdateWarpInDelay(); pExt->UpdateTiberiumEater(); pExt->AmmoAutoConvertActions(); + pExt->HealthAutoConvertActions(); if (pExt->AttackMoveFollowerTempCount) pExt->AttackMoveFollowerTempCount--; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 760b6e7907..ceedf8c89d 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1124,6 +1124,13 @@ void TechnoTypeExt::LoadFromINIFile(CCINIClass* const pINI) || ExtraThreatCoefficient_Facing.Get(RulesExt::Global()->ExtraThreatCoefficient_Facing) != 0.0 || ExtraThreatCoefficient_DistanceToLastTarget.Get(RulesExt::Global()->ExtraThreatCoefficient_DistanceToLastTarget) != 0.0; + this->Convert_Health_AbovePercent.Read(exINI, pSection, "Convert.Health.AbovePercent"); + this->Convert_Health_BelowPercent.Read(exINI, pSection, "Convert.Health.BelowPercent"); + this->Convert_Health.Read(exINI, pSection, "Convert.Health"); + + if (this->Convert_Health_AbovePercent > this->Convert_Health_BelowPercent) + Debug::Log("[Developer warning][%s] Convert.Health.AbovePercent is greater than Convert.Health.BelowPercent, resulting in no conversion.\n", pSection); + // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -1746,6 +1753,10 @@ void TechnoTypeExt::Serialize(T& Stm) .Process(this->ExtraThreatCoefficient_InRangeDistance) .Process(this->ExtraThreatCoefficient_Facing) .Process(this->ExtraThreatCoefficient_DistanceToLastTarget) + + .Process(this->Convert_Health_AbovePercent) + .Process(this->Convert_Health_BelowPercent) + .Process(this->Convert_Health) ; } void TechnoTypeExt::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 01a283b49c..63f1afdbd7 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -428,6 +428,10 @@ class TechnoTypeExt : public ObjectTypeExt Nullable DropCrate; + Valueable Convert_Health_AbovePercent; + Valueable Convert_Health_BelowPercent; + Nullable Convert_Health; + Nullable Unsellable; // Ares 3.0 TechnoTypeExt(TechnoTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) @@ -808,6 +812,10 @@ class TechnoTypeExt : public ObjectTypeExt , ExtraThreatCoefficient_DistanceToLastTarget {} , DropCrate {} + + , Convert_Health_AbovePercent { -1.0 } + , Convert_Health_BelowPercent { -1.0 } + , Convert_Health {} { } virtual ~TechnoTypeExt() = default;