Skip to content

Commit 4399dc1

Browse files
Enhance r-click melee check to include target object
1 parent a5c048e commit 4399dc1

File tree

4 files changed

+189
-4
lines changed

4 files changed

+189
-4
lines changed

X2WOTCCommunityHighlander/Src/XComGame/Classes/CHHelpers.uc

+104
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,24 @@ struct OverrideHasHeightAdvantageStruct
287287
var protectedwrite array<OverrideHasHeightAdvantageStruct> OverrideHasHeightAdvantageCallbacks;
288288
// End Issue #851
289289

290+
// Start Issue #1138
291+
struct PrioritizeRightClickMeleeStruct
292+
{
293+
var delegate<PrioritizeRightClickMeleeDelegate> PrioritizeRightClickMeleeFn;
294+
var int Priority;
295+
296+
structdefaultproperties
297+
{
298+
Priority = 50
299+
}
300+
};
301+
var protectedwrite array<PrioritizeRightClickMeleeStruct> PrioritizeRightClickMeleeCallbacks;
302+
// End Issue #1138
303+
290304
delegate EHLDelegateReturn ShouldDisplayMultiSlotItemInStrategyDelegate(XComGameState_Unit UnitState, XComGameState_Item ItemState, out int bDisplayItem, XComUnitPawn UnitPawn, optional XComGameState CheckGameState); // Issue #885
291305
delegate EHLDelegateReturn ShouldDisplayMultiSlotItemInTacticalDelegate(XComGameState_Unit UnitState, XComGameState_Item ItemState, out int bDisplayItem, XGUnit UnitVisualizer, optional XComGameState CheckGameState); // Issue #885
292306
delegate EHLDelegateReturn OverrideHasHeightAdvantageDelegate(XComGameState_Unit Attacker, XComGameState_Unit TargetUnit, out int bHasHeightAdvantage); // Issue #851
307+
delegate EHLDelegateReturn PrioritizeRightClickMeleeDelegate(XComGameState_Unit UnitState, XComGameState_BaseObject TargetObject, out XComGameState_Ability PrioritizedMeleeAbility); // Issue #1138
293308

294309
// Start Issue #123
295310
simulated static function RebuildPerkContentCache() {
@@ -949,6 +964,95 @@ simulated function TriggerOverrideHasHeightAdvantage(XComGameState_Unit Attacker
949964
}
950965
// End Issue #851
951966

967+
// Start Issue #1138
968+
/// HL-Docs: ref:PrioritizeRightClickMelee
969+
/// # Delegate Priority
970+
/// You can optionally specify callback Priority.
971+
///```unrealscript
972+
///CHHelpersObj.AddPrioritizeRightClickMeleeCallback(PrioritizeRightClickMelee, 45);
973+
///```
974+
/// Delegates with higher Priority value are executed first.
975+
/// Delegates with the same Priority are executed in the order they were added to CHHelpers,
976+
/// which would normally be the same as [DLCRunOrder](../misc/DLCRunOrder.md).
977+
/// This function will return `true` if the delegate was successfully registered.
978+
simulated function bool AddPrioritizeRightClickMeleeCallback(delegate<PrioritizeRightClickMeleeDelegate> PrioritizeRightClickMeleeFn, optional int Priority = 50)
979+
{
980+
local PrioritizeRightClickMeleeStruct NewPrioritizeRightClickMeleeCallback;
981+
local int i, PriorityIndex;
982+
local bool bPriorityIndexFound;
983+
984+
if (PrioritizeRightClickMeleeFn == none)
985+
{
986+
return false;
987+
}
988+
// Cycle through the array of callbacks backwards
989+
for (i = PrioritizeRightClickMeleeCallbacks.Length - 1; i >= 0; i--)
990+
{
991+
// Do not allow registering the same delegate more than once.
992+
if (PrioritizeRightClickMeleeCallbacks[i].PrioritizeRightClickMeleeFn == PrioritizeRightClickMeleeFn)
993+
{
994+
return false;
995+
}
996+
997+
// Record the array index of the callback whose priority is higher or equal to the priority of the new callback,
998+
// so that the new callback can be inserted right after it.
999+
if (PrioritizeRightClickMeleeCallbacks[i].Priority >= Priority && !bPriorityIndexFound)
1000+
{
1001+
PriorityIndex = i + 1; // +1 so that InsertItem puts the new callback *after* this one.
1002+
1003+
// Keep cycling through the array so that the previous check for duplicate delegates can run for every currently registered delegate.
1004+
bPriorityIndexFound = true;
1005+
}
1006+
}
1007+
1008+
NewPrioritizeRightClickMeleeCallback.Priority = Priority;
1009+
NewPrioritizeRightClickMeleeCallback.PrioritizeRightClickMeleeFn = PrioritizeRightClickMeleeFn;
1010+
PrioritizeRightClickMeleeCallbacks.InsertItem(PriorityIndex, NewPrioritizeRightClickMeleeCallback);
1011+
1012+
return true;
1013+
}
1014+
1015+
/// HL-Docs: ref:PrioritizeRightClickMelee
1016+
/// # Removing Delegates
1017+
/// If necessary, it's possible to remove a delegate.
1018+
///```unrealscript
1019+
///CHHelpersObj.RemovePrioritizeRightClickMeleeCallback(PrioritizeRightClickMelee);
1020+
///```
1021+
/// The function will return `true` if the Callback was successfully deleted, return false otherwise.
1022+
simulated function bool RemovePrioritizeRightClickMeleeCallback(delegate<PrioritizeRightClickMeleeDelegate> PrioritizeRightClickMeleeFn)
1023+
{
1024+
local int i;
1025+
1026+
for (i = PrioritizeRightClickMeleeCallbacks.Length - 1; i >= 0; i--)
1027+
{
1028+
if (PrioritizeRightClickMeleeCallbacks[i].PrioritizeRightClickMeleeFn == PrioritizeRightClickMeleeFn)
1029+
{
1030+
PrioritizeRightClickMeleeCallbacks.Remove(i, 1);
1031+
return true;
1032+
}
1033+
}
1034+
return false;
1035+
}
1036+
1037+
// Called by X2AbilityTrigger_EndOfMove::GetAvailableEndOfMoveAbilityForUnit()
1038+
// This is an internal CHL API. It is not intended for use by mods and is not covered by Backwards Compatibility policy.
1039+
simulated function TriggerPrioritizeRightClickMelee(XComGameState_Unit UnitState, XComGameState_BaseObject TargetObject, out XComGameState_Ability PrioritizedMeleeAbility)
1040+
{
1041+
local delegate<PrioritizeRightClickMeleeDelegate> PrioritizeRightClickMeleeFn;
1042+
local int i;
1043+
1044+
for (i = 0; i < PrioritizeRightClickMeleeCallbacks.Length; i++)
1045+
{
1046+
PrioritizeRightClickMeleeFn = PrioritizeRightClickMeleeCallbacks[i].PrioritizeRightClickMeleeFn;
1047+
1048+
if (PrioritizeRightClickMeleeFn(UnitState, TargetObject, PrioritizedMeleeAbility) == EHLDR_InterruptDelegates)
1049+
{
1050+
break;
1051+
}
1052+
}
1053+
}
1054+
// End Issue #1138
1055+
9521056
// Start Issue #855
9531057
static function name GetPlaceEvacZoneAbilityName()
9541058
{

X2WOTCCommunityHighlander/Src/XComGame/Classes/X2AbilityTrigger_EndOfMove.uc

+81-2
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,70 @@ static function bool AbilityHasEndOfMoveTrigger(X2AbilityTemplate Template)
2727
return false;
2828
}
2929

30+
/// HL-Docs: feature:PrioritizeRightClickMelee; issue:1138; tags:tactical
31+
/// This feature allows mods to define which melee ability should be prioritized for use when the
32+
/// user triggers a right-click melee action on an enemy.
33+
///
34+
/// Normally this override would have been implemented as an event, but this check happens in the
35+
/// tactical UI on user-click and movement tile hover-over which could lead to issues and slowdowns,
36+
/// so the delegates system is used instead.
37+
///
38+
/// ## How to use
39+
///
40+
/// Implement the following code in your mod's `X2DownloadableContentInfo` class:
41+
/// ```unrealscript
42+
/// static event OnPostTemplatesCreated()
43+
/// {
44+
/// local CHHelpers CHHelpersObj;
45+
///
46+
/// CHHelpersObj = class'CHHelpers'.static.GetCDO();
47+
/// if (CHHelpersObj != none)
48+
/// {
49+
/// CHHelpersObj.AddPrioritizeRightClickMeleeCallback(PrioritizeRightClickMelee);
50+
/// }
51+
/// }
52+
///
53+
/// // To avoid crashes associated with garbage collection failure when transitioning between Tactical and Strategy,
54+
/// // this function must be bound to the ClassDefaultObject of your class. Having this function in a class that
55+
/// // `extends X2DownloadableContentInfo` is the easiest way to ensure that.
56+
/// static private function EHLDelegateReturn PrioritizeRightClickMelee(XComGameState_Unit UnitState, XComGameState_BaseObject TargetObject, out XComGameState_Ability PrioritizedMeleeAbility)
57+
/// {
58+
/// // Optionally modify PrioritizedMeleeAbility here.
59+
/// // `PrioritizedMeleeAbility` is the currently prioritized melee skill to use,
60+
/// // and should be replaced with a different skill from the unit's available skills if
61+
/// // using a different prioritization schema.
62+
///
63+
/// // One way to get the target is to load its state from history: `XCOMHISTORY.GetGameStateForObjectID(TargetObject.ObjectID)
64+
/// // NOTE: TargetObject can be NONE
65+
///
66+
/// // One way to get the actions available for the unit is to use the unit cache: `TACTICALRULES.GetGameRulesCache_Unit(UnitState.GetReference(), UnitCache)
67+
///
68+
/// // Return EHLDR_NoInterrupt or EHLDR_InterruptDelegates depending on
69+
/// // if you want to allow other delegates to run after yours
70+
/// // and potentially modify PrioritizedMeleeAbility further.
71+
/// return EHLDR_NoInterrupt;
72+
///}
73+
/// ```
3074
static function XComGameState_Ability GetAvailableEndOfMoveAbilityForUnit(XComGameState_Unit UnitState)
3175
{
3276
local XComGameStateHistory History;
3377
local GameRulesCache_Unit UnitCache;
3478
local XComGameState_Ability AbilityState;
35-
local int ActionIndex;
79+
local int ActionIndex;
80+
// Variables for Issue #1138
81+
local CHHelpers CHHelpersObj;
82+
local XComGameState_Ability PrioritizedMeleeAbility;
3683

84+
// Begin Issue #1138
85+
CHHelpersObj = class'CHHelpers'.static.GetCDO();
86+
CHHelpersObj.TriggerPrioritizeRightClickMelee(UnitState, none, PrioritizedMeleeAbility);
87+
if(PrioritizedMeleeAbility != none)
88+
{
89+
return PrioritizedMeleeAbility;
90+
}
91+
// End Issue #1138
92+
93+
// Issue #1138 Original logic fallback if we don't find a priority override
3794
if(`TACTICALRULES.GetGameRulesCache_Unit(UnitState.GetReference(), UnitCache))
3895
{
3996
History = `XCOMHISTORY;
@@ -48,4 +105,26 @@ static function XComGameState_Ability GetAvailableEndOfMoveAbilityForUnit(XComGa
48105
}
49106

50107
return None;
51-
}
108+
}
109+
110+
/// Begin Issue #1138
111+
/// Add new version of the GetAvailableEndOfMoveAbilityForUnit which will allow for a TaretObject
112+
/// to be provided. This version of the ability will allow for mods to refine their targetting
113+
/// based upon what/who is being targetted, enabling such things as right-click moving medkit heals
114+
/// on friendly units.
115+
static function XComGameState_Ability CHL_GetAvailableEndOfMoveAbilityForUnit(XComGameState_Unit UnitState, XComGameState_BaseObject TargetObject)
116+
{
117+
local CHHelpers CHHelpersObj;
118+
local XComGameState_Ability PrioritizedMeleeAbility;
119+
120+
CHHelpersObj = class'CHHelpers'.static.GetCDO();
121+
CHHelpersObj.TriggerPrioritizeRightClickMelee(UnitState, TargetObject, PrioritizedMeleeAbility);
122+
if(PrioritizedMeleeAbility != none)
123+
{
124+
return PrioritizedMeleeAbility;
125+
}
126+
127+
// Original logic fallback if we don't find a priority override
128+
return GetAvailableEndOfMoveAbilityForUnit(UnitState);
129+
}
130+
/// End Issue #1138

X2WOTCCommunityHighlander/Src/XComGame/Classes/XComPathingPawn.uc

+2-1
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,8 @@ simulated /* protected */ function bool CanUnitMeleeFromMove(XComGameState_BaseO
512512

513513
// find the unit's default melee ability
514514
UnitState = XComGameState_Unit(History.GetGameStateForObjectID(LastActiveUnit.ObjectID));
515-
MeleeAbility = class'X2AbilityTrigger_EndOfMove'.static.GetAvailableEndOfMoveAbilityForUnit(UnitState);
515+
// Issue #1138 Provide the target for determining the melee action to take
516+
MeleeAbility = class'X2AbilityTrigger_EndOfMove'.static.CHL_GetAvailableEndOfMoveAbilityForUnit(UnitState, TargetObject);
516517
if(MeleeAbility == none)
517518
{
518519
return false;

X2WOTCCommunityHighlander/Src/XComGame/Classes/XComTacticalInput.uc

+2-1
Original file line numberDiff line numberDiff line change
@@ -3113,7 +3113,8 @@ function bool ClickToPath()
31133113
History = `XCOMHISTORY;
31143114

31153115
ActiveUnitState = XComGameState_Unit(History.GetGameStateForObjectID(GetActiveUnit().ObjectID));
3116-
AbilityState = class'X2AbilityTrigger_EndOfMove'.static.GetAvailableEndOfMoveAbilityForUnit(ActiveUnitState);
3116+
// Issue #1138 Provide the target for determining the melee action to take
3117+
AbilityState = class'X2AbilityTrigger_EndOfMove'.static.CHL_GetAvailableEndOfMoveAbilityForUnit(ActiveUnitState, PathingPawn.LastTargetObject);
31173118
if(AbilityState != none
31183119
&& PathingPawn.LastTargetObject != none
31193120
&& `TACTICALRULES.GetGameRulesCache_Unit(ActiveUnitState.GetReference(), UnitCache))

0 commit comments

Comments
 (0)