From 8373c4b33fe3ef982b6262bfcc2d75753f4e45f8 Mon Sep 17 00:00:00 2001 From: Xymanek Date: Sat, 13 Jul 2019 08:19:52 +0300 Subject: [PATCH 1/5] Add `UITacticalHUD_MouseControls` --- .../Classes/UITacticalHUD_MouseControls.uc | 365 ++++++++++++++++++ .../X2WOTCCommunityHighlander.x2proj | 3 + 2 files changed, 368 insertions(+) create mode 100644 X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_MouseControls.uc diff --git a/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_MouseControls.uc b/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_MouseControls.uc new file mode 100644 index 000000000..07483243b --- /dev/null +++ b/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_MouseControls.uc @@ -0,0 +1,365 @@ +//--------------------------------------------------------------------------------------- +// ********* FIRAXIS SOURCE CODE ****************** +// FILE: UITacticalHUD_MouseControls.uc +// AUTHORS: Brit Steiner, Tronster +// +// PURPOSE: Container for mouse controls within the tactical hud. +//--------------------------------------------------------------------------------------- +// Copyright (c) 2016 Firaxis Games, Inc. All rights reserved. +//--------------------------------------------------------------------------------------- + +class UITacticalHUD_MouseControls extends UIPanel; + +struct TButtonItems +{ + var string Label; + var string key; + var EUIState UIState; +}; + +//---------------------------------------------------------------------------- +// MEMBERS +var localized string m_strPrevSoldier; +var localized string m_strNextSoldier; +var localized string m_strEndTurn; +var localized string m_strNoKeyBoundString; +var localized string m_strCancelShot; +var localized string m_strShotInfo; +var localized string m_strSoldierInfo; + +var int m_optPrevSoldier; +var int m_optNextSoldier; +var int m_optEndTurn; +var int m_optRotateCameraLeft; +var int m_optRotateCameraRight; +var int m_optCallSkyranger; +var int m_optChosenInfo; + +var int m_optCancelShot; +var int m_optShotInfo; + +var int m_iCurrentSelection; + +var Color m_clrBad; + +var array ButtonItems; +var array CommandAbilities; + +var UIPanel AttentionPulse; + +//---------------------------------------------------------------------------- +// METHODS +// + +simulated function UITacticalHUD_MouseControls InitMouseControls() +{ + InitPanel(); + + ButtonItems.Length = 10; + + //Ask to be dynamically bound: + UITacticalHUD(screen).InitializeMouseControls(); + + return self; +} + +simulated function OnInit() +{ + super.OnInit(); + UpdateControls(); +} + +simulated function SetCommandAbilities(array NewCommandAbilities) +{ + CommandAbilities = NewCommandAbilities; +} + +simulated function UpdateControls() +{ + local string key, label; + local PlayerInput kInput; + local XComKeybindingData kKeyData; + local int i; + local TacticalBindableCommands command; + local XComGameState_Ability AbilityState; + local XComGameStateHistory History; + local XComGameState_BattleData BattleData; + local XComGameState_Unit ChosenUnit; + + History = `XCOMHISTORY; + BattleData = XComGameState_BattleData(History.GetSingleGameStateObjectForClass(class'XComGameState_BattleData')); + + kInput = PC.PlayerInput; + kKeyData = Movie.Pres.m_kKeybindingData; + + AS_SetHoverHelp(""); + + if(UITacticalHUD(screen).m_isMenuRaised) + { + SetNumActiveControls(1); + + key = kKeyData.GetPrimaryOrSecondaryKeyStringForAction(kInput, eGBC_Cancel, eKC_General); + SetButtonItem( m_optCancelShot, m_strCancelShot, key != "" ? key : m_strNoKeyBoundString, ButtonItems[0].UIState ); + } + else + { + key = kKeyData.GetPrimaryOrSecondaryKeyStringForAction(kInput, eTBC_EndTurn); + SetButtonItem( m_optEndTurn, m_strEndTurn, key != "" ? key : m_strNoKeyBoundString, ButtonItems[m_optEndTurn].UIState ); + + key = kKeyData.GetPrimaryOrSecondaryKeyStringForAction(kInput, eTBC_PrevUnit); + SetButtonItem( m_optPrevSoldier, m_strPrevSoldier, key != "" ? key : m_strNoKeyBoundString, ButtonItems[m_optPrevSoldier].UIState ); + + key = kKeyData.GetPrimaryOrSecondaryKeyStringForAction(kInput, eTBC_NextUnit); + SetButtonItem( m_optNextSoldier, m_strNextSoldier, key != "" ? key : m_strNoKeyBoundString, ButtonItems[m_optNextSoldier].UIState ); + + key = kKeyData.GetPrimaryOrSecondaryKeyStringForAction(kInput, eTBC_CamRotateLeft); + label = kKeyData.GetTacticalBindableActionLabel(eTBC_CamRotateLeft); + SetButtonItem( m_optRotateCameraLeft, label, key != "" ? key : m_strNoKeyBoundString, ButtonItems[m_optRotateCameraLeft].UIState ); + + key = kKeyData.GetPrimaryOrSecondaryKeyStringForAction(kInput, eTBC_CamRotateRight); + label = kKeyData.GetTacticalBindableActionLabel(eTBC_CamRotateRight); + SetButtonItem( m_optRotateCameraRight, label, key != "" ? key : m_strNoKeyBoundString, ButtonItems[m_optRotateCameraRight].UIState ); + + for(i = 0; i < CommandAbilities.Length; i++) + { + command = TacticalBindableCommands(eTBC_CommandAbility1 + i); + AbilityState = XComGameState_Ability(`XCOMHISTORY.GetGameStateForObjectID(CommandAbilities[i].AbilityObjectRef.ObjectID)); + key = kKeyData.GetPrimaryOrSecondaryKeyStringForAction(kInput, command); + label = Caps(AbilityState.GetMyFriendlyName()); + SetButtonItem( m_optCallSkyranger + i, label, key != "" ? key : m_strNoKeyBoundString, ButtonItems[m_optCallSkyranger + i].UIState, BattleData.IsAbilityObjectiveHighlighted(AbilityState.GetMyTemplate())); + } + + ChosenUnit = class'XComGameState_Unit'.static.GetActivatedChosen(); + if ( ChosenUnit != none ) // Show the "Chosen Info" button if the chosen is active on a map + { + SetNumActiveControls(7); + + key = kKeyData.GetPrimaryOrSecondaryKeyStringForAction(kInput, eTBC_OpenChosenHUD); + label = kKeyData.GetTacticalBindableActionLabel(eTBC_OpenChosenHUD); + SetButtonItem(m_optChosenInfo, label, key != "" ? key : m_strNoKeyBoundString, ButtonItems[m_optChosenInfo].UIState); + } + else + { + if(CommandAbilities.Length > 0) + SetNumActiveControls(6); + else //MP has no command abilities + SetNumActiveControls(5); + } + } +} + +simulated function SetButtonItem( int index, string label, string key, EUIState uistate, optional bool bIsSpecial = false ) +{ + // Remap any key names that blow past the maximum space with the wide Asian font + if ( GetLanguage() == "KOR" || GetLanguage() == "JPN" ) + { + key = Repl( key, "BACKSPACE", "BKSPACE", false ); + } + + ButtonItems[index].Label = label; + ButtonItems[index].key = key; + ButtonItems[index].UIState = uistate; + + AS_SetIconButton(index, label, key, GetIconLabelForIndex(index), ButtonItems[index].UIState != eUIState_Disabled, bIsSpecial); +} + + +simulated function SetButtonState(int index, EUIState uistate) +{ + SetButtonItem(index, ButtonItems[index].Label, ButtonItems[index].key, uistate); +} + +simulated function OnMouseEvent(int cmd, array args) +{ + local string sButtonId; + local int RequestID; + + switch( cmd ) + { + case class'UIUtilities_Input'.const.FXS_L_MOUSE_UP: + //if( `CAMERAMGR.IsCameraBusyWithKismetLookAts() ) + // return true; + sButtonId = args[args.Length - 2]; + sButtonId -= "btn"; + m_iCurrentSelection = int(sButtonId); + OnAccept(); + break; + + case class'UIUtilities_Input'.const.FXS_L_MOUSE_IN: + sButtonId = args[args.Length - 2]; + sButtonId -= "btn"; + m_iCurrentSelection = int(sButtonId); + AS_SetHoverHelp( ButtonItems[m_iCurrentSelection].Label $": [" $ButtonItems[m_iCurrentSelection].key $"]" ); + break; + + case class'UIUtilities_Input'.const.FXS_L_MOUSE_OUT: + sButtonId = args[args.Length - 2]; + sButtonId -= "btn"; + RequestID = int(sButtonId); + if( m_iCurrentSelection == RequestID ) + AS_SetHoverHelp(""); + break; + } +} + +simulated function bool OnAccept() +{ + local bool bIsFirstTutorial; + local PlayerInput kInput; + local XComGameState_Unit ChosenUnit; + + kInput = PC.PlayerInput; + + if (ButtonItems[m_iCurrentSelection].UIState != eUIState_Normal) + { + PlaySound( SoundCue'SoundUI.NegativeSelection2Cue', true , true ); + return true; + } + + // Don't allow end turn during the first phase of the tutorial. + bIsFirstTutorial = `TUTORIAL != none; + + if( UITacticalHUD(screen).m_isMenuRaised ) + { + switch( m_iCurrentSelection ) + { + case m_optCancelShot: + UITacticalHUD(screen).CancelTargetingAction(); + break; + } + } + else + { + switch( m_iCurrentSelection ) + { + case m_optPrevSoldier: + if( bIsFirstTutorial || !XComTacticalInput(kInput).PrevUnit() ) + { + PlaySound( SoundCue'SoundUI.NegativeSelection2Cue', true , true ); + } + break; + + case m_optNextSoldier: + if( bIsFirstTutorial || !XComTacticalInput(kInput).NextUnit() ) + { + PlaySound( SoundCue'SoundUI.NegativeSelection2Cue', true , true ); + } + break; + + case m_optEndTurn: + if( bIsFirstTutorial ) + { + // In the tutorial, play negative sound to reinforce to the player that they can't click the "end turn" button. + PlaySound( SoundCue'SoundUI.NegativeSelection2Cue', true , true ); + } + else + { + // XComTacticalController.PerformEndTurn plays the select sound + // so that keyboards and game controllers also get aural feedback. + XComTacticalController(PC).PerformEndTurn(ePlayerEndTurnType_PlayerInput); + } + break; + case m_optRotateCameraLeft: + if( `ISCONTROLLERACTIVE ) + XComTacticalInput(kInput).DPad_Left(class'UIUtilities_Input'.const.FXS_ACTION_RELEASE); + else + XComTacticalInput(kInput).Key_Q(class'UIUtilities_Input'.const.FXS_ACTION_RELEASE); + break; + case m_optRotateCameraRight: + if( `ISCONTROLLERACTIVE ) + XComTacticalInput(kInput).DPad_Right(class'UIUtilities_Input'.const.FXS_ACTION_RELEASE); + else + XComTacticalInput(kInput).Key_E(class'UIUtilities_Input'.const.FXS_ACTION_RELEASE); + break; + case m_optChosenInfo: + ChosenUnit = class'XComGameState_Unit'.static.GetActivatedChosen(); + if( ChosenUnit != none ) // Allow interaction "Chosen Info" button if the chosen is active on a map + { + `PRES.UIChosenRevealScreen(); + } + break; + default: + ActivateCommandAbility(m_iCurrentSelection - 5); + break; + } + } + + return true; +} + +simulated function ActivateCommandAbility(int AbilityIndex) +{ + local int AbilityHudIndex; + + AbilityHudIndex = UITacticalHUD(Screen).m_kAbilityHUD.GetAbilityIndex(CommandAbilities[AbilityIndex]); + if(AbilityHudIndex > -1) + { + `Pres.GetTacticalHUD().m_kAbilityHUD.SelectAbility( AbilityHudIndex ); + } +} + +simulated function string GetIconLabelForIndex(int index) +{ + if(UITacticalHUD(screen).m_isMenuRaised) + { + switch(index) + { + case m_optCancelShot: return "cancelShot"; + case m_optShotInfo: return "shotInfo"; + } + } + else + { + switch(index) + { + case m_optEndTurn: return "endTurn"; + case m_optPrevSoldier: return "prevSoldier"; + case m_optNextSoldier: return "nextSoldier"; + case m_optRotateCameraLeft: return "rotCamLeft"; + case m_optRotateCameraRight:return "rotCamRight"; + case m_optCallSkyranger: return "placeEvac"; + case m_optChosenInfo: return "chosenInfo"; + } + } +} + +//============================================================================== +// FLASH FUNCTIONS: +//============================================================================== +simulated function SetNumActiveControls( int numActive ) { + Movie.ActionScriptVoid(MCPath$".SetNumActive"); +} + +simulated function AS_SetIconButton( int index, string label, string hotKey, string iconLabel, bool enabled, bool bIsSpecial) { + Movie.ActionScriptVoid(MCPath$".SetIconButton"); +} + +simulated function AS_SetHoverHelp( string label ) { + Movie.ActionScriptVoid(MCPath$".SetHoverHelp"); +} + + +//============================================================================== +// DEFAULTS: +//============================================================================== + +defaultproperties +{ + MCName="mouseControls"; + + m_clrBad=(R=200,G=0,B=0,A=175) + + // Non-shot hud + m_optEndTurn = 0; + m_optPrevSoldier = 1; + m_optNextSoldier = 2; + m_optRotateCameraLeft = 3; + m_optRotateCameraRight = 4; + m_optCallSkyranger = 5; + m_optChosenInfo = 6; + // During shot + m_optCancelShot = 0; + m_optShotInfo = 1; + + bAnimateOnInit = false; +} diff --git a/X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj b/X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj index 04aa63b9f..9891ea991 100644 --- a/X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj +++ b/X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj @@ -293,6 +293,9 @@ Content + + Content + Content From c0e2e77b723bb2d239228ec9b24b6cc3ad4e038b Mon Sep 17 00:00:00 2001 From: Xymanek Date: Sat, 13 Jul 2019 10:48:52 +0300 Subject: [PATCH 2/5] [WIP] Commander actions API --- .../Classes/UITacticalHUD_AbilityContainer.uc | 4 +- .../Classes/UITacticalHUD_MouseControls.uc | 2 + .../XComGame/Classes/XComCHCommanderAction.uc | 116 ++++++++++++++++++ .../X2WOTCCommunityHighlander.x2proj | 3 + 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc diff --git a/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_AbilityContainer.uc b/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_AbilityContainer.uc index fb1bf0acc..e420842f9 100644 --- a/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_AbilityContainer.uc +++ b/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_AbilityContainer.uc @@ -1053,11 +1053,13 @@ simulated function UpdateAbilitiesArray() if (`ISCONTROLLERACTIVE) { + // TODO UITacticalHUD(screen).UpdateSkyrangerButton(); } else { - UITacticalHUD(screen).m_kMouseControls.SetCommandAbilities(arrCommandAbilities); + //UITacticalHUD(screen).m_kMouseControls.SetCommandAbilities(arrCommandAbilities); + UITacticalHUD(screen).m_kMouseControls.CommanderActions = class'XComCHCommanderAction'.static.ProcessCommanderAbilities(arrCommandAbilities); UITacticalHUD(screen).m_kMouseControls.UpdateControls(); } diff --git a/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_MouseControls.uc b/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_MouseControls.uc index 07483243b..9dd4e0463 100644 --- a/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_MouseControls.uc +++ b/X2WOTCCommunityHighlander/Src/XComGame/Classes/UITacticalHUD_MouseControls.uc @@ -47,6 +47,8 @@ var array CommandAbilities; var UIPanel AttentionPulse; +var array CommanderActions; + //---------------------------------------------------------------------------- // METHODS // diff --git a/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc b/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc new file mode 100644 index 000000000..939501511 --- /dev/null +++ b/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc @@ -0,0 +1,116 @@ +// Such weird name due to dependson fun +class XComCHCommanderAction extends Object dependson(XComLWTuple); + +var name Id; + +var string sIcon; +var string DisplayText; +var string Tooltip; + +var AvailableAction ActionInfo; // Automatically used if OnActivated is none +var array AdditionalData; + +delegate OnActivated (XComCHCommanderAction Action); + +/////////////// +/// Helpers /// +/////////////// + +function XComLWTuple GetAdditionalDataById (name TupleId) +{ + local XComLWTuple Tuple; + + foreach AdditionalData(Tuple) + { + if (Tuple.Id == TupleId) + { + return Tuple; + } + } + + return none; +} + +static function name GetActionNameForAbility (X2AbilityTemplate AbilityTemplate) +{ + return name("Ability_" $ AbilityTemplate.DataName); +} + +//////////////// +/// Creation /// +//////////////// + +static function XComCHCommanderAction CreateFromAvailableAction (AvailableAction InActionInfo) +{ + local XComGameState_Ability AbilityState; + local X2AbilityTemplate AbilityTemplate; + local XComCHCommanderAction Action; + + AbilityState = XComGameState_Ability(`XCOMHISTORY.GetGameStateForObjectID(InActionInfo.AbilityObjectRef.ObjectID)); + if (AbilityState == none) + { + `RedScreen("CHCommanderAction::CreateFromAvailableAction cannot find ability state"); + return none; + } + + AbilityTemplate = AbilityState.GetMyTemplate(); + if (AbilityTemplate == none) + { + `RedScreen("CHCommanderAction::CreateFromAvailableAction cannot find ability template"); + return none; + } + + Action = new class'XComCHCommanderAction'; + + Action.Id = GetActionNameForAbility(AbilityTemplate); + Action.sIcon = AbilityTemplate.IconImage; + Action.DisplayText = Caps(AbilityState.GetMyFriendlyName()); + Action.ActionInfo = InActionInfo; + + return Action; +} + +// Intended to be called from UITacticalHUD_AbilityContainer. Moved here due to dependson issues +static function array ProcessCommanderAbilities (array Abilities) +{ + local array CHActions; + local XComCHCommanderAction CHAction; + local AvailableAction Ability; + local XComLWTValue TupleValue; + local XComLWTuple Tuple; + + foreach Abilities(Ability) + { + CHActions.AddItem(CreateFromAvailableAction(Ability)); + } + + Tuple = new class'XComLWTuple'; + Tuple.Id = 'ModifyCommanderActions'; + + foreach CHActions(CHAction) + { + TupleValue.kind = XComLWTVObject; + TupleValue.o = CHAction; + + Tuple.Data.AddItem(TupleValue); + } + + `XEVENTMGR.TriggerEvent('ModifyCommanderActions', Tuple); + CHActions.Length = 0; + + foreach Tuple.Data(TupleValue) + { + if (TupleValue.kind != XComLWTVObject) continue; + + CHAction = XComCHCommanderAction(TupleValue.o); + if (CHAction == none) + { + `Redscreen("ModifyCommanderActions listener supplied non-CHCommanderAction or none object - skipping"); + continue; + } + + CHActions.AddItem(CHAction); + } + + return CHActions; +} \ No newline at end of file diff --git a/X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj b/X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj index 9891ea991..e5340efea 100644 --- a/X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj +++ b/X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj @@ -437,6 +437,9 @@ Content + + Content + Content From b67cb187c85cd572b8fbae55dd2352e43646794d Mon Sep 17 00:00:00 2001 From: Xymanek Date: Sat, 13 Jul 2019 17:42:26 +0300 Subject: [PATCH 3/5] Add the highlight argument --- .../Src/XComGame/Classes/XComCHCommanderAction.uc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc b/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc index 939501511..75658e4c8 100644 --- a/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc +++ b/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc @@ -6,6 +6,7 @@ var name Id; var string sIcon; var string DisplayText; var string Tooltip; +var bool bHighlight; var AvailableAction ActionInfo; // Automatically used if OnActivated is none var array AdditionalData; @@ -42,6 +43,7 @@ static function name GetActionNameForAbility (X2AbilityTemplate AbilityTemplate) static function XComCHCommanderAction CreateFromAvailableAction (AvailableAction InActionInfo) { + local XComGameState_BattleData BattleData; local XComGameState_Ability AbilityState; local X2AbilityTemplate AbilityTemplate; local XComCHCommanderAction Action; @@ -60,11 +62,13 @@ static function XComCHCommanderAction CreateFromAvailableAction (AvailableAction return none; } + BattleData = XComGameState_BattleData(`XCOMHISTORY.GetSingleGameStateObjectForClass(class'XComGameState_BattleData')); Action = new class'XComCHCommanderAction'; Action.Id = GetActionNameForAbility(AbilityTemplate); Action.sIcon = AbilityTemplate.IconImage; Action.DisplayText = Caps(AbilityState.GetMyFriendlyName()); + Action.bHighlight = BattleData.IsAbilityObjectiveHighlighted(AbilityTemplate); Action.ActionInfo = InActionInfo; return Action; @@ -105,7 +109,7 @@ static function array ProcessCommanderAbilities (array Date: Wed, 31 Jul 2019 13:20:35 +0300 Subject: [PATCH 4/5] Some helpers and tutorial guard --- .../XComGame/Classes/XComCHCommanderAction.uc | 81 +++++++++++++------ 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc b/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc index 75658e4c8..931989cb4 100644 --- a/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc +++ b/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc @@ -8,7 +8,7 @@ var string DisplayText; var string Tooltip; var bool bHighlight; -var AvailableAction ActionInfo; // Automatically used if OnActivated is none +var AvailableAction AbilityInfo; // Automatically used if OnActivated is none var array AdditionalData; delegate OnActivated (XComCHCommanderAction Action); @@ -37,18 +37,48 @@ static function name GetActionNameForAbility (X2AbilityTemplate AbilityTemplate) return name("Ability_" $ AbilityTemplate.DataName); } +function bool IsPlaceEvac () +{ + local X2AbilityTemplate AbilityTemplate; + + AbilityTemplate = GetAbilityTemplate(); + if (AbilityTemplate == none) return false; + + return AbilityTemplate.DataName == 'PlaceEvacZone'; +} + +function bool IsAbility () +{ + return OnActivated == none; +} + +function XComGameState_Ability GetAbilityState () +{ + return XComGameState_Ability(`XCOMHISTORY.GetGameStateForObjectID(AbilityInfo.AbilityObjectRef.ObjectID)); +} + +function X2AbilityTemplate GetAbilityTemplate () +{ + local XComGameState_Ability AbilityState; + + AbilityState = GetAbilityState(); + if (AbilityState == none) return none; + + return AbilityState.GetMyTemplate(); +} + //////////////// /// Creation /// //////////////// -static function XComCHCommanderAction CreateFromAvailableAction (AvailableAction InActionInfo) +static function XComCHCommanderAction CreateFromAvailableAction (AvailableAction InAbilityInfo) { local XComGameState_BattleData BattleData; local XComGameState_Ability AbilityState; local X2AbilityTemplate AbilityTemplate; local XComCHCommanderAction Action; - AbilityState = XComGameState_Ability(`XCOMHISTORY.GetGameStateForObjectID(InActionInfo.AbilityObjectRef.ObjectID)); + AbilityState = XComGameState_Ability(`XCOMHISTORY.GetGameStateForObjectID(InAbilityInfo.AbilityObjectRef.ObjectID)); if (AbilityState == none) { `RedScreen("CHCommanderAction::CreateFromAvailableAction cannot find ability state"); @@ -69,7 +99,7 @@ static function XComCHCommanderAction CreateFromAvailableAction (AvailableAction Action.sIcon = AbilityTemplate.IconImage; Action.DisplayText = Caps(AbilityState.GetMyFriendlyName()); Action.bHighlight = BattleData.IsAbilityObjectiveHighlighted(AbilityTemplate); - Action.ActionInfo = InActionInfo; + Action.AbilityInfo = InAbilityInfo; return Action; } @@ -88,32 +118,37 @@ static function array ProcessCommanderAbilities (array Date: Wed, 31 Jul 2019 13:42:55 +0300 Subject: [PATCH 5/5] Guard against listeners messing with ability-decorating commander actions (eg. evac) --- .../XComGame/Classes/XComCHCommanderAction.uc | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc b/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc index 931989cb4..a4fe171eb 100644 --- a/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc +++ b/X2WOTCCommunityHighlander/Src/XComGame/Classes/XComCHCommanderAction.uc @@ -141,15 +141,66 @@ static function array ProcessCommanderAbilities (array Abilities) +{ + local AvailableAction AbilityInfo; + local bool bFound; + local int i, j; + + foreach Abilities(AbilityInfo, i) + { + if (CHAction.AbilityInfo.AbilityObjectRef.ObjectID == AbilityInfo.AbilityObjectRef.ObjectID) + { + bFound = true; + break; + } + } + + if (!bFound) return false; + + if (CHAction.AbilityInfo.AvailableTargetCurrIndex != AbilityInfo.AvailableTargetCurrIndex) return false; + if (CHAction.AbilityInfo.eAbilityIconBehaviorHUD != AbilityInfo.eAbilityIconBehaviorHUD) return false; + if (CHAction.AbilityInfo.AvailableTargets.Length != AbilityInfo.AvailableTargets.Length) return false; + if (CHAction.AbilityInfo.bInputTriggered != AbilityInfo.bInputTriggered) return false; + if (CHAction.AbilityInfo.AvailableCode != AbilityInfo.AvailableCode) return false; + if (CHAction.AbilityInfo.bFreeAim != AbilityInfo.bFreeAim) return false; + + for (i = 0; i < AbilityInfo.AvailableTargets.Length; i++) + { + if (AbilityInfo.AvailableTargets[i].AdditionalTargets.Length != CHAction.AbilityInfo.AvailableTargets[i].AdditionalTargets.Length) return false; + if (AbilityInfo.AvailableTargets[i].PrimaryTarget != CHAction.AbilityInfo.AvailableTargets[i].PrimaryTarget) return false; + + for (j = 0; j < AbilityInfo.AvailableTargets[i].AdditionalTargets.Length; j++) + { + if (AbilityInfo.AvailableTargets[i].AdditionalTargets[j] != CHAction.AbilityInfo.AvailableTargets[i].AdditionalTargets[j]) return false; + } + } + + // All good + + Abilities.Remove(i, 1); + return true; } \ No newline at end of file