Skip to content

Commit

Permalink
Revamp approach to simplify and expand mod interactions
Browse files Browse the repository at this point in the history
  • Loading branch information
FearTheBunnies committed May 15, 2023
1 parent 39d60db commit 73240bd
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 1 deletion.
104 changes: 104 additions & 0 deletions X2WOTCCommunityHighlander/Src/XComGame/Classes/CHHelpers.uc
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,24 @@ struct OverrideHasHeightAdvantageStruct
var protectedwrite array<OverrideHasHeightAdvantageStruct> OverrideHasHeightAdvantageCallbacks;
// End Issue #851

// Start Issue #1138
struct PrioritizeRightClickMeleeStruct
{
var delegate<PrioritizeRightClickMeleeDelegate> PrioritizeRightClickMeleeFn;
var int Priority;

structdefaultproperties
{
Priority = 50
}
};
var protectedwrite array<PrioritizeRightClickMeleeStruct> PrioritizeRightClickMeleeCallbacks;
// End Issue #1138

delegate EHLDelegateReturn ShouldDisplayMultiSlotItemInStrategyDelegate(XComGameState_Unit UnitState, XComGameState_Item ItemState, out int bDisplayItem, XComUnitPawn UnitPawn, optional XComGameState CheckGameState); // Issue #885
delegate EHLDelegateReturn ShouldDisplayMultiSlotItemInTacticalDelegate(XComGameState_Unit UnitState, XComGameState_Item ItemState, out int bDisplayItem, XGUnit UnitVisualizer, optional XComGameState CheckGameState); // Issue #885
delegate EHLDelegateReturn OverrideHasHeightAdvantageDelegate(XComGameState_Unit Attacker, XComGameState_Unit TargetUnit, out int bHasHeightAdvantage); // Issue #851
delegate EHLDelegateReturn PrioritizeRightClickMeleeDelegate(XComGameState_Unit UnitState, out XComGameState_Ability PrioritizedMeleeAbility); // Issue #1138

// Start Issue #123
simulated static function RebuildPerkContentCache() {
Expand Down Expand Up @@ -940,6 +955,95 @@ simulated function TriggerOverrideHasHeightAdvantage(XComGameState_Unit Attacker
}
// End Issue #851

// Start Issue #1138
/// HL-Docs: ref:PrioritizeRightClickMelee
/// # Delegate Priority
/// You can optionally specify callback Priority.
///```unrealscript
///CHHelpersObj.AddPrioritizeRightClickMeleeCallback(PrioritizeRightClickMelee, 45);
///```
/// Delegates with higher Priority value are executed first.
/// Delegates with the same Priority are executed in the order they were added to CHHelpers,
/// which would normally be the same as [DLCRunOrder](../misc/DLCRunOrder.md).
/// This function will return `true` if the delegate was successfully registered.
simulated function bool AddPrioritizeRightClickMeleeCallback(delegate<PrioritizeRightClickMeleeDelegate> PrioritizeRightClickMeleeFn, optional int Priority = 50)
{
local PrioritizeRightClickMeleeStruct NewPrioritizeRightClickMeleeCallback;
local int i, PriorityIndex;
local bool bPriorityIndexFound;

if (PrioritizeRightClickMeleeFn == none)
{
return false;
}
// Cycle through the array of callbacks backwards
for (i = PrioritizeRightClickMeleeCallbacks.Length - 1; i >= 0; i--)
{
// Do not allow registering the same delegate more than once.
if (PrioritizeRightClickMeleeCallbacks[i].PrioritizeRightClickMeleeFn == PrioritizeRightClickMeleeFn)
{
return false;
}

// Record the array index of the callback whose priority is higher or equal to the priority of the new callback,
// so that the new callback can be inserted right after it.
if (PrioritizeRightClickMeleeCallbacks[i].Priority >= Priority && !bPriorityIndexFound)
{
PriorityIndex = i + 1; // +1 so that InsertItem puts the new callback *after* this one.

// Keep cycling through the array so that the previous check for duplicate delegates can run for every currently registered delegate.
bPriorityIndexFound = true;
}
}

NewPrioritizeRightClickMeleeCallback.Priority = Priority;
NewPrioritizeRightClickMeleeCallback.PrioritizeRightClickMeleeFn = PrioritizeRightClickMeleeFn;
PrioritizeRightClickMeleeCallbacks.InsertItem(PriorityIndex, NewPrioritizeRightClickMeleeCallback);

return true;
}

/// HL-Docs: ref:PrioritizeRightClickMelee
/// # Removing Delegates
/// If necessary, it's possible to remove a delegate.
///```unrealscript
///CHHelpersObj.RemovePrioritizeRightClickMeleeCallback(PrioritizeRightClickMelee);
///```
/// The function will return `true` if the Callback was successfully deleted, return false otherwise.
simulated function bool RemovePrioritizeRightClickMeleeCallback(delegate<PrioritizeRightClickMeleeDelegate> PrioritizeRightClickMeleeFn)
{
local int i;

for (i = PrioritizeRightClickMeleeCallbacks.Length - 1; i >= 0; i--)
{
if (PrioritizeRightClickMeleeCallbacks[i].PrioritizeRightClickMeleeFn == PrioritizeRightClickMeleeFn)
{
PrioritizeRightClickMeleeCallbacks.Remove(i, 1);
return true;
}
}
return false;
}

// Called by X2AbilityTrigger_EndOfMove::GetAvailableEndOfMoveAbilityForUnit()
// This is an internal CHL API. It is not intended for use by mods and is not covered by Backwards Compatibility policy.
simulated function TriggerPrioritizeRightClickMelee(XComGameState_Unit UnitState, out XComGameState_Ability PrioritizedMeleeAbility)
{
local delegate<PrioritizeRightClickMeleeDelegate> PrioritizeRightClickMeleeFn;
local int i;

for (i = 0; i < PrioritizeRightClickMeleeCallbacks.Length; i++)
{
PrioritizeRightClickMeleeFn = PrioritizeRightClickMeleeCallbacks[i].PrioritizeRightClickMeleeFn;

if (PrioritizeRightClickMeleeFn(UnitState, PrioritizedMeleeAbility) == EHLDR_InterruptDelegates)
{
break;
}
}
}
// End Issue #1138

// Start Issue #855
static function name GetPlaceEvacZoneAbilityName()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,72 @@ static function bool AbilityHasEndOfMoveTrigger(X2AbilityTemplate Template)
return false;
}

/// HL-Docs: feature:PrioritizeRightClickMelee; issue:1138; tags:tactical
/// This feature allows mods to define which melee ability should be prioritized for use when the
/// user triggers a right-click melee action on an enemy.
///
/// Normally this override would have been implemented as an event, but this check happens in the
/// tactical UI on user-click and movement tile hover-over which could lead to issues and slowdowns,
/// so the delegates system is used instead.
///
/// ## How to use
///
/// Implement the following code in your mod's `X2DownloadableContentInfo` class:
/// ```unrealscript
/// static event OnPostTemplatesCreated()
/// {
/// local CHHelpers CHHelpersObj;
///
/// CHHelpersObj = class'CHHelpers'.static.GetCDO();
/// if (CHHelpersObj != none)
/// {
/// CHHelpersObj.AddPrioritizeRightClickMeleeCallback(PrioritizeRightClickMelee);
/// }
/// }
///
/// // To avoid crashes associated with garbage collection failure when transitioning between Tactical and Strategy,
/// // this function must be bound to the ClassDefaultObject of your class. Having this function in a class that
/// // `extends X2DownloadableContentInfo` is the easiest way to ensure that.
/// static private function EHLDelegateReturn PrioritizeRightClickMelee(XComGameState_Unit UnitState, out XComGameState_Ability PrioritizedMeleeAbility)
/// {
/// local GameRulesCache_Unit UnitCache;
///
/// // To get the list of all available actions, load the unit cache from the tactical ruleset
/// if(`TACTICALRULES.GetGameRulesCache_Unit(UnitState.GetReference(), UnitCache))
/// {
/// // Optionally modify PrioritizedMeleeAbility here.
/// // `PrioritizedMeleeAbility` is the currently prioritized melee skill to use,
/// // and should be replaced with a different skill from the unit's available skills if
/// // using a different prioritization schema. Set `PrioritizedMeleeAbility` to NONE
/// // to revert to base-game behaviour.
/// }
///
/// // Return EHLDR_NoInterrupt or EHLDR_InterruptDelegates depending on
/// // if you want to allow other delegates to run after yours
/// // and potentially modify PrioritizedMeleeAbility further.
/// return EHLDR_NoInterrupt;
///}
/// ```
static function XComGameState_Ability GetAvailableEndOfMoveAbilityForUnit(XComGameState_Unit UnitState)
{
local XComGameStateHistory History;
local GameRulesCache_Unit UnitCache;
local XComGameState_Ability AbilityState;
local int ActionIndex;
// Variables for Issue #1138
local CHHelpers CHHelpersObj;
local XComGameState_Ability PrioritizedMeleeAbility;

// Begin Issue #1138
CHHelpersObj = class'CHHelpers'.static.GetCDO();
CHHelpersObj.TriggerPrioritizeRightClickMelee(UnitState, PrioritizedMeleeAbility);
if(PrioritizedMeleeAbility != none)
{
return PrioritizedMeleeAbility;
}
// End Issue #1138

// Issue #1138 Original logic fallback if we don't find a priority override
if(`TACTICALRULES.GetGameRulesCache_Unit(UnitState.GetReference(), UnitCache))
{
History = `XCOMHISTORY;
Expand All @@ -48,4 +107,4 @@ static function XComGameState_Ability GetAvailableEndOfMoveAbilityForUnit(XComGa
}

return None;
}
}

0 comments on commit 73240bd

Please sign in to comment.