From e8dcf295503e55902ef0f87f8dd877a8d35f3cbe Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Mon, 23 Feb 2026 16:39:55 +0800 Subject: [PATCH 01/11] init --- CREDITS.md | 1 + docs/New-or-Enhanced-Logics.md | 35 +++++++ docs/Whats-New.md | 1 + src/Ext/Rules/Body.cpp | 15 +++ src/Ext/Rules/Body.h | 16 ++++ src/Ext/Techno/Body.cpp | 124 +++++++++++++++++++++++++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.cpp | 11 +++ src/Ext/TechnoType/Body.cpp | 24 +++++ src/Ext/TechnoType/Body.h | 24 +++++ src/New/Type/AttachEffectTypeClass.cpp | 2 + src/New/Type/AttachEffectTypeClass.h | 2 + 12 files changed, 256 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index 092084b4a4..77d74b9d44 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -504,6 +504,7 @@ This page lists all the individual contributions to the project by their author. - Customize particle system of parasite logic - Fix an issue where parasites that have infected infantry do not provide a refund when the infected infantry enters a Grinding building - Fix the issue that `PassengerDeletion` dont consider passenger's passenger, parasite and hijacker + - New bounty logic - **Apollo** - Translucent SHP drawing patches - **ststl**: - Customizable `ShowTimer` priority of superweapons diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 83f2fafb5d..359c0472ef 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -149,6 +149,7 @@ ReflectDamage.Override= ; integer ReflectDamage.UseInvokerAsOwner=false ; boolean DisableWeapons=false ; boolean Unkillable=false ; boolean +Bounty= ; boolean LaserTrail.Type= ; LaserTrailType Groups= ; comma-separated list of strings (group IDs) @@ -2167,6 +2168,40 @@ TiberiumEater.Anims.Tiberium3= ; List of AnimationTypes TiberiumEater.AnimMove=true ; boolean ``` +### New bounty logic + +- Like ares's bounty logic, but it's easyer to use than ares. + + +In `rulesmd.ini`: +```ini +[General] +Bounty.Enable=false ; boolean +Bounty.Enablers= ; list ot TechnoTypes +Bounty.Default=false ; boolean +Bounty.Multiplier=1.0 ; double +Bounty.KillerMultiplier=1.0 ; double + +[AudioVisual] +Bounty.Display=false ; boolean +Bounty.Display.House=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) + +[SOMETECHNO] ; TechnoType +Bounty= ; boolean +Bounty.Multiplier= ; double +Bounty.Multiplier.Veteran= ; double +Bounty.Multiplier.Elite ; double +Bounty.Value=0 ; integer +Bounty.Value.Veteran= ; integer +Bounty.Value.Elite= ; integer +Bounty.KillerMultiplier= ; double +Bounty.KillerMultiplier.Veteran= ; double +Bounty.KillerMultiplier.Elite= ; double +Bounty.Display= ; boolean +Bounty.Display.House= ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) +Bounty.Display.Offset=0,0 ; X,Y +``` + ### Weapons fired on warping in / out - It is now possible to add weapons that are fired on a teleporting TechnoType when it warps in or out. They are at the same time as the appropriate animations (`WarpIn` / `WarpOut`) are displayed. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index a4eaebd6b4..b3cf9fbe5b 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -542,6 +542,7 @@ New: - Allow jumpjet climbing ignore building height (by TaranDahl) - [Allow draw SuperWeapon timer as percentage](User-Interface.md#allow-draw-superweapon-timer-as-percentage) (by NetsuNegi) - Customize particle system of parasite logic (by NetsuNegi) +- New bounty logic (by NetsuNegi) 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/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 228b0415ca..2295647b14 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -368,6 +368,14 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->PenetratesTransport_Level.Read(exINI, GameStrings::CombatDamage, "PenetratesTransport.Level"); + this->Bounty_Enable.Read(exINI, GameStrings::General, "Bounty.Enable"); + this->Bounty_Enablers.Read(exINI, GameStrings::General, "Bounty.Enablers"); + this->Bounty_Default.Read(exINI, GameStrings::General, "Bounty.Default"); + this->Bounty_Multiplier.Read(exINI, GameStrings::General, "Bounty.Multiplier"); + this->Bounty_KillerMultiplier.Read(exINI, GameStrings::General, "Bounty.KillerMultiplier"); + this->Bounty_Display.Read(exINI, GameStrings::AudioVisual, "Bounty.Display"); + this->Bounty_Display_House.Read(exINI, GameStrings::AudioVisual, "Bounty.Display.House"); + this->UnitsUnsellable.Read(exINI, GameStrings::General, "UnitsUnsellable"); // Section AITargetTypes @@ -672,6 +680,13 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->DefaultToGuardArea) .Process(this->CylinderRangefinding) .Process(this->PenetratesTransport_Level) + .Process(this->Bounty_Enable) + .Process(this->Bounty_Enablers) + .Process(this->Bounty_Default) + .Process(this->Bounty_Multiplier) + .Process(this->Bounty_KillerMultiplier) + .Process(this->Bounty_Display) + .Process(this->Bounty_Display_House) .Process(this->UnitsUnsellable) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index ca624cd9ae..5788c74e75 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -318,6 +318,14 @@ class RulesExt Valueable PenetratesTransport_Level; + Valueable Bounty_Enable; + ValueableVector Bounty_Enablers; + Valueable Bounty_Default; + Valueable Bounty_Multiplier; + Valueable Bounty_KillerMultiplier; + Valueable Bounty_Display; + Valueable Bounty_Display_House; + Valueable UnitsUnsellable; ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) @@ -582,6 +590,14 @@ class RulesExt , PenetratesTransport_Level { 10 } + , Bounty_Enable { false } + , Bounty_Enablers {} + , Bounty_Default { false } + , Bounty_Multiplier { 1.0 } + , Bounty_KillerMultiplier { 1.0 } + , Bounty_Display { false } + , Bounty_Display_House { AffectedHouse::All } + , UnitsUnsellable { false } { } diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index ed4ebd3ce3..4672805912 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include TechnoExt::ExtContainer TechnoExt::ExtMap; UnitClass* TechnoExt::Deployer = nullptr; @@ -883,6 +885,128 @@ void TechnoExt::ClickedApproachObject(FootClass* pThis, ObjectClass* pObject) event.AddEvent(); } +void TechnoExt::GiveBounty(TechnoClass* pVictim, TechnoClass* pKiller, int victimCost) +{ + if (!RulesExt::Global()->Bounty_Enable) + return; + + const auto pKillerExt = TechnoExt::ExtMap.Find(pKiller); + const auto pKillerTypeExt = pKillerExt->TypeExtData; + const auto it = std::ranges::find_if(pKillerExt->AttachedEffects, [](std::unique_ptr const& pAE) { return pAE->IsActive() && pAE->GetType()->Bounty.isset(); }); + + if (it != pKillerExt->AttachedEffects.cend()) + { + if (!(*it)->GetType()->Bounty.Get()) + return; + } + else + { + if (!pKillerTypeExt->Bounty.Get(RulesExt::Global()->Bounty_Default)) + return; + } + + const auto pKillerHouse = pKiller->Owner; + + if (pKillerHouse->IsAlliedWith(pVictim->Owner)) + return; + + const auto pKillerHouseExt = HouseExt::ExtMap.Find(pKillerHouse); + + // check that any aux techno exist and no neg techno + auto IsTechnoPresent = [pKillerHouse, pKillerHouseExt](TechnoTypeClass* pType) + { + const auto pBuildingType = abstract_cast(pType); + + if (pBuildingType && (!BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.empty() || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + return BuildingTypeExt::GetUpgradesAmount(pBuildingType, pKillerHouse) > 0; + + return pKillerHouseExt->CountOwnedPresentAndLimboed(pType) > 0; + }; + + if (!RulesExt::Global()->Bounty_Enablers.empty() && std::ranges::none_of(RulesExt::Global()->Bounty_Enablers, IsTechnoPresent)) + return; + + const auto pVictimTypeExt = TechnoTypeExt::ExtMap.Find(pVictim->GetTechnoType()); + double victimMultiplier = 1.0; + + switch (pVictim->Veterancy.GetRemainingLevel()) + { + case Rank::Elite: + if (pVictimTypeExt->Bounty_Multiplier_Elite.isset()) + { + victimMultiplier = pVictimTypeExt->Bounty_Multiplier_Elite.Get(); + break; + } + + case Rank::Veteran: + if (pVictimTypeExt->Bounty_Multiplier_Vet.isset()) + { + victimMultiplier = pVictimTypeExt->Bounty_Multiplier_Elite.Get(); + break; + } + + default: + if (pVictimTypeExt->Bounty_Multiplier.isset()) + { + victimMultiplier = pVictimTypeExt->Bounty_Multiplier.Get(); + break; + } + + victimMultiplier = RulesExt::Global()->Bounty_Multiplier; + } + + int defaultCost = victimMultiplier ? static_cast(std::round(victimCost * victimMultiplier)) : pVictimTypeExt->Bounty_Value.Get(pVictim); + + if (!defaultCost) + { + defaultCost = victimCost; + + if (!defaultCost) + return; + } + + double killerMultiplier = 1.0; + + switch (pKiller->Veterancy.GetRemainingLevel()) + { + case Rank::Elite: + if (pKillerTypeExt->Bounty_KillerMultiplier_Elite.isset()) + { + killerMultiplier = pKillerTypeExt->Bounty_KillerMultiplier_Elite.Get(); + break; + } + + case Rank::Veteran: + if (pKillerTypeExt->Bounty_KillerMultiplier_Vet.isset()) + { + killerMultiplier = pKillerTypeExt->Bounty_KillerMultiplier_Elite.Get(); + break; + } + + default: + if (pKillerTypeExt->Bounty_KillerMultiplier.isset()) + { + killerMultiplier = pKillerTypeExt->Bounty_KillerMultiplier.Get(); + break; + } + + killerMultiplier = RulesExt::Global()->Bounty_KillerMultiplier; + } + + const int value = static_cast(std::round(defaultCost * killerMultiplier)); + + if (!value || !pKillerHouse->CanTransactMoney(value)) + return; + + pKillerHouse->TransactMoney(value); + + if (pKillerTypeExt->Bounty_Display.Get(RulesExt::Global()->Bounty_Display)) + { + const auto displayTo = pKillerTypeExt->Bounty_Display_House.Get(RulesExt::Global()->Bounty_Display_House); + FlyingStrings::AddMoneyString(value, pKiller, pKiller->Owner, displayTo, pKiller->Location, pKillerTypeExt->Bounty_Display_Offset); + } +} + bool TechnoExt::EjectRandomly(FootClass* pEjectee, const CoordStruct& coords, int distance, bool select) { std::vector usableCoords; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 7f178426b4..7a41561294 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -295,6 +295,7 @@ class TechnoExt static bool SimpleDeployerAllowedToDeploy(UnitClass* pThis, bool defaultValue, bool alwaysCheckLandTypes); static void ShowPromoteAnim(TechnoClass* pThis); static void ClickedApproachObject(FootClass* pThis, ObjectClass* pObject); + static void GiveBounty(TechnoClass* pVictim, TechnoClass* pKiller, int victimCost); static bool EjectRandomly(FootClass* pEjectee, const CoordStruct& coords, int distance, bool select); static bool EjectSurvivor(FootClass* pSurvivor, CoordStruct coords, bool select); diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 7bbf1c3a11..bb37856f12 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -1856,3 +1856,14 @@ DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5F30, FootClass_GetThreatValue_Wrapper); // Un DEFINE_FUNCTION_JUMP(VTABLE, 0x7E417C, Building_GetThreatValue_Wrapper); // BuildingClass #pragma endregion + +DEFINE_HOOK(0x702E64, TechnoClass_RegisterDestruction_Bounty, 0x6) +{ + GET(TechnoClass*, pKiller, EDI); + GET(TechnoClass*, pVictim, ESI); + GET(int, victimCost, EBP); + + TechnoExt::GiveBounty(pVictim, pKiller, victimCost); + + return 0; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 8cc82868c4..d1a4e13aea 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1160,6 +1160,18 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->JumpjetClimbIgnoreBuilding.Read(exINI, pSection, "JumpjetClimbIgnoreBuilding"); + this->Bounty.Read(exINI, pSection, "Bounty"); + this->Bounty_Multiplier.Read(exINI, pSection, "Bounty.Multiplier"); + this->Bounty_Multiplier_Vet.Read(exINI, pSection, "Bounty.Multiplier.Veteran"); + this->Bounty_Multiplier_Elite.Read(exINI, pSection, "Bounty.Multiplier.Elite"); + this->Bounty_Value.Read(exINI, pSection, "Bounty.Value.%s"); + this->Bounty_KillerMultiplier.Read(exINI, pSection, "Bounty.KillerMultiplier"); + this->Bounty_KillerMultiplier_Vet.Read(exINI, pSection, "Bounty.KillerMultiplier.Veteran"); + this->Bounty_KillerMultiplier_Elite.Read(exINI, pSection, "Bounty.KillerMultiplier.Elite"); + this->Bounty_Display.Read(exINI, pSection, "Bounty.Display"); + this->Bounty_Display_House.Read(exINI, pSection, "Bounty.Display.House"); + this->Bounty_Display_Offset.Read(exINI, pSection, "Bounty.Display.Offset"); + // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -1874,6 +1886,18 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->PenetratesTransport_DamageMultiplier) .Process(this->JumpjetClimbIgnoreBuilding) + + .Process(this->Bounty) + .Process(this->Bounty_Multiplier) + .Process(this->Bounty_Multiplier_Vet) + .Process(this->Bounty_Multiplier_Elite) + .Process(this->Bounty_Value) + .Process(this->Bounty_KillerMultiplier) + .Process(this->Bounty_KillerMultiplier_Vet) + .Process(this->Bounty_KillerMultiplier_Elite) + .Process(this->Bounty_Display) + .Process(this->Bounty_Display_House) + .Process(this->Bounty_Display_Offset) .Process(this->Unsellable) ; diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index c11729ec83..0324a1868d 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -485,6 +485,18 @@ class TechnoTypeExt Nullable JumpjetClimbIgnoreBuilding; + Nullable Bounty; + Nullable Bounty_Multiplier; + Nullable Bounty_Multiplier_Vet; + Nullable Bounty_Multiplier_Elite; + Promotable Bounty_Value; + Nullable Bounty_KillerMultiplier; + Nullable Bounty_KillerMultiplier_Vet; + Nullable Bounty_KillerMultiplier_Elite; + Nullable Bounty_Display; + Nullable Bounty_Display_House; + Valueable Bounty_Display_Offset; + Nullable Unsellable; // Ares 3.0 ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) @@ -926,6 +938,18 @@ class TechnoTypeExt , JumpjetClimbIgnoreBuilding {} + , Bounty {} + , Bounty_Multiplier {} + , Bounty_Multiplier_Vet {} + , Bounty_Multiplier_Elite {} + , Bounty_Value { 0 } + , Bounty_KillerMultiplier {} + , Bounty_KillerMultiplier_Vet {} + , Bounty_KillerMultiplier_Elite {} + , Bounty_Display {} + , Bounty_Display_House {} + , Bounty_Display_Offset { Point2D::Empty } + , Unsellable {} { } diff --git a/src/New/Type/AttachEffectTypeClass.cpp b/src/New/Type/AttachEffectTypeClass.cpp index 02bc5abb9e..103b11b845 100644 --- a/src/New/Type/AttachEffectTypeClass.cpp +++ b/src/New/Type/AttachEffectTypeClass.cpp @@ -161,6 +161,7 @@ void AttachEffectTypeClass::LoadFromINI(CCINIClass* pINI) this->DisableWeapons.Read(exINI, pSection, "DisableWeapons"); this->Unkillable.Read(exINI, pSection, "Unkillable"); + this->Bounty.Read(exINI, pSection, "Bounty"); this->LaserTrail_Type.Read(exINI, pSection, "LaserTrail.Type"); // Groups @@ -230,6 +231,7 @@ void AttachEffectTypeClass::Serialize(T& Stm) .Process(this->ReflectDamage_UseInvokerAsOwner) .Process(this->DisableWeapons) .Process(this->Unkillable) + .Process(this->Bounty) .Process(this->LaserTrail_Type) .Process(this->Groups) ; diff --git a/src/New/Type/AttachEffectTypeClass.h b/src/New/Type/AttachEffectTypeClass.h index b0759a97c0..56dde22493 100644 --- a/src/New/Type/AttachEffectTypeClass.h +++ b/src/New/Type/AttachEffectTypeClass.h @@ -99,6 +99,7 @@ class AttachEffectTypeClass final : public Enumerable Valueable ReflectDamage_UseInvokerAsOwner; Valueable DisableWeapons; Valueable Unkillable; + Nullable Bounty; ValueableIdx LaserTrail_Type; std::vector Groups; @@ -162,6 +163,7 @@ class AttachEffectTypeClass final : public Enumerable , ReflectDamage_UseInvokerAsOwner { false } , DisableWeapons { false } , Unkillable { false } + , Bounty {} , LaserTrail_Type { -1 } , Groups {} {}; From 8765b46aa6eb566b4bb63942e89ecd521472af92 Mon Sep 17 00:00:00 2001 From: Noble Fish <89088785+DeathFishAtEase@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:03:27 +0800 Subject: [PATCH 02/11] Update New-or-Enhanced-Logics.md --- docs/New-or-Enhanced-Logics.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 359c0472ef..5c8e874f29 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -149,7 +149,7 @@ ReflectDamage.Override= ; integer ReflectDamage.UseInvokerAsOwner=false ; boolean DisableWeapons=false ; boolean Unkillable=false ; boolean -Bounty= ; boolean +Bounty= ; boolean LaserTrail.Type= ; LaserTrailType Groups= ; comma-separated list of strings (group IDs) @@ -2176,28 +2176,28 @@ TiberiumEater.AnimMove=true ; boolean In `rulesmd.ini`: ```ini [General] -Bounty.Enable=false ; boolean -Bounty.Enablers= ; list ot TechnoTypes -Bounty.Default=false ; boolean -Bounty.Multiplier=1.0 ; double -Bounty.KillerMultiplier=1.0 ; double +Bounty.Enable=false ; boolean +Bounty.Enablers= ; list of TechnoTypes +Bounty.Default=false ; boolean +Bounty.Multiplier=1.0 ; double +Bounty.KillerMultiplier=1.0 ; double [AudioVisual] -Bounty.Display=false ; boolean -Bounty.Display.House=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) +Bounty.Display=false ; boolean +Bounty.Display.House=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) -[SOMETECHNO] ; TechnoType -Bounty= ; boolean -Bounty.Multiplier= ; double +[SOMETECHNO] ; TechnoType +Bounty= ; boolean +Bounty.Multiplier= ; double Bounty.Multiplier.Veteran= ; double -Bounty.Multiplier.Elite ; double -Bounty.Value=0 ; integer +Bounty.Multiplier.Elite= ; double +Bounty.Value=0 ; integer Bounty.Value.Veteran= ; integer -Bounty.Value.Elite= ; integer -Bounty.KillerMultiplier= ; double +Bounty.Value.Elite= ; integer +Bounty.KillerMultiplier= ; double Bounty.KillerMultiplier.Veteran= ; double -Bounty.KillerMultiplier.Elite= ; double -Bounty.Display= ; boolean +Bounty.KillerMultiplier.Elite= ; double +Bounty.Display= ; boolean Bounty.Display.House= ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) Bounty.Display.Offset=0,0 ; X,Y ``` From 859efe58773156208e7e7a5252fc53780ad6cc12 Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Mon, 23 Feb 2026 22:00:43 +0800 Subject: [PATCH 03/11] Update docs --- docs/New-or-Enhanced-Logics.md | 41 ++++++---- docs/Whats-New.md | 2 +- docs/locale/zh_CN/LC_MESSAGES/CREDITS.po | 3 + .../LC_MESSAGES/New-or-Enhanced-Logics.po | 79 +++++++++++++++++++ docs/locale/zh_CN/LC_MESSAGES/Whats-New.po | 5 ++ 5 files changed, 115 insertions(+), 15 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 5c8e874f29..f5f7ef468d 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -58,6 +58,8 @@ This page describes all the engine features that are either new and introduced b - `DisableWeapons` can be used to disable ability to fire any and all weapons. - On TechnoTypes with `OpenTopped=true`, `OpenTopped.CheckTransportDisableWeapons` can be set to true to make passengers not be able to fire out if transport's weapons are disabled by `DisableWeapons`. - `Unkillable` can be used to prevent the techno from being killed by taken damage (minimum health will be 1). + - `Bounty` can be used to override the option with the same name in technology types, enabling it for technology types that originally did not have the Bounty Hunter ability, or conversely, disabling it. + - If multiple AEs with this setting exist simultaneously, the last AE applied and in effect shall prevail. - It is possible to set groups for attach effect types by defining strings in `Groups`. - Groups can be used instead of types for removing effects and weapon filters. @@ -2170,8 +2172,15 @@ TiberiumEater.AnimMove=true ; boolean ### New bounty logic -- Like ares's bounty logic, but it's easyer to use than ares. - +- Similar to [Ares' bounty logic](https://ares-developers.github.io/Ares-docs/new/bounty.html), but with more configurable options and easier to use. +- `Bounty.Enable` is the master switch for the new bounty logic; it must be turned on first to utilize the new bounty system. +- `Bounty.Enablers` specifies the TechnoTypes that grant the bounty capability, not limited to buildings. + - If this list is empty, the bounty logic is enabled without requiring any prerequisite TechnoTypes. +- `Bounty.Multiplier` is the multiplier applied to the bounty a hunter receives when a victim is killed. This is defined on the victim. +- `Bounty.Value` is the fixed amount of funds a hunter receives when a victim is killed. This is also defined on the victim. + - If `Bounty.Multiplier` is not 0, the base bounty value is the victim's actual cost multiplied by this multiplier; otherwise, the base value is the amount set by `Bounty.Value`. +- `Bounty.KillerMultiplier` is the multiplier applied to the bounty received when killing a target. This is defined on the hunter. +- `Bounty.Display` controls whether the bounty amount is shown, displayed on the hunter. In `rulesmd.ini`: ```ini @@ -2187,19 +2196,23 @@ Bounty.Display=false ; boolean Bounty.Display.House=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) [SOMETECHNO] ; TechnoType -Bounty= ; boolean -Bounty.Multiplier= ; double -Bounty.Multiplier.Veteran= ; double -Bounty.Multiplier.Elite= ; double +Bounty= ; boolean, default to [General] -> Bounty.Default +Bounty.Multiplier= ; double, default to [General] -> Bounty.Multiplier +Bounty.Multiplier.Veteran= ; double, default to [TechnoType] -> Bounty.Multiplier +Bounty.Multiplier.Elite= ; double, default to [TechnoType] -> Bounty.Multiplier Bounty.Value=0 ; integer -Bounty.Value.Veteran= ; integer -Bounty.Value.Elite= ; integer -Bounty.KillerMultiplier= ; double -Bounty.KillerMultiplier.Veteran= ; double -Bounty.KillerMultiplier.Elite= ; double -Bounty.Display= ; boolean -Bounty.Display.House= ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) -Bounty.Display.Offset=0,0 ; X,Y +Bounty.Value.Veteran= ; integer, default to [TechnoType] -> Bounty.Value +Bounty.Value.Elite= ; integer, default to [TechnoType] -> Bounty.Value +Bounty.KillerMultiplier= ; double, default to [General] -> Bounty.KillerMultiplier +Bounty.KillerMultiplier.Veteran= ; double, default to [TechnoType] -> Bounty.KillerMultiplier +Bounty.KillerMultiplier.Elite= ; double, default to [TechnoType] -> Bounty.KillerMultiplier +Bounty.Display= ; boolean, default to [AudioVisual] -> Bounty.Display +Bounty.Display.House= ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all), default to [AudioVisual] -> Bounty.Display.House +Bounty.Display.Offset=0,0 ; X,Y, pixels relative to default +``` + +```{note} +Do not use this alongside Ares' bounty logic; although it will not cause a crash, the hunter will receive a bounty from each logic. ``` ### Weapons fired on warping in / out diff --git a/docs/Whats-New.md b/docs/Whats-New.md index b3cf9fbe5b..224775d6ef 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -542,7 +542,7 @@ New: - Allow jumpjet climbing ignore building height (by TaranDahl) - [Allow draw SuperWeapon timer as percentage](User-Interface.md#allow-draw-superweapon-timer-as-percentage) (by NetsuNegi) - Customize particle system of parasite logic (by NetsuNegi) -- New bounty logic (by NetsuNegi) +- [New bounty logic](New-or-Enhanced-Logics.md#new-bounty-logic) (by NetsuNegi) 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/CREDITS.po b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po index ddf9c5ab8b..33fae98b48 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po +++ b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po @@ -1635,6 +1635,9 @@ msgid "" "passenger, parasite and hijacker" msgstr "修复了 `PassengerDeletion` 未考虑乘客的乘客、寄生者和偷车贼的问题" +msgid "New bounty logic" +msgstr "新版赏金逻辑" + msgid "**Apollo** - Translucent SHP drawing patches" msgstr "**Apollo** - 半透明 SHP 绘制补丁" 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 bcfa729c78..8574c01d43 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 @@ -432,6 +432,19 @@ msgid "" " damage (minimum health will be 1)." msgstr "`Unkillable` 可用于防止科技类型因受到伤害而死亡(最低为 1)。" +msgid "" +"`Bounty` can be used to override the option with the same name in " +"technology types, enabling it for technology types that originally did " +"not have the Bounty Hunter ability, or conversely, disabling it." +msgstr "" +"`Bounty` 可用于覆盖科技类型上的同名设置,以使得本没有赏金猎人能力的单位可以启动它,或反过来禁用赏金猎人能力。" + +msgid "" +"If multiple AEs with this setting exist simultaneously, the last AE " +"applied and in effect shall prevail." +msgstr "" +"若同时存在多个带有该设置的 AE 则以最后赋予且处于生效状态的 AE 为准。" + msgid "" "It is possible to set groups for attach effect types by defining strings " "in `Groups`." @@ -4793,6 +4806,72 @@ msgid "" "the TechnoType." msgstr "若 `TiberiumEater.AnimMove` 设为 true 则动画会跟随单位移动。" +msgid "New bounty logic" +msgstr "新版赏金逻辑" + +msgid "" +"Similar to [Ares' bounty logic](https://ares-developers.github.io/Ares-" +"docs/new/bounty.html), but with more configurable options and easier to " +"use." +msgstr "" +"类似于 [Ares 的赏金逻辑](https://ares-developers.github.io/Ares-" +"docs/new/bounty.html),但允许更多设置项且更加易用。" + +msgid "" +"`Bounty.Enable` is the master switch for the new bounty logic; it must be" +" turned on first to utilize the new bounty system." +msgstr "" +"`Bounty.Enable` 是整个新版赏金逻辑的总开关,只有先打开它才能使用新版赏金逻辑。" + +msgid "" +"`Bounty.Enablers` specifies the TechnoTypes that grant the bounty " +"capability, not limited to buildings." +msgstr "" +"`Bounty.Enablers` 指定给予所属方赏金能力所需的科技类型,不局限于建筑。" + +msgid "" +"If this list is empty, the bounty " +"logic is enabled without requiring any prerequisite TechnoTypes." +msgstr "" +"若该列表为空,则所属方无需任何科技类型作为前提即可启动赏金逻辑。" + +msgid "" +"`Bounty.Multiplier` is the multiplier applied to the bounty a hunter " +"receives when a victim is killed. This is defined on the victim." +msgstr "" +"`Bounty.Multiplier` 用于在猎物上设置其被击杀时猎人所得赏金的倍率。" + +msgid "" +"`Bounty.Value` is the fixed amount of funds a hunter receives when a " +"victim is killed. This is also defined on the victim." +msgstr "" +"`Bounty.Value` 用于在猎物上设置其被击杀时猎人所得的固定资金额度。" + +msgid "" +"If `Bounty.Multiplier` is not 0, the base bounty value is the victim's " +"actual cost multiplied by this multiplier; otherwise, the base value is " +"the amount set by `Bounty.Value`." +msgstr "" +"若 `Bounty.Multiplier` 非 0 则以猎物的实际造价乘以此倍率作为基准值,否则使用 `Bounty.Value` 设定的值。" + +msgid "" +"`Bounty.KillerMultiplier` is the multiplier applied to the bounty " +"received when killing a target. This is defined on the hunter." +msgstr "" +"`Bounty.KillerMultiplier` 用于在猎人上设置击杀目标是所得赏金的倍率。" + +msgid "" +"`Bounty.Display` controls whether the bounty amount is shown, displayed " +"on the hunter." +msgstr "" +"`Bounty.Display` 控制是否在猎人身上显示赏金金额数值。" + +msgid "" +"Do not use this alongside Ares' bounty logic; although it will not cause " +"a crash, the hunter will receive a bounty from each logic." +msgstr "" +"不要和 Ares 的赏金逻辑共同使用,虽然这不会导致崩溃,但猎人会从两个逻辑各获得一份赏金。" + msgid "Weapons fired on warping in / out" msgstr "武器在传送时开火" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po index 33b3d3b24d..fd2e217021 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po @@ -1713,6 +1713,11 @@ msgstr "" msgid "Customize particle system of parasite logic (by NetsuNegi)" msgstr "自定义寄生粒子系统(by NetsuNegi)" +msgid "" +"[New bounty logic](New-or-Enhanced-Logics.md#new-bounty-logic) (by " +"NetsuNegi)" +msgstr "[新版赏金逻辑](New-or-Enhanced-Logics.md#new-bounty-logic)(by NetsuNegi)" + msgid "Vanilla fixes:" msgstr "原版问题修复:" From 2860cc8715c81bb30e6941b18da78dae3327453b Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Mon, 23 Feb 2026 22:04:26 +0800 Subject: [PATCH 04/11] =?UTF-8?q?`Bounty.Display.House`=E2=86=92`Bounty.Di?= =?UTF-8?q?splay.Houses`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ext/Rules/Body.cpp | 4 ++-- src/Ext/Rules/Body.h | 4 ++-- src/Ext/Techno/Body.cpp | 2 +- src/Ext/TechnoType/Body.cpp | 4 ++-- src/Ext/TechnoType/Body.h | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 2295647b14..78a3835307 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -374,7 +374,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->Bounty_Multiplier.Read(exINI, GameStrings::General, "Bounty.Multiplier"); this->Bounty_KillerMultiplier.Read(exINI, GameStrings::General, "Bounty.KillerMultiplier"); this->Bounty_Display.Read(exINI, GameStrings::AudioVisual, "Bounty.Display"); - this->Bounty_Display_House.Read(exINI, GameStrings::AudioVisual, "Bounty.Display.House"); + this->Bounty_Display_Houses.Read(exINI, GameStrings::AudioVisual, "Bounty.Display.Houses"); this->UnitsUnsellable.Read(exINI, GameStrings::General, "UnitsUnsellable"); @@ -686,7 +686,7 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->Bounty_Multiplier) .Process(this->Bounty_KillerMultiplier) .Process(this->Bounty_Display) - .Process(this->Bounty_Display_House) + .Process(this->Bounty_Display_Houses) .Process(this->UnitsUnsellable) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 5788c74e75..741985f6a7 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -324,7 +324,7 @@ class RulesExt Valueable Bounty_Multiplier; Valueable Bounty_KillerMultiplier; Valueable Bounty_Display; - Valueable Bounty_Display_House; + Valueable Bounty_Display_Houses; Valueable UnitsUnsellable; @@ -596,7 +596,7 @@ class RulesExt , Bounty_Multiplier { 1.0 } , Bounty_KillerMultiplier { 1.0 } , Bounty_Display { false } - , Bounty_Display_House { AffectedHouse::All } + , Bounty_Display_Houses { AffectedHouse::All } , UnitsUnsellable { false } { } diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 4672805912..b9a44e21b7 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1002,7 +1002,7 @@ void TechnoExt::GiveBounty(TechnoClass* pVictim, TechnoClass* pKiller, int victi if (pKillerTypeExt->Bounty_Display.Get(RulesExt::Global()->Bounty_Display)) { - const auto displayTo = pKillerTypeExt->Bounty_Display_House.Get(RulesExt::Global()->Bounty_Display_House); + const auto displayTo = pKillerTypeExt->Bounty_Display_Houses.Get(RulesExt::Global()->Bounty_Display_Houses); FlyingStrings::AddMoneyString(value, pKiller, pKiller->Owner, displayTo, pKiller->Location, pKillerTypeExt->Bounty_Display_Offset); } } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index d1a4e13aea..627c0db7cb 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1169,7 +1169,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Bounty_KillerMultiplier_Vet.Read(exINI, pSection, "Bounty.KillerMultiplier.Veteran"); this->Bounty_KillerMultiplier_Elite.Read(exINI, pSection, "Bounty.KillerMultiplier.Elite"); this->Bounty_Display.Read(exINI, pSection, "Bounty.Display"); - this->Bounty_Display_House.Read(exINI, pSection, "Bounty.Display.House"); + this->Bounty_Display_Houses.Read(exINI, pSection, "Bounty.Display.Houses"); this->Bounty_Display_Offset.Read(exINI, pSection, "Bounty.Display.Offset"); // Ares 0.2 @@ -1896,7 +1896,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->Bounty_KillerMultiplier_Vet) .Process(this->Bounty_KillerMultiplier_Elite) .Process(this->Bounty_Display) - .Process(this->Bounty_Display_House) + .Process(this->Bounty_Display_Houses) .Process(this->Bounty_Display_Offset) .Process(this->Unsellable) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 0324a1868d..dec225fd42 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -494,7 +494,7 @@ class TechnoTypeExt Nullable Bounty_KillerMultiplier_Vet; Nullable Bounty_KillerMultiplier_Elite; Nullable Bounty_Display; - Nullable Bounty_Display_House; + Nullable Bounty_Display_Houses; Valueable Bounty_Display_Offset; Nullable Unsellable; // Ares 3.0 @@ -947,7 +947,7 @@ class TechnoTypeExt , Bounty_KillerMultiplier_Vet {} , Bounty_KillerMultiplier_Elite {} , Bounty_Display {} - , Bounty_Display_House {} + , Bounty_Display_Houses {} , Bounty_Display_Offset { Point2D::Empty } , Unsellable {} From 46436bdc0dac21361ba1a05acaa116d2e74f833c Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Mon, 23 Feb 2026 22:06:28 +0800 Subject: [PATCH 05/11] =?UTF-8?q?[doc]=20`Bounty.Display.House`=E2=86=92`B?= =?UTF-8?q?ounty.Display.Houses`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/New-or-Enhanced-Logics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index f5f7ef468d..3df706aed2 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2193,7 +2193,7 @@ Bounty.KillerMultiplier=1.0 ; double [AudioVisual] Bounty.Display=false ; boolean -Bounty.Display.House=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) +Bounty.Display.Houses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) [SOMETECHNO] ; TechnoType Bounty= ; boolean, default to [General] -> Bounty.Default @@ -2207,7 +2207,7 @@ Bounty.KillerMultiplier= ; double, default to [General] -> Bounty.Kil Bounty.KillerMultiplier.Veteran= ; double, default to [TechnoType] -> Bounty.KillerMultiplier Bounty.KillerMultiplier.Elite= ; double, default to [TechnoType] -> Bounty.KillerMultiplier Bounty.Display= ; boolean, default to [AudioVisual] -> Bounty.Display -Bounty.Display.House= ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all), default to [AudioVisual] -> Bounty.Display.House +Bounty.Display.Houses= ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all), default to [AudioVisual] -> Bounty.Display.Houses Bounty.Display.Offset=0,0 ; X,Y, pixels relative to default ``` From 1d32ec891e3914edbc6ac9d6806f79647de91b0a Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Mon, 23 Feb 2026 22:08:37 +0800 Subject: [PATCH 06/11] Update New-or-Enhanced-Logics.md --- docs/New-or-Enhanced-Logics.md | 2 +- docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 3df706aed2..96258721c0 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -58,7 +58,7 @@ This page describes all the engine features that are either new and introduced b - `DisableWeapons` can be used to disable ability to fire any and all weapons. - On TechnoTypes with `OpenTopped=true`, `OpenTopped.CheckTransportDisableWeapons` can be set to true to make passengers not be able to fire out if transport's weapons are disabled by `DisableWeapons`. - `Unkillable` can be used to prevent the techno from being killed by taken damage (minimum health will be 1). - - `Bounty` can be used to override the option with the same name in technology types, enabling it for technology types that originally did not have the Bounty Hunter ability, or conversely, disabling it. + - `Bounty` can be used to override the option with the same name in TechnoTypes, enabling it for TechnoTypes that originally did not have the Bounty Hunter ability, or conversely, disabling it. - If multiple AEs with this setting exist simultaneously, the last AE applied and in effect shall prevail. - It is possible to set groups for attach effect types by defining strings in `Groups`. - Groups can be used instead of types for removing effects and weapon filters. 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 8574c01d43..dd5cea7772 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 @@ -434,7 +434,7 @@ msgstr "`Unkillable` 可用于防止科技类型因受到伤害而死亡(最 msgid "" "`Bounty` can be used to override the option with the same name in " -"technology types, enabling it for technology types that originally did " +"TechnoTypes, enabling it for TechnoTypes that originally did " "not have the Bounty Hunter ability, or conversely, disabling it." msgstr "" "`Bounty` 可用于覆盖科技类型上的同名设置,以使得本没有赏金猎人能力的单位可以启动它,或反过来禁用赏金猎人能力。" From 912d03c9ccf8dbfe8ae6158d696bdb3a39c6114d Mon Sep 17 00:00:00 2001 From: Noble Fish <89088785+DeathFishAtEase@users.noreply.github.com> Date: Mon, 23 Feb 2026 23:06:20 +0800 Subject: [PATCH 07/11] Resolve the alignment issue introduced by batch replacement --- docs/New-or-Enhanced-Logics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 96258721c0..cbe594e647 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2193,7 +2193,7 @@ Bounty.KillerMultiplier=1.0 ; double [AudioVisual] Bounty.Display=false ; boolean -Bounty.Display.Houses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) +Bounty.Display.Houses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) [SOMETECHNO] ; TechnoType Bounty= ; boolean, default to [General] -> Bounty.Default @@ -2207,7 +2207,7 @@ Bounty.KillerMultiplier= ; double, default to [General] -> Bounty.Kil Bounty.KillerMultiplier.Veteran= ; double, default to [TechnoType] -> Bounty.KillerMultiplier Bounty.KillerMultiplier.Elite= ; double, default to [TechnoType] -> Bounty.KillerMultiplier Bounty.Display= ; boolean, default to [AudioVisual] -> Bounty.Display -Bounty.Display.Houses= ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all), default to [AudioVisual] -> Bounty.Display.Houses +Bounty.Display.Houses= ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all), default to [AudioVisual] -> Bounty.Display.Houses Bounty.Display.Offset=0,0 ; X,Y, pixels relative to default ``` From b6fbe52d02bf49a5d60d0206b5e4dab7e1a9c75a Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Tue, 24 Feb 2026 07:30:24 +0800 Subject: [PATCH 08/11] update --- src/Ext/Techno/Body.cpp | 61 ++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index b9a44e21b7..d47654174c 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -927,41 +927,41 @@ void TechnoExt::GiveBounty(TechnoClass* pVictim, TechnoClass* pKiller, int victi return; const auto pVictimTypeExt = TechnoTypeExt::ExtMap.Find(pVictim->GetTechnoType()); - double victimMultiplier = 1.0; + int value = pVictimTypeExt->Bounty_Value.Get(pVictim); - switch (pVictim->Veterancy.GetRemainingLevel()) + if (!value) { - case Rank::Elite: - if (pVictimTypeExt->Bounty_Multiplier_Elite.isset()) - { - victimMultiplier = pVictimTypeExt->Bounty_Multiplier_Elite.Get(); - break; - } + double victimMultiplier = 1.0; - case Rank::Veteran: - if (pVictimTypeExt->Bounty_Multiplier_Vet.isset()) + switch (pVictim->Veterancy.GetRemainingLevel()) { - victimMultiplier = pVictimTypeExt->Bounty_Multiplier_Elite.Get(); - break; - } + case Rank::Elite: + if (pVictimTypeExt->Bounty_Multiplier_Elite.isset()) + { + victimMultiplier = pVictimTypeExt->Bounty_Multiplier_Elite.Get(); + break; + } - default: - if (pVictimTypeExt->Bounty_Multiplier.isset()) - { - victimMultiplier = pVictimTypeExt->Bounty_Multiplier.Get(); - break; - } + case Rank::Veteran: + if (pVictimTypeExt->Bounty_Multiplier_Vet.isset()) + { + victimMultiplier = pVictimTypeExt->Bounty_Multiplier_Elite.Get(); + break; + } - victimMultiplier = RulesExt::Global()->Bounty_Multiplier; - } + default: + if (pVictimTypeExt->Bounty_Multiplier.isset()) + { + victimMultiplier = pVictimTypeExt->Bounty_Multiplier.Get(); + break; + } - int defaultCost = victimMultiplier ? static_cast(std::round(victimCost * victimMultiplier)) : pVictimTypeExt->Bounty_Value.Get(pVictim); + victimMultiplier = RulesExt::Global()->Bounty_Multiplier; + } - if (!defaultCost) - { - defaultCost = victimCost; + value = static_cast(std::round(victimCost * victimMultiplier)); - if (!defaultCost) + if (!value) return; } @@ -993,7 +993,7 @@ void TechnoExt::GiveBounty(TechnoClass* pVictim, TechnoClass* pKiller, int victi killerMultiplier = RulesExt::Global()->Bounty_KillerMultiplier; } - const int value = static_cast(std::round(defaultCost * killerMultiplier)); + value = static_cast(std::round(value * killerMultiplier)); if (!value || !pKillerHouse->CanTransactMoney(value)) return; @@ -1003,7 +1003,12 @@ void TechnoExt::GiveBounty(TechnoClass* pVictim, TechnoClass* pKiller, int victi if (pKillerTypeExt->Bounty_Display.Get(RulesExt::Global()->Bounty_Display)) { const auto displayTo = pKillerTypeExt->Bounty_Display_Houses.Get(RulesExt::Global()->Bounty_Display_Houses); - FlyingStrings::AddMoneyString(value, pKiller, pKiller->Owner, displayTo, pKiller->Location, pKillerTypeExt->Bounty_Display_Offset); + auto pDisplayOn = pKiller; + + if (const auto pTransporter = pKiller->Transporter) + pDisplayOn = pTransporter; + + FlyingStrings::AddMoneyString(value, pKiller, pKiller->Owner, displayTo, pDisplayOn->Location, pKillerTypeExt->Bounty_Display_Offset); } } From c5da8ab49f12b91c8daca5c796a4c1dfa391de19 Mon Sep 17 00:00:00 2001 From: NetsuNegi39 Date: Tue, 24 Feb 2026 07:34:25 +0800 Subject: [PATCH 09/11] Update New-or-Enhanced-Logics.md --- docs/New-or-Enhanced-Logics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index cbe594e647..0ad3322f9c 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2178,7 +2178,7 @@ TiberiumEater.AnimMove=true ; boolean - If this list is empty, the bounty logic is enabled without requiring any prerequisite TechnoTypes. - `Bounty.Multiplier` is the multiplier applied to the bounty a hunter receives when a victim is killed. This is defined on the victim. - `Bounty.Value` is the fixed amount of funds a hunter receives when a victim is killed. This is also defined on the victim. - - If `Bounty.Multiplier` is not 0, the base bounty value is the victim's actual cost multiplied by this multiplier; otherwise, the base value is the amount set by `Bounty.Value`. + - If `Bounty.Value` is not 0, the base value is the amount set by `Bounty.Value`; otherwise, the base bounty value is the victim's actual cost multiplied by `Bounty.Multiplier`. - `Bounty.KillerMultiplier` is the multiplier applied to the bounty received when killing a target. This is defined on the hunter. - `Bounty.Display` controls whether the bounty amount is shown, displayed on the hunter. From bee2f2c9f3ee2762a229fd7c464cdbd10d01a051 Mon Sep 17 00:00:00 2001 From: Netsu_Negi <71598172+NetsuNegi@users.noreply.github.com> Date: Tue, 24 Feb 2026 07:49:07 +0800 Subject: [PATCH 10/11] Update New-or-Enhanced-Logics.po --- docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 dd5cea7772..f56f919949 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 @@ -4848,11 +4848,12 @@ msgstr "" "`Bounty.Value` 用于在猎物上设置其被击杀时猎人所得的固定资金额度。" msgid "" -"If `Bounty.Multiplier` is not 0, the base bounty value is the victim's " -"actual cost multiplied by this multiplier; otherwise, the base value is " -"the amount set by `Bounty.Value`." +"If `Bounty.Value` is not 0, the base value is the amount set by " +"`Bounty.Value`; otherwise, the base bounty value is the victim's " +"actual cost multiplied by `Bounty.Multiplier`." msgstr "" -"若 `Bounty.Multiplier` 非 0 则以猎物的实际造价乘以此倍率作为基准值,否则使用 `Bounty.Value` 设定的值。" +"若 `Bounty.Value` 非 0 则以`Bounty.Value` 设定的值作为基准值,否则使用" +"猎物的实际造价乘以 `Bounty.Multiplier` 。" msgid "" "`Bounty.KillerMultiplier` is the multiplier applied to the bounty " From 19d27ec2fc9d3678b6f98fd5243b25885796f1b4 Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Tue, 24 Feb 2026 11:24:50 +0800 Subject: [PATCH 11/11] Update New-or-Enhanced-Logics.po --- docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f56f919949..4defcc8037 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 @@ -4852,7 +4852,7 @@ msgid "" "`Bounty.Value`; otherwise, the base bounty value is the victim's " "actual cost multiplied by `Bounty.Multiplier`." msgstr "" -"若 `Bounty.Value` 非 0 则以`Bounty.Value` 设定的值作为基准值,否则使用" +"若 `Bounty.Value` 非 0 则以 `Bounty.Value` 设定的值作为基准值,否则使用" "猎物的实际造价乘以 `Bounty.Multiplier` 。" msgid ""