-
-
Notifications
You must be signed in to change notification settings - Fork 128
New bounty logic #2118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
New bounty logic #2118
Changes from 7 commits
e8dcf29
8765b46
859efe5
2860cc8
46436bd
1d32ec8
912d03c
b6fbe52
c5da8ab
bee2f2c
19d27ec
2487771
61f9caa
f6aad5f
15b90bb
0ef519e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 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. | ||
|
|
||
|
|
@@ -149,6 +151,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 +2170,51 @@ TiberiumEater.Anims.Tiberium3= ; List of AnimationTypes | |
| TiberiumEater.AnimMove=true ; boolean | ||
| ``` | ||
|
|
||
| ### New bounty logic | ||
|
|
||
| - 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. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be allowed to be displayed on the head of the prey. |
||
|
|
||
| In `rulesmd.ini`: | ||
| ```ini | ||
| [General] | ||
| 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.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 | ||
| 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, 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.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 | ||
| ``` | ||
|
|
||
| ```{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 | ||
|
|
||
| - 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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,8 @@ | |
| #include <Ext/Event/Body.h> | ||
|
|
||
| #include <Utilities/AresFunctions.h> | ||
| #include <Ext/BuildingType/Body.h> | ||
| #include <Misc/FlyingStrings.h> | ||
|
|
||
| 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<AttachEffectClass> 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<BuildingTypeClass*>(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<int>(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<int>(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_Houses.Get(RulesExt::Global()->Bounty_Display_Houses); | ||
| 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<CoordStruct> usableCoords; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be best if the value of the holder's bounty could also be adjusted through AE.