Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions X2WOTCCommunityHighlander/Config/XComGame.ini
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,7 @@ InterruptionsUpdateTurnStartLocation = false ; Should interruptions update tur
InterruptionsResetPanicTestsPerformedThisTurn = false ; Should interruptions reset the tracker for the amount of panic tests performed this turn, just like a normal turn? Default is false, because the unit isn't taking a typical turn.
InterruptionsTriggerGroupTurnBegunEvent = true ; Should interruptions trigger the GroupTurnBegunEvent, just like what happens in a normal turn. Default is true, because interruptions work closely with "groups" (XComGameState_AIGroup) in the code side of things.
; End Issue #1325

; Issue #1591
+SustainAbilityNames=SustainTriggered
bEnableImprovedCanAbilityHit = true
13 changes: 12 additions & 1 deletion X2WOTCCommunityHighlander/Src/XComGame/Classes/CHHelpers.uc
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,17 @@ var config bool bDisableAutomaticBondPhoto;
var config bool bManualPhotoTakenOnLastMission;
// End Issue #1453

// Single line for Issue #1591 - Variable to allow use of CanAbilityHitUnit_CH
// Allows mods access to the ability context to give better control over hit function in subclasses
var config bool bEnableImprovedCanAbilityHit;

// Begin Issue #1591 - Variables to allow abilities to bypass base-game stasis and sustain
var config array<name> AbilitiesToBypassStasis;
var config array<name> AbilitiesToBypassSustain;
// Issue #1591 - Ar
var config array<name> SustainAbilityNames;

// End Issue #1591
// Start Issue #885
enum EHLDelegateReturn
{
Expand Down Expand Up @@ -1168,4 +1179,4 @@ static function bool GeoscapeReadyForUpdate()
StrategyMap != none &&
StrategyMap.m_eUIState != eSMS_Flight &&
StrategyMap.Movie.Pres.ScreenStack.GetCurrentScreen() == StrategyMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,13 @@ function bool RetainIndividualConcealment(XComGameState_Effect EffectState, XCom
function bool DoesEffectAllowUnitToBleedOut(XComGameState_Unit UnitState) { return true; }
function bool DoesEffectAllowUnitToBeLooted(XComGameState NewGameState, XComGameState_Unit UnitState) { return true; }
function bool CanAbilityHitUnit(name AbilityName) { return true; }
// Begin Issue #1591 - Provide a hook to allow over-riding CanAbilityHitUnit in child classes, returning default immunityFn if not over-ridden
// Gives access to abilityname to allow mods to exclude certain abilities
function bool CanAbilityHitUnit_CH(name AbilityName, optional XComGameStateContext_Ability AbilityContext, optional XComGameState_Effect EffectState)
{
return CanAbilityHitUnit(AbilityName);
}
// End Issue #1591
function bool PreDeathCheck(XComGameState NewGameState, XComGameState_Unit UnitState, XComGameState_Effect EffectState) { return false; }
function bool PreBleedoutCheck(XComGameState NewGameState, XComGameState_Unit UnitState, XComGameState_Effect EffectState) { return false; }
function bool ForcesBleedout(XComGameState NewGameState, XComGameState_Unit UnitState, XComGameState_Effect EffectState) { return bEffectForcesBleedout; }
Expand Down
191 changes: 191 additions & 0 deletions X2WOTCCommunityHighlander/Src/XComGame/Classes/X2Effect_Stasis.uc
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
class X2Effect_Stasis extends X2Effect_Persistent
config(GameData_SoldierSkills);

var localized string StasisFlyover, StasisRemoved, StasisRemovedText;
var name StunStartAnim, StunStopAnim;
var bool bSkipFlyover;
var float StartAnimBlendTime;

var config array<name> STASIS_REMOVE_EFFECTS_SOURCE, STASIS_REMOVE_EFFECTS_TARGET;

simulated protected function OnEffectAdded(const out EffectAppliedData ApplyEffectParameters, XComGameState_BaseObject kNewTargetState, XComGameState NewGameState, XComGameState_Effect NewEffectState)
{
local XComGameState_Unit TargetUnit;

TargetUnit = XComGameState_Unit(kNewTargetState);
TargetUnit.bInStasis = true;
TargetUnit.ActionPoints.Length = 0;
TargetUnit.ReserveActionPoints.Length = 0;

`XEVENTMGR.TriggerEvent('AffectedByStasis', kNewTargetState, kNewTargetState);

super.OnEffectAdded(ApplyEffectParameters, kNewTargetState, NewGameState, NewEffectState);
}

simulated function OnEffectRemoved(const out EffectAppliedData ApplyEffectParameters, XComGameState NewGameState, bool bCleansed, XComGameState_Effect RemovedEffectState)
{
local XComGameState_Unit TargetUnit;

TargetUnit = XComGameState_Unit(NewGameState.GetGameStateForObjectID(ApplyEffectParameters.TargetStateObjectRef.ObjectID));
if (TargetUnit == none)
{
TargetUnit = XComGameState_Unit(`XCOMHISTORY.GetGameStateForObjectID(ApplyEffectParameters.TargetStateObjectRef.ObjectID));
`assert(TargetUnit != none);
TargetUnit = XComGameState_Unit(NewGameState.ModifyStateObject(TargetUnit.Class, TargetUnit.ObjectID));
}

TargetUnit.bInStasis = false;

super.OnEffectRemoved(ApplyEffectParameters, NewGameState, bCleansed, RemovedEffectState);
}

function UnitEndedTacticalPlay(XComGameState_Effect EffectState, XComGameState_Unit UnitState)
{
UnitState.bInStasis = false;
}

function ModifyTurnStartActionPoints(XComGameState_Unit UnitState, out array<name> ActionPoints, XComGameState_Effect EffectState)
{
// no actions allowed while in stasis
ActionPoints.Length = 0;
}

function bool ProvidesDamageImmunity(XComGameState_Effect EffectState, name DamageType)
{
return true;
}

function bool CanAbilityHitUnit(name AbilityName)
{
return false;
}
// Issue #1591 - Allow abilities in the CHHelpers array to bypass CanAbilityHit
// And allow checking the name of the effect that applied stasis (e.g. Sustain) so they can be handled seperately
function bool CanAbilityHitUnit_CH(name AbilityName, optional XComGameStateContext_Ability AbilityContext, optional XComGameState_Effect EffectState)
{
`LOG("CanAbilityHitUnit_CH called with AbilityName: " $ AbilityName,, 'BDLOG');

if (class'CHHelpers'.default.AbilitiesToBypassStasis.Find(AbilityName) != INDEX_NONE)
{
`LOG("Found in AbilitiesToBypassStasis - returning true",, 'BDLOG');
return true;
}

if (EffectState != none)
{
`LOG("EffectState AbilityTemplateName: " $ EffectState.ApplyEffectParameters.AbilityInputContext.AbilityTemplateName,, 'BDLOG');
`LOG("SustainAbilityNames length: " $ class'CHHelpers'.default.SustainAbilityNames.Length,, 'BDLOG');
`LOG("AbilitiesToBypassSustain length: " $ class'CHHelpers'.default.AbilitiesToBypassSustain.Length,, 'BDLOG');

if (class'CHHelpers'.default.SustainAbilityNames.Find(EffectState.ApplyEffectParameters.AbilityInputContext.AbilityTemplateName) != INDEX_NONE
&& class'CHHelpers'.default.AbilitiesToBypassSustain.Find(AbilityName) != INDEX_NONE)
{
`LOG("Found in SustainAbilityNames and AbilitiesToBypassSustain - returning true",, 'BDLOG');
return true;
}
}
else
{
`LOG("EffectState is none",, 'BDLOG');
}

`LOG("CanAbilityHitUnit_CH returning false",, 'BDLOG');
return false;
}
simulated function AddX2ActionsForVisualization(XComGameState VisualizeGameState, out VisualizationActionMetadata ActionMetadata, name EffectApplyResult)
{
// Empty because we will be adding all this at the end with ModifyTracksVisualization
}

simulated function ModifyTracksVisualization(XComGameState VisualizeGameState, out VisualizationActionMetadata ModifyTrack, const name EffectApplyResult)
{
local X2Action_PlaySoundAndFlyOver SoundAndFlyOver;
local X2Action_PlayAnimation PlayAnimation;

if (EffectApplyResult == 'AA_Success' && ModifyTrack.StateObject_NewState.IsA('XComGameState_Unit'))
{
if (!bSkipFlyover)
{
SoundAndFlyOver = X2Action_PlaySoundAndFlyOver(class'X2Action_PlaySoundAndFlyOver'.static.AddToVisualizationTree(ModifyTrack, VisualizeGameState.GetContext()));
SoundAndFlyOver.SetSoundAndFlyOverParameters(None, default.StasisFlyover, '', eColor_Bad, class'UIUtilities_Image'.const.UnitStatus_Stunned, 1.0, true);
}

if( XComGameState_Unit(ModifyTrack.StateObject_NewState).IsTurret() )
{
class'X2Action_UpdateTurretAnim'.static.AddToVisualizationTree(ModifyTrack, VisualizeGameState.GetContext());
}
else
{
// Not a turret
// Play the start stun animation
PlayAnimation = X2Action_PlayAnimation(class'X2Action_PlayAnimation'.static.AddToVisualizationTree(ModifyTrack, VisualizeGameState.GetContext()));
PlayAnimation.Params.AnimName = StunStartAnim;
PlayAnimation.Params.BlendTime = StartAnimBlendTime;
}
class'X2StatusEffects'.static.UpdateUnitFlag(ModifyTrack, VisualizeGameState.GetContext());

super.AddX2ActionsForVisualization(VisualizeGameState, ModifyTrack, EffectApplyResult);
}
}

simulated function AddX2ActionsForVisualization_Sync( XComGameState VisualizeGameState, out VisualizationActionMetadata ActionMetadata )
{
//We assume 'AA_Success', because otherwise the effect wouldn't be here (on load) to get sync'd
ModifyTracksVisualization(VisualizeGameState, ActionMetadata, 'AA_Success');
}

simulated function AddX2ActionsForVisualization_Removed(XComGameState VisualizeGameState, out VisualizationActionMetadata ActionMetadata, const name EffectApplyResult, XComGameState_Effect RemovedEffect)
{
local X2Action_PlayAnimation PlayAnimation;

super.AddX2ActionsForVisualization_Removed(VisualizeGameState, ActionMetadata, EffectApplyResult, RemovedEffect);

if (XComGameState_Unit(ActionMetadata.StateObject_NewState) != none)
{
if( XComGameState_Unit(ActionMetadata.StateObject_NewState).IsTurret() )
{
class'X2Action_UpdateTurretAnim'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(), false, ActionMetadata.LastActionAdded);
}
else
{
// The unit is not a turret
PlayAnimation = X2Action_PlayAnimation(class'X2Action_PlayAnimation'.static.AddToVisualizationTree(ActionMetadata, VisualizeGameState.GetContext(), false, ActionMetadata.LastActionAdded));
PlayAnimation.Params.AnimName = StunStopAnim;
}
class'X2StatusEffects'.static.UpdateUnitFlag(ActionMetadata, VisualizeGameState.GetContext());

class'X2StatusEffects'.static.AddEffectSoundAndFlyOverToTrack(ActionMetadata, VisualizeGameState.GetContext(), default.StasisRemoved, '', eColor_Good, class'UIUtilities_Image'.const.UnitStatus_Stunned, 2.0f);
class'X2StatusEffects'.static.AddEffectMessageToTrack(
ActionMetadata,
default.StasisRemovedText,
VisualizeGameState.GetContext(),
class'UIEventNoticesTactical'.default.StasisTitle,
"img:///UILibrary_PerkIcons.UIPerk_stasis",
eUIState_Good);
}
}

function RegisterForEvents(XComGameState_Effect EffectGameState)
{
local XComGameState_Unit UnitState;
local X2EventManager EventMan;
local Object EffectObj;

UnitState = XComGameState_Unit(`XCOMHISTORY.GetGameStateForObjectID(EffectGameState.ApplyEffectParameters.TargetStateObjectRef.ObjectID));
EventMan = `XEVENTMGR;

EffectObj = EffectGameState;
EventMan.RegisterForEvent(EffectObj, 'AffectedByStasis', class'XComGameState_Effect'.static.AffectedByStasis_Listener, ELD_OnStateSubmitted, , UnitState);
}

DefaultProperties
{
EffectName = "Stasis"
DuplicateResponse = eDupe_Refresh
CustomIdleOverrideAnim="HL_StunnedIdle"
StunStartAnim="HL_StunnedStart"
StunStopAnim="HL_StunnedStop"
ModifyTracksFn=ModifyTracksVisualization
EffectHierarchyValue=950
StartAnimBlendTime=0.1f
}
Original file line number Diff line number Diff line change
Expand Up @@ -1198,10 +1198,24 @@ static function CheckTargetForHitModification(out AvailableTarget kTarget, XComG
{
bIsResultHit = ModifyContext.IsResultContextHit();

if( bIsResultHit && !TargetUnitState.CanAbilityHitUnit(AbilityTemplate.DataName) )
if (bIsResultHit)
{
ModifyContext.ResultContext.HitResult = eHit_Miss;
`COMBATLOG("Effect on Target is forcing a miss against" @ TargetUnitState.GetName(eNameType_RankFull));
if (class'CHHelpers'.default.bEnableImprovedCanAbilityHit)
{
if (!TargetUnitState.CanAbilityHitUnit_CH(AbilityTemplate.DataName, ModifyContext))
{
ModifyContext.ResultContext.HitResult = eHit_Miss;
`COMBATLOG("Effect on Target is forcing a miss against" @ TargetUnitState.GetName(eNameType_RankFull));
}
}
else
{
if (!TargetUnitState.CanAbilityHitUnit(AbilityTemplate.DataName))
{
ModifyContext.ResultContext.HitResult = eHit_Miss;
`COMBATLOG("Effect on Target is forcing a miss against" @ TargetUnitState.GetName(eNameType_RankFull));
}
}
}

if (AbilityTemplate.AbilityToHitOwnerOnMissCalc != None
Expand Down Expand Up @@ -1268,10 +1282,24 @@ static function CheckTargetForHitModification(out AvailableTarget kTarget, XComG
{
bIsResultHit = ModifyContext.IsResultContextMultiHit(MultiIndex);

if( bIsResultHit && !TargetUnitState.CanAbilityHitUnit(AbilityTemplate.DataName) )
if (bIsResultHit)
{
ModifyContext.ResultContext.MultiTargetHitResults[MultiIndex] = eHit_Miss;
`COMBATLOG("Effect on MultiTarget is forcing a miss against" @ TargetUnitState.GetName(eNameType_RankFull));
if (class'CHHelpers'.default.bEnableImprovedCanAbilityHit)
{
if (!TargetUnitState.CanAbilityHitUnit_CH(AbilityTemplate.DataName, ModifyContext))
{
ModifyContext.ResultContext.MultiTargetHitResults[MultiIndex] = eHit_Miss;
`COMBATLOG("Effect on MultiTarget is forcing a miss against" @ TargetUnitState.GetName(eNameType_RankFull));
}
}
else
{
if (!TargetUnitState.CanAbilityHitUnit(AbilityTemplate.DataName))
{
ModifyContext.ResultContext.MultiTargetHitResults[MultiIndex] = eHit_Miss;
`COMBATLOG("Effect on MultiTarget is forcing a miss against" @ TargetUnitState.GetName(eNameType_RankFull));
}
}
}

if ( AbilityTemplate.Hostility == eHostility_Offensive )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15266,6 +15266,35 @@ function bool CanAbilityHitUnit(name AbilityName)
return bCanHit;
}

function bool CanAbilityHitUnit_CH(name AbilityName, optional XComGameStateContext_Ability AbilityContext)
{
local StateObjectReference EffectRef;
local XComGameState_Effect EffectState;
local XComGameStateHistory History;
local X2Effect_Persistent Effect;
local bool bCanHit;

History = `XCOMHISTORY;
bCanHit = true;

foreach AffectedByEffects(EffectRef)
{
EffectState = XComGameState_Effect(History.GetGameStateForObjectID(EffectRef.ObjectID));
if (EffectState != none)
{
Effect = EffectState.GetX2Effect();
if (Effect != none)
{
bCanHit = bCanHit && Effect.CanAbilityHitUnit_CH(AbilityName, AbilityContext, EffectState);
if (!bCanHit)
break;
}
}
}

return bCanHit;
}

// Checks to see if all Codex that originated from an original Codex are dead
event bool AreAllCodexInLineageDead(XComGameState NewGameState/*, XComGameState_Unit UnitState*/)
{
Expand Down
3 changes: 3 additions & 0 deletions X2WOTCCommunityHighlander/X2WOTCCommunityHighlander.x2proj
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,9 @@
<Content Include="Src\XComGame\Classes\X2Effect_SpawnDestructible.uc">
<SubType>Content</SubType>
</Content>
<Content Include="Src\XComGame\Classes\X2Effect_Stasis.uc">
<SubType>Content</SubType>
</Content>
<Content Include="Src\XComGame\Classes\X2Effect_Sustain.uc">
<SubType>Content</SubType>
</Content>
Expand Down
Loading