Kregano Posted August 15, 2017 Share Posted August 15, 2017 I'm trying to update an older mod (AOE suppression with cone targeting) with a special reaction shot. This reaction shot is intended to have a few special features:100% chance to hit (got this mostly working). Reduced damage. Applies a debuff (TBD). All enemies within 3 tiles of the target get hit with the debuff.So far, I've only gotten the 100% chance to hit (barring obstacles in the way) to work. For some reason, the debuff doesn't apply ever, the AOE doesn't kick in, and sometimes the damage reduction will break the aim calculations and make the shots miss. I've asked for help on Reddit, and aside from the 200% HitMod thing, they've been of little to no help. I'm hoping the Nexus forums might be more helpful. Here's my current code iteration, trying to use Walk Fire as a guide for the damage reduction: //--------------------------------------------------------------------------------------- // FILE: X2Ability_ConeOfFire.uc // AUTHOR: Kregano // DATE: // PURPOSE: Defines Cone of Fire ability. // //--------------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------------- class X2Ability_ConeOfFireStuff extends X2Ability_GrenadierAbilitySet config(GameData_SoldierSkills) dependson (XComGameStateContext_Ability); static function array<X2DataTemplate> CreateTemplates() { local array<X2DataTemplate> Templates; Templates.AddItem(ConeOfFire()); Templates.AddItem(COF_ReactionShot()); return Templates; } static function X2AbilityTemplate ConeOfFire() { local X2AbilityTemplate Template; local X2AbilityCost_ActionPoints ActionPointCost; local X2AbilityCost_Ammo AmmoCost; local X2AbilityCooldown Cooldown; local X2Effect_ReserveActionPoints ReserveActionPointsEffect; local X2AbilityMultiTarget_Cone ConeMultiTarget; local X2Condition_UnitProperty UnitPropertyCondition; local X2Effect_Suppression SuppressionEffect; `CREATE_X2ABILITY_TEMPLATE(Template, 'ConeOfFire'); Template.IconImage = "img:///UILibrary_ConeOfFire.UIPerk_coneoffire"; ActionPointCost = new class'X2AbilityCost_ActionPoints'; ActionPointCost.bConsumeAllPoints = true; // this will guarantee the unit has at least 1 action point ActionPointCost.bFreeCost = true; // ReserveActionPoints effect will take all action points away Template.AbilityCosts.AddItem(ActionPointCost); AmmoCost = new class'X2AbilityCost_Ammo'; AmmoCost.iAmmo = 2; Template.AbilityCosts.AddItem(AmmoCost); Cooldown = new class'X2AbilityCooldown'; Cooldown.iNumTurns = 1; Template.AbilityCooldown = Cooldown; Template.AbilityShooterConditions.AddItem(default.LivingShooterProperty); Template.AddShooterEffectExclusions(); ReserveActionPointsEffect = new class'X2Effect_ReserveActionPoints'; ReserveActionPointsEffect.ReserveType = 'Suppression'; Template.AddShooterEffect(ReserveActionPointsEffect); Template.AbilityToHitCalc = default.DeadEye; Template.AbilityTargetConditions.AddItem(default.LivingHostileUnitDisallowMindControlProperty); Template.AbilityTargetConditions.AddItem(default.GameplayVisibilityCondition); Template.AbilityTriggers.AddItem(default.PlayerInputTrigger); Template.AbilityTargetStyle = new class'X2AbilityTarget_Cursor'; ConeMultiTarget.bIgnoreBlockingCover = true; ConeMultiTarget = new class'X2AbilityMultiTarget_Cone'; ConeMultiTarget.bUseWeaponRadius = true; ConeMultiTarget.ConeEndDiameter = 12 * class'XComWorldData'.const.WORLD_StepSize; ConeMultiTarget.bUseWeaponRangeForLength = true; Template.AbilityMultiTargetStyle = ConeMultiTarget; UnitPropertyCondition = new class'X2Condition_UnitProperty'; UnitPropertyCondition.ExcludeDead = true; UnitPropertyCondition.ExcludeFriendlyToSource = true; Template.AbilityShooterConditions.AddItem(UnitPropertyCondition); Template.AbilityTargetConditions.AddItem(UnitPropertyCondition); SuppressionEffect = new class'X2Effect_Suppression'; SuppressionEffect.BuildPersistentEffect(1, false, true, false, eGameRule_PlayerTurnBegin); SuppressionEffect.bRemoveWhenTargetDies = true; SuppressionEffect.bRemoveWhenSourceDamaged = true; SuppressionEffect.bBringRemoveVisualizationForward = true; SuppressionEffect.SetDisplayInfo(ePerkBuff_Penalty, Template.LocFriendlyName, class'X2Ability_GrenadierAbilitySet'.default.SuppressionTargetEffectDesc, Template.IconImage); SuppressionEffect.SetSourceDisplayInfo(ePerkBuff_Bonus, Template.LocFriendlyName, class'X2Ability_GrenadierAbilitySet'.default.SuppressionSourceEffectDesc, Template.IconImage); Template.AddMultiTargetEffect(SuppressionEffect); Template.AddTargetEffect(class'X2Ability_GrenadierAbilitySet'.static.HoloTargetEffect()); Template.bAllowAmmoEffects = true; Template.ShotHUDPriority = class'UIUtilities_Tactical'.const.CLASS_LIEUTENANT_PRIORITY; Template.AbilitySourceName = 'eAbilitySource_Perk'; Template.eAbilityIconBehaviorHUD = eAbilityIconBehavior_AlwaysShow; Template.bDisplayInUITooltip = false; Template.AdditionalAbilities.AddItem('COF_ReactionShot'); Template.bIsASuppressionEffect = true; Template.AbilityConfirmSound = "TacticalUI_ActivateAbility"; Template.AssociatedPassives.AddItem('HoloTargeting'); Template.CinescriptCameraType = "StandardSuppression"; Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; Template.BuildVisualizationFn = ConeOfFireBuildVisualization; Template.BuildAppliedVisualizationSyncFn = class'X2Ability_GrenadierAbilitySet'.static.SuppressionBuildVisualizationSync; Template.TargetingMethod = class'X2TargetingMethod_Cone'; Template.bCrossClassEligible = true; return Template; } simulated function ConeOfFireBuildVisualization(XComGameState VisualizeGameState, out array<VisualizationTrack> OutVisualizationTracks) { local XComGameStateHistory History; local XComGameStateContext_Ability Context; local StateObjectReference InteractingUnitRef; local VisualizationTrack EmptyTrack; local VisualizationTrack BuildTrack; local int i; local XComGameState_Ability Ability; local X2Action_PlaySoundAndFlyOver SoundAndFlyOver; local XComUnitPawn UnitPawn; local XComWeapon Weapon; History = `XCOMHISTORY; Context = XComGameStateContext_Ability(VisualizeGameState.GetContext()); InteractingUnitRef = Context.InputContext.SourceObject; //Configure the visualization track for the shooter //**************************************************************************************** BuildTrack = EmptyTrack; BuildTrack.StateObject_OldState = History.GetGameStateForObjectID(InteractingUnitRef.ObjectID, eReturnType_Reference, VisualizeGameState.HistoryIndex - 1); BuildTrack.StateObject_NewState = VisualizeGameState.GetGameStateForObjectID(InteractingUnitRef.ObjectID); BuildTrack.TrackActor = History.GetVisualizer(InteractingUnitRef.ObjectID); // Check the actor's pawn and weapon, see if they can play the suppression effect UnitPawn = XGUnit(BuildTrack.TrackActor).GetPawn(); Weapon = XComWeapon(UnitPawn.Weapon); if (Weapon != None && !UnitPawn.GetAnimTreeController().CanPlayAnimation(Weapon.WeaponSuppressionFireAnimSequenceName) && !UnitPawn.GetAnimTreeController().CanPlayAnimation(class'XComWeapon'.default.WeaponSuppressionFireAnimSequenceName)) { // The unit can't play their weapon's suppression effect. Replace it with the normal fire effect so at least they'll look like they're shooting Weapon.WeaponSuppressionFireAnimSequenceName = Weapon.WeaponFireAnimSequenceName; } class'X2Action_ExitCover'.static.AddToVisualizationTrack(BuildTrack, Context); class'X2Action_StartSuppression'.static.AddToVisualizationTrack(BuildTrack, Context); OutVisualizationTracks.AddItem(BuildTrack); //**************************************************************************************** //Configure the visualization track for the targets for (i = 0; i < Context.InputContext.MultiTargets.Length; i++) { // Fake it out by assigning the first multi-target as the primary target if (Context.InputContext.PrimaryTarget.ObjectID == 0) Context.InputContext.PrimaryTarget = Context.InputContext.MultiTargets[i]; InteractingUnitRef = Context.InputContext.MultiTargets[i]; Ability = XComGameState_Ability(History.GetGameStateForObjectID(Context.InputContext.AbilityRef.ObjectID, eReturnType_Reference, VisualizeGameState.HistoryIndex - 1)); BuildTrack = EmptyTrack; BuildTrack.StateObject_OldState = History.GetGameStateForObjectID(InteractingUnitRef.ObjectID, eReturnType_Reference, VisualizeGameState.HistoryIndex - 1); BuildTrack.StateObject_NewState = VisualizeGameState.GetGameStateForObjectID(InteractingUnitRef.ObjectID); BuildTrack.TrackActor = History.GetVisualizer(InteractingUnitRef.ObjectID); SoundAndFlyOver = X2Action_PlaySoundAndFlyOver(class'X2Action_PlaySoundAndFlyOver'.static.AddToVisualizationTrack(BuildTrack, Context)); SoundAndFlyOver.SetSoundAndFlyOverParameters(None, Ability.GetMyTemplate().LocFlyOverText, '', eColor_Bad); if (XComGameState_Unit(BuildTrack.StateObject_OldState).ReserveActionPoints.Length != 0 && XComGameState_Unit(BuildTrack.StateObject_NewState).ReserveActionPoints.Length == 0) { SoundAndFlyOver = X2Action_PlaySoundAndFlyOver(class'X2Action_PlaySoundAndFlyOver'.static.AddToVisualizationTrack(BuildTrack, Context)); SoundAndFlyOver.SetSoundAndFlyOverParameters(none, class'XLocalizedData'.default.OverwatchRemovedMsg, '', eColor_Bad); } OutVisualizationTracks.AddItem(BuildTrack); } } static function X2AbilityTemplate COF_ReactionShot() { local X2AbilityTemplate Template; local X2AbilityCost_ReserveActionPoints ReserveActionPointCost; local X2AbilityToHitCalc_StandardAim ToHitCalc; local X2Condition_Visibility TargetVisibilityCondition; local X2AbilityTrigger_Event Trigger; local X2Condition_UnitEffectsWithAbilitySource TargetEffectCondition; local X2Effect_RemoveEffects RemoveSuppression; local X2Effect ShotEffect; local X2AbilityCost_Ammo AmmoCost; local X2Effect_ReactionShot DamageEffect; local X2Effect_Stunned StunnedEffect; local X2AbilityMultiTarget_Radius RadiusMultiTarget; `CREATE_X2ABILITY_TEMPLATE(Template, 'COF_ReactionShot'); Template.bDontDisplayInAbilitySummary = true; ReserveActionPointCost = new class'X2AbilityCost_ReserveActionPoints'; ReserveActionPointCost.iNumPoints = 1; ReserveActionPointCost.AllowedTypes.AddItem('Suppression'); Template.AbilityCosts.AddItem(ReserveActionPointCost); AmmoCost = new class'X2AbilityCost_Ammo'; AmmoCost.iAmmo = 1; Template.AbilityCosts.AddItem(AmmoCost); ToHitCalc = new class'X2AbilityToHitCalc_StandardAim'; ToHitCalc.BuiltInHitMod = 200; ToHitCalc.bAllowCrit = false; Template.AbilityToHitCalc = ToHitCalc; Template.AbilityToHitOwnerOnMissCalc = ToHitCalc; Template.AbilityTargetStyle = default.SimpleSingleTarget; Template.AbilityTargetConditions.AddItem(default.LivingHostileTargetProperty); TargetEffectCondition = new class'X2Condition_UnitEffectsWithAbilitySource'; TargetEffectCondition.AddRequireEffect(class'X2Effect_Suppression'.default.EffectName, 'AA_UnitIsNotSuppressed'); Template.AbilityTargetConditions.AddItem(TargetEffectCondition); TargetVisibilityCondition = new class'X2Condition_Visibility'; TargetVisibilityCondition.bRequireGameplayVisible = true; Template.AbilityTargetConditions.AddItem(TargetVisibilityCondition); Template.AbilityShooterConditions.AddItem(default.LivingShooterProperty); Template.bAllowAmmoEffects = true; RadiusMultiTarget = new class'X2AbilityMultiTarget_Radius'; RadiusMultiTarget.fTargetRadius = 4.5; // Units are measured in meters and each tile is 1.5 meters RadiusMultiTarget.bIgnoreBlockingCover = true; Template.AbilityMultiTargetStyle = RadiusMultiTarget; DamageEffect = new class'X2Effect_ReactionShot'; DamageEffect.BuildPersistentEffect(1, true, false, false); DamageEffect.SetDisplayInfo(ePerkBuff_Passive, Template.LocFriendlyName, Template.GetMyLongDescription(), Template.IconImage, true,,Template.AbilitySourceName); Template.AddTargetEffect(DamageEffect); StunnedEffect = class'X2StatusEffects'.static.CreateStunnedStatusEffect(2, 100); StunnedEffect.bRemoveWhenSourceDies = false; Template.AddTargetEffect(StunnedEffect); RemoveSuppression = new class'X2Effect_RemoveEffects'; RemoveSuppression.EffectNamesToRemove.AddItem(class'X2Effect_Suppression'.default.EffectName); RemoveSuppression.bCheckSource = true; RemoveSuppression.SetupEffectOnShotContextResult(true, true); Template.AddShooterEffect(RemoveSuppression); //Trigger on movement - interrupt the move Trigger = new class'X2AbilityTrigger_Event'; Trigger.EventObserverClass = class'X2TacticalGameRuleset_MovementObserver'; Trigger.MethodName = 'InterruptGameState'; Template.AbilityTriggers.AddItem(Trigger); Template.AbilitySourceName = 'eAbilitySource_Standard'; Template.eAbilityIconBehaviorHUD = EAbilityIconBehavior_NeverShow; Template.IconImage = "img:///UILibrary_PerkIcons.UIPerk_supression"; Template.ShotHUDPriority = class'UIUtilities_Tactical'.const.CLASS_LIEUTENANT_PRIORITY; Template.bDisplayInUITooltip = false; Template.bDisplayInUITacticalText = false; //don't want to exit cover, we are already in suppression/alert mode. Template.bSkipExitCoverWhenFiring = true; Template.bAllowFreeFireWeaponUpgrade = true; // Put holo target effect first because if the target dies from this shot, it will be too late to notify the effect. ShotEffect = class'X2Ability_GrenadierAbilitySet'.static.HoloTargetEffect(); ShotEffect.TargetConditions.AddItem(class'X2Ability_DefaultAbilitySet'.static.OverwatchTargetEffectsCondition()); Template.AddTargetEffect(ShotEffect); // Various Soldier ability specific effects - effects check for the ability before applying ShotEffect = class'X2Ability_GrenadierAbilitySet'.static.ShredderDamageEffect(); ShotEffect.TargetConditions.AddItem(class'X2Ability_DefaultAbilitySet'.static.OverwatchTargetEffectsCondition()); Template.AddTargetEffect(ShotEffect); Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; Template.BuildVisualizationFn = TypicalAbility_BuildVisualization; return Template; } ===X2Effect_ReactionShot=== class X2Effect_ReactionShot extends X2Effect_Persistent config(WOTCskillTweaks); var config float REAC_DAMAGE_MODIFIER; function int GetAttackingDamageModifier(XComGameState_Effect EffectState, XComGameState_Unit Attacker, Damageable TargetDamageable, XComGameState_Ability AbilityState, const out EffectAppliedData AppliedData, const int CurrentDamage, optional XComGameState NewGameState) { local float ExtraDamage; if(AbilityState.GetMyTemplateName() == 'COF_ReactionShot') { if (class'XComGameStateContext_Ability'.static.IsHitResultHit(AppliedData.AbilityResultContext.HitResult)) { ExtraDamage = -1 * (float(CurrentDamage) * default.REAC_DAMAGE_MODIFIER); } } return int(ExtraDamage); } Link to comment Share on other sites More sharing options...
Recommended Posts