Shuma22 Posted October 26, 2016 Share Posted October 26, 2016 So on today's business I'm trying to do 2 things related to abilities and items, i've looked at pretty much all the tutorials i have been able to find about abilities but i haven't managed to quite get things to work, the first is i'm trying to create a new weapon, nothing too fancy but right now it's supposed to be a rifle that marks targets when it hits them. At the moment it's mostly working, it's a new weapon with an ability that marks, consumes ammo and can miss, the problem is that at the moment it doesn't deal damage. What i've done is i have basically replaced the standard shot ability in the weapon with my ability, which is basically the Mark ability with some bits and pieces of code i copy pasted from the standard shot ability to get it to function like a normal shot. This is what i have right now, it's probably a mess there's a lot of commented lines i have there just for reference when i was trying to get it to work and probably some useless junk there, but this is it: // This is an Unreal Script //--------------------------------------------------------------------------------------- // Copyright (c) 2016 Firaxis Games, Inc. All rights reserved. //--------------------------------------------------------------------------------------- class X2Ability_HunterShot2 extends X2Ability config(GameData_AdventScoutAbility); var config int MARK_TARGET_LOCAL_COOLDOWN; var config int HUNTER_MARK_DAMAGE; var localized string TargetGettingMarkedFriendlyName; var localized string TargetGettingMarkedFriendlyDesc; static function array<X2DataTemplate> CreateTemplates() { local array<X2DataTemplate> Templates; Templates.AddItem(CreateHunterShot2()); return Templates; } static function X2DataTemplate CreateHunterShot2() { local X2AbilityTemplate Template; local X2AbilityCost_ActionPoints ActionPointCost; local X2Condition_UnitProperty UnitPropertyCondition; local X2Condition_Visibility TargetVisibilityCondition; local X2Condition_UnitEffects UnitEffectsCondition; local X2AbilityTarget_Single SingleTarget; local X2AbilityTrigger_PlayerInput InputTrigger; local X2Effect_Persistent MarkedEffect; local X2AbilityCooldown_LocalAndGlobal Cooldown; local X2AbilityCost_Ammo AmmoCost; //local X2Effect_ApplyWeaponDamage WeaponDamageEffect; //local X2Effect_ApplyWeaponDamage DamageEffect; `CREATE_X2ABILITY_TEMPLATE(Template, 'HunterShot2'); Template.IconImage = "img:///UILibrary_PerkIcons.UIPerk_advent_marktarget"; Template.eAbilityIconBehaviorHUD = eAbilityIconBehavior_AlwaysShow; Template.AbilitySourceName = 'eAbilitySource_Standard'; Template.Hostility = eHostility_Offensive; Cooldown = new class'X2AbilityCooldown_LocalAndGlobal'; Cooldown.iNumTurns = default.MARK_TARGET_LOCAL_COOLDOWN; Template.AbilityCooldown = Cooldown; //Action Point Cost ActionPointCost = new class'X2AbilityCost_ActionPoints'; ActionPointCost.iNumPoints = 1; ActionPointCost.bConsumeAllPoints = true; ActionPointCost.bFreeCost = false; Template.AbilityCosts.AddItem(ActionPointCost); //Ammo AmmoCost = new class'X2AbilityCost_Ammo'; AmmoCost.iAmmo = 1; Template.AbilityCosts.AddItem(AmmoCost); Template.bAllowAmmoEffects = true; // The shooter cannot be mind controlled UnitEffectsCondition = new class'X2Condition_UnitEffects'; UnitEffectsCondition.AddExcludeEffect(class'X2Effect_MindControl'.default.EffectName, 'AA_UnitIsMindControlled'); Template.AbilityShooterConditions.AddItem(UnitEffectsCondition); Template.AbilityShooterConditions.AddItem(default.LivingShooterProperty); // Target must be an enemy UnitPropertyCondition = new class'X2Condition_UnitProperty'; UnitPropertyCondition.ExcludeDead = true; UnitPropertyCondition.ExcludeFriendlyToSource = true; UnitPropertyCondition.RequireWithinRange = false; Template.AbilityTargetConditions.AddItem(UnitPropertyCondition); // Target must be visible and may use squad sight TargetVisibilityCondition = new class'X2Condition_Visibility'; TargetVisibilityCondition.bRequireGameplayVisible = true; TargetVisibilityCondition.bAllowSquadsight = true; Template.AbilityTargetConditions.AddItem(TargetVisibilityCondition); // Target cannot already be marked UnitEffectsCondition = new class'X2Condition_UnitEffects'; UnitEffectsCondition.AddExcludeEffect(class'X2StatusEffects'.default.MarkedName, 'AA_UnitIsMarked'); Template.AbilityTargetConditions.AddItem(UnitEffectsCondition); // 100% chance to hit //Template.AbilityToHitCalc = default.DeadEye; Template.AbilityToHitCalc = default.SimpleStandardAim; Template.AbilityToHitOwnerOnMissCalc = default.SimpleStandardAim; SingleTarget = new class'X2AbilityTarget_Single'; Template.AbilityTargetStyle = SingleTarget; InputTrigger = new class'X2AbilityTrigger_PlayerInput'; Template.AbilityTriggers.AddItem(InputTrigger); // Targeting Method Template.TargetingMethod = class'X2TargetingMethod_OverTheShoulder'; Template.bUsesFiringCamera = true; Template.CinescriptCameraType = "StandardGunFiring"; // Damage Effect Template.AddTargetEffect(default.WeaponUpgradeMissDamage); //Weapon Damage Effect //WeaponDamageEffect = new class'X2Effect_ApplyWeaponDamage'; //WeaponDamageEffect.bIgnoreBaseDamage = true; //DamageEffect.EffectDamageValue.DamageType = 'Projectile_MagAdvent'; //Template.AddTargetEffect(WeaponDamageEffect); //Template.BaseDamage = default.HUNTER_MARK_DAMAGE; //DamageEffect.EffectDamageValue = default.HUNTER_MARK_DAMAGE; //DamageEffect.DamageEffect = class'X2Item_AdventScout_Weapons'.default.ADVTROOPERM1_WPN_BASEDAMAGE; //DamageEffect = new class'X2Effect_ApplyWeaponDamage'; //DamageEffect.EnvironmentalDamageAmount = default.WRATH_CANNON_ENVIRONMENT_DAMAGE_AMOUNT; //DamageEffect.bExplosiveDamage = true; //Template.AddMultiTargetEffect(DamageEffect); //Template.AddMultiTargetEffect(new class'X2Effect_ApplyFireToWorld'); //PhysicalDamageEffect = new class'X2Effect_ApplyWeaponDamage'; //PhysicalDamageEffect.EffectDamageValue = class'X2Item_DefaultWeapons'.default.ANDROMEDONROBOT_MELEEATTACK_BASEDAMAGE; //PhysicalDamageEffect.EffectDamageValue.DamageType = 'Melee'; // This also deals environmental damage //PhysicalDamageEffect.EnvironmentalDamageAmount = default.BIG_DAMN_PUNCH_ENVIRONMENT_DAMAGE_AMOUNT; //Template.AddTargetEffect(PhysicalDamageEffect); // Create the Marked effect MarkedEffect = class'X2StatusEffects'.static.CreateMarkedEffect(2, false); //Template.AddTargetEffect(MarkedEffect); Template.AddTargetEffect(MarkedEffect); //BMU - changing to an immediate execution for evaluation //Template.CustomFireAnim = 'HL_SignalPoint'; Template.CustomFireAnim = 'FF_FireMag'; //Make it live //Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; //Template.BuildInterruptGameStateFn = TypicalAbility_BuildInterruptGameState; //Template.BuildVisualizationFn = TargetGettingMarked_BuildVisualization; // MAKE IT LIVE! Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; Template.BuildVisualizationFn = TypicalAbility_BuildVisualization; Template.BuildInterruptGameStateFn = TypicalAbility_BuildInterruptGameState; //Cinematic shot? //Template.CinescriptCameraType = "Mark_Target"; return Template; } Like i said, the ability mostly works, i just don't know how to get it to deal damage. The second thing i'm trying to do is create a "stun proximity mine" but i'm having 2 issues, the first one is that i cannot even get it to show for my aliens to use, and the second is that i don't really know how to add effects to something. The stun proximity mine code i have right now is just the proximity mine template but with a couple of lines added that i took from the EMP grenade, right now it looks like this: class X2Item_AdventScout_Grenade extends X2Item config(GameData_WeaponData); var config WeaponDamageValue PROXIMITYMINE_BASEDAMAGE; var config int PROXIMITYMINE_RANGE; var config int PROXIMITYMINE_RADIUS; static function array<X2DataTemplate> CreateTemplates() { local array<X2DataTemplate> Grenades; Grenades.AddItem(ProximityStunMine()); return Grenades; } static function X2GrenadeTemplate ProximityStunMine() { local X2GrenadeTemplate Template; local ArtifactCost Resources; local X2Effect_Stunned StunnedEffect; `CREATE_X2TEMPLATE(class'X2GrenadeTemplate', Template, 'ProximityStunMine'); Template.strImage = "img:///UILibrary_StrategyImages.X2InventoryIcons.Inv_Proximity_Mine"; Template.EquipSound = "StrategyUI_Grenade_Equip"; Template.AddAbilityIconOverride('ThrowGrenade', "img:///UILibrary_PerkIcons.UIPerk_grenade_proximitymine"); Template.AddAbilityIconOverride('LaunchGrenade', "img:///UILibrary_PerkIcons.UIPerk_grenade_proximitymine"); Template.iRange = default.PROXIMITYMINE_RANGE; Template.iRadius = default.PROXIMITYMINE_RADIUS; Template.iClipSize = 1; Template.BaseDamage = default.PROXIMITYMINE_BASEDAMAGE; Template.iSoundRange = 10; Template.iEnvironmentDamage = 20; Template.DamageTypeTemplateName = 'Explosion'; Template.Tier = 2; Template.Abilities.AddItem('ThrowGrenade'); Template.Abilities.AddItem(class'X2Ability_Grenades'.default.ProximityMineDetonationAbilityName); Template.Abilities.AddItem('GrenadeFuse'); StunnedEffect = class'X2StatusEffects'.static.CreateStunnedStatusEffect(2, 33); //StunnedEffect.SetDisplayInfo(ePerkBuff_Penalty, class'X2StatusEffects'.default.RoboticStunnedFriendlyName, class'X2StatusEffects'.default.RoboticStunnedFriendlyDesc, "img:///UILibrary_PerkIcons.UIPerk_stun"); //StunnedEffect.TargetConditions.AddItem(UnitCondition); Template.ThrownGrenadeEffects.AddItem(StunnedEffect); Template.bOverrideConcealmentRule = true; // override the normal behavior for the throw or launch grenade ability Template.OverrideConcealmentRule = eConceal_Always; // always stay concealed when throwing or launching a proximity mine Template.GameArchetype = "WP_Proximity_Mine.WP_Proximity_Mine"; Template.iPhysicsImpulse = 10; Template.CanBeBuilt = true; Template.TradingPostValue = 25; // Requirements Template.Requirements.RequiredTechs.AddItem('AutopsyAndromedon'); // Cost Resources.ItemTemplateName = 'Supplies'; Resources.Quantity = 100; Template.Cost.ResourceCosts.AddItem(Resources); Template.SetUIStatMarkup(class'XLocalizedData'.default.RangeLabel, , default.PROXIMITYMINE_RANGE); Template.SetUIStatMarkup(class'XLocalizedData'.default.RadiusLabel, , default.PROXIMITYMINE_RADIUS); Template.SetUIStatMarkup(class'XLocalizedData'.default.ShredLabel, , default.PROXIMITYMINE_BASEDAMAGE.Shred); return Template; } defaultproperties { bShouldCreateDifficultyVariants = true } Another problem i have is that i don't know if i even have the correct effect, i basically took the stunned effect from the EMP grenade and removed the lines that seemed to reference robots and the solution builds, the problem i have is that i can't even test it because it doesn't let me use it on my aliens, it doesn't show up at all. I have it on their config file like this and the other items show up fine: ;A configuration File [XComGame.X2ItemTemplateManager] +Loadouts=(LoadoutName="AdventScout_Loadout", Items[0]=(Item="AdventScout_SMG")) ;Captain +Loadouts=(LoadoutName="AdventScoutCaptain_Loadout", Items[0]=(Item="AdventHunter_Rifle")) +Loadouts=(LoadoutName="AdventScoutCaptain_Loadout", Items[0]=(Item="ProximityStunMine")) It's just the Proximity stun mine that doesn't. So basically, as usual, i have no idea what i'm doing. Link to comment Share on other sites More sharing options...
Shuma22 Posted October 27, 2016 Author Share Posted October 27, 2016 (edited) So i fixed the loadout thing and now it shows on the aliens, the problem is that now there's something wrong with it when i try to throw it, i can't actually throw the mine far away, if i move my cursor to change the location when i click "throw grenade" the aiming breaks. And if i just throw it the moment i click throw grenade before the aiming breaks, i get a redscreen error: http://puu.sh/rXaCh/a3cf28ca91.jpg Any ideas? Edit: Ok it's working now, it seems it was a problem because i still had it pointing to the default config file, and i guess the game doesn't like 2 items or abilities using the same configs, i made my own for my stun mine and now it works. There are some problems though, now when i throw the mine it blows instantly like a grenade, it doesn't stay there like a proximity mine. It does stun apparently, but i think it only stuns one target caught in the AoE, and there is no FX for the explosion, the mine just lands, the target gets stunned and then the mine disappears. Edited October 27, 2016 by Shuma22 Link to comment Share on other sites More sharing options...
robojumper Posted October 27, 2016 Share Posted October 27, 2016 I wrote a Proximity Mine like thing for E3245 for his Tracer's Arsenal Mod. You should check it out if you want to see how to do it. Link to comment Share on other sites More sharing options...
Shuma22 Posted October 28, 2016 Author Share Posted October 28, 2016 You're a hero, thank you. I'll post again when i figure it out or if i fail miserably. Link to comment Share on other sites More sharing options...
Shuma22 Posted November 1, 2016 Author Share Posted November 1, 2016 Well i made some progress, i'd be lying if i said i understood what it all meant but at least my proximity mines now blow up like proximity mines, i also made some progress on having my ability deal damage but i still have 2 problems. Problem 1: My new weapon and my new ability deal damage as they are supposed to, except when i hit something with armor it always deals 1 damage through their armor. Doesn't matter how much armor they have, it's always 1 damage. It does this even if it's the standard shot or the ability i made, and i'm not sure why it's happening. Also sometimes it will show in the enemy healthbar how much damage it will do, and sometimes it wont. http://puu.sh/s39pf/b16a886945.jpg Here's the code for the ability. class X2Ability_HunterShot2 extends X2Ability config(GameData_AdventScoutAbility); var config int MARK_TARGET_LOCAL_COOLDOWN; //var config int HUNTER_MARK_DAMAGE; //var config WeaponDamageValue HUNTERRIFLE_WPN_BASEDAMAGE; var localized string TargetGettingMarkedFriendlyName; var localized string TargetGettingMarkedFriendlyDesc; static function array<X2DataTemplate> CreateTemplates() { local array<X2DataTemplate> Templates; Templates.AddItem(CreateHunterShot2()); return Templates; } static function X2DataTemplate CreateHunterShot2() { local X2AbilityTemplate Template; local X2AbilityCost_ActionPoints ActionPointCost; local X2Condition_UnitProperty UnitPropertyCondition; local X2Condition_Visibility TargetVisibilityCondition; local X2Condition_UnitEffects UnitEffectsCondition; local X2AbilityTarget_Single SingleTarget; local X2AbilityTrigger_PlayerInput InputTrigger; local X2Effect_Persistent MarkedEffect; local X2AbilityCooldown_LocalAndGlobal Cooldown; local X2AbilityCost_Ammo AmmoCost; ////local X2Effect_ApplyWeaponDamage WeaponDamage; local X2Effect_ApplyWeaponDamage WeaponDamageEffect; ////local X2Effect_ApplyWeaponDamage DamageEffect; `CREATE_X2ABILITY_TEMPLATE(Template, 'HunterShot2'); Template.IconImage = "img:///UILibrary_PerkIcons.UIPerk_advent_marktarget"; Template.eAbilityIconBehaviorHUD = eAbilityIconBehavior_AlwaysShow; Template.AbilitySourceName = 'eAbilitySource_Standard'; Template.Hostility = eHostility_Offensive; Cooldown = new class'X2AbilityCooldown_LocalAndGlobal'; Cooldown.iNumTurns = default.MARK_TARGET_LOCAL_COOLDOWN; Template.AbilityCooldown = Cooldown; //Action Point Cost ActionPointCost = new class'X2AbilityCost_ActionPoints'; ActionPointCost.iNumPoints = 1; ActionPointCost.bConsumeAllPoints = true; ActionPointCost.bFreeCost = false; Template.AbilityCosts.AddItem(ActionPointCost); //Ammo AmmoCost = new class'X2AbilityCost_Ammo'; AmmoCost.iAmmo = 1; Template.AbilityCosts.AddItem(AmmoCost); Template.bAllowAmmoEffects = true; // The shooter cannot be mind controlled UnitEffectsCondition = new class'X2Condition_UnitEffects'; UnitEffectsCondition.AddExcludeEffect(class'X2Effect_MindControl'.default.EffectName, 'AA_UnitIsMindControlled'); Template.AbilityShooterConditions.AddItem(UnitEffectsCondition); Template.AbilityShooterConditions.AddItem(default.LivingShooterProperty); // Target must be an enemy UnitPropertyCondition = new class'X2Condition_UnitProperty'; UnitPropertyCondition.ExcludeDead = true; UnitPropertyCondition.ExcludeFriendlyToSource = true; UnitPropertyCondition.RequireWithinRange = false; Template.AbilityTargetConditions.AddItem(UnitPropertyCondition); // Target must be visible and may use squad sight TargetVisibilityCondition = new class'X2Condition_Visibility'; TargetVisibilityCondition.bRequireGameplayVisible = true; TargetVisibilityCondition.bAllowSquadsight = true; Template.AbilityTargetConditions.AddItem(TargetVisibilityCondition); // Target cannot already be marked UnitEffectsCondition = new class'X2Condition_UnitEffects'; UnitEffectsCondition.AddExcludeEffect(class'X2StatusEffects'.default.MarkedName, 'AA_UnitIsMarked'); Template.AbilityTargetConditions.AddItem(UnitEffectsCondition); // 100% chance to hit //Template.AbilityToHitCalc = default.DeadEye; Template.AbilityToHitCalc = default.SimpleStandardAim; Template.AbilityToHitOwnerOnMissCalc = default.SimpleStandardAim; SingleTarget = new class'X2AbilityTarget_Single'; Template.AbilityTargetStyle = SingleTarget; InputTrigger = new class'X2AbilityTrigger_PlayerInput'; Template.AbilityTriggers.AddItem(InputTrigger); // Targeting Method Template.TargetingMethod = class'X2TargetingMethod_OverTheShoulder'; Template.bUsesFiringCamera = true; Template.CinescriptCameraType = "StandardGunFiring"; // Weapon Upgrade Compatibility Template.bAllowFreeFireWeaponUpgrade = true; // Flag that permits action to become 'free action' via 'Hair Trigger' or similar upgrade / effects // Put holo target effect first because if the target dies from this shot, it will be too late to notify the effect. Template.AddTargetEffect(class'X2Ability_GrenadierAbilitySet'.static.HoloTargetEffect()); // Various Soldier ability specific effects - effects check for the ability before applying Template.AddTargetEffect(class'X2Ability_GrenadierAbilitySet'.static.ShredderDamageEffect()); // Damage Effect Template.AddTargetEffect(default.WeaponUpgradeMissDamage); // Damage Effect WeaponDamageEffect = new class'X2Effect_ApplyWeaponDamage'; WeaponDamageEffect.bIgnoreBaseDamage = false; /////WeaponDamageEffect.EffectDamageValue.Damage = Damage; /////WeaponDamageEffect.EffectDamageValue.Damage = default.HUNTER_MARK_DAMAGE; //WeaponDamageEffect.EffectDamageValue = class'X2Item_AdventScout_Weapon'.default.HUNTERRIFLE_WPN_BASEDAMAGE; /////WeaponDamageEffect.EffectDamageValue.Spread = Spread; /////WeaponDamageEffect.EffectDamageValue.DamageType = 'Projectile_MagAdvent'; //Template.AddTargetEffect(WeaponDamageEffect); //Weapon Damage Effect //WeaponDamageEffect = new class'X2Effect_ApplyWeaponDamage'; //WeaponDamage = new class'X2Effect_ApplyWeaponDamage'; //WeaponDamageEffect.bIgnoreBaseDamage = true; //DamageEffect.EffectDamageValue.DamageType = 'Projectile_MagAdvent'; //Template.AddTargetEffect(WeaponDamageEffect); //Template.BaseDamage = default.HUNTER_MARK_DAMAGE; //DamageEffect.EffectDamageValue = default.HUNTER_MARK_DAMAGE; //DamageEffect.DamageEffect = class'X2Item_AdventScout_Weapons'.default.ADVTROOPERM1_WPN_BASEDAMAGE; //DamageEffect = new class'X2Effect_ApplyWeaponDamage'; //DamageEffect.EnvironmentalDamageAmount = default.WRATH_CANNON_ENVIRONMENT_DAMAGE_AMOUNT; //DamageEffect.bExplosiveDamage = true; //Template.AddMultiTargetEffect(DamageEffect); //Template.AddMultiTargetEffect(new class'X2Effect_ApplyFireToWorld'); //PhysicalDamageEffect = new class'X2Effect_ApplyWeaponDamage'; //PhysicalDamageEffect.EffectDamageValue = class'X2Item_DefaultWeapons'.default.ANDROMEDONROBOT_MELEEATTACK_BASEDAMAGE; //PhysicalDamageEffect.EffectDamageValue.DamageType = 'Melee'; // This also deals environmental damage //PhysicalDamageEffect.EnvironmentalDamageAmount = default.BIG_DAMN_PUNCH_ENVIRONMENT_DAMAGE_AMOUNT; //Template.AddTargetEffect(PhysicalDamageEffect); // Create the Marked effect MarkedEffect = class'X2StatusEffects'.static.CreateMarkedEffect(2, false); //Template.AddTargetEffect(MarkedEffect); Template.AddTargetEffect(MarkedEffect); //BMU - changing to an immediate execution for evaluation //Template.CustomFireAnim = 'HL_SignalPoint'; Template.CustomFireAnim = 'FF_FireMag'; //Make it live //Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; //Template.BuildInterruptGameStateFn = TypicalAbility_BuildInterruptGameState; //Template.BuildVisualizationFn = TargetGettingMarked_BuildVisualization; // MAKE IT LIVE! Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; Template.BuildVisualizationFn = TypicalAbility_BuildVisualization; Template.BuildInterruptGameStateFn = TypicalAbility_BuildInterruptGameState; //Cinematic shot? //Template.CinescriptCameraType = "Mark_Target"; return Template; } The Weapon templates, the weapon in question is the "Hunter rifle" class X2Item_AdventScout_Weapon extends X2Item config(AdventScout_WeaponData); var config array<int> FLAT_CONVENTIONAL_RANGE; var config WeaponDamageValue ADVTROOPERM1_WPN_BASEDAMAGE; var config int ASSAULTRIFLE_MAGNETIC_ICLIPSIZE; var config int ASSAULTRIFLE_MAGNETIC_ISOUNDRANGE; var config int ASSAULTRIFLE_MAGNETIC_IENVIRONMENTDAMAGE; var config int ADVTROOPERM1_IDEALRANGE; var config WeaponDamageValue HUNTERRIFLE_WPN_BASEDAMAGE; var config int HUNTERRIFLE_MAGNETIC_ICLIPSIZE; var config int HUNTERRIFLE_MAGNETIC_ISOUNDRANGE; var config int HUNTERRIFLE_MAGNETIC_IENVIRONMENTDAMAGE; var config int HUNTERRIFLE_IDEALRANGE; static function array<X2DataTemplate> CreateTemplates() { local array<X2DataTemplate> Templates; Templates.AddItem(CreateTemplate_AdventScout_SMG()); Templates.AddItem(CreateTemplate_AdventHunter_Rifle()); return Templates; } static function X2DataTemplate CreateTemplate_AdventScout_SMG() { local X2WeaponTemplate Template; `CREATE_X2TEMPLATE(class'X2WeaponTemplate', Template, 'AdventScout_SMG'); Template.WeaponPanelImage = "_ConventionalRifle"; // used by the UI. Probably determines iconview of the weapon. Template.ItemCat = 'weapon'; Template.WeaponCat = 'rifle'; Template.WeaponTech = 'magnetic'; Template.strImage = "img:///UILibrary_Common.AlienWeapons.AdventAssaultRifle"; Template.RemoveTemplateAvailablility(Template.BITFIELD_GAMEAREA_Multiplayer); //invalidates multiplayer availability Template.RangeAccuracy = default.FLAT_CONVENTIONAL_RANGE; Template.BaseDamage = default.ADVTROOPERM1_WPN_BASEDAMAGE; Template.iClipSize = default.ASSAULTRIFLE_MAGNETIC_ICLIPSIZE; Template.iSoundRange = default.ASSAULTRIFLE_MAGNETIC_ISOUNDRANGE; Template.iEnvironmentDamage = default.ASSAULTRIFLE_MAGNETIC_IENVIRONMENTDAMAGE; Template.iIdealRange = default.ADVTROOPERM1_IDEALRANGE; Template.InventorySlot = eInvSlot_PrimaryWeapon; Template.Abilities.AddItem('StandardShot'); Template.Abilities.AddItem('Overwatch'); Template.Abilities.AddItem('OverwatchShot'); Template.Abilities.AddItem('Reload'); Template.Abilities.AddItem('HotLoadAmmo'); //Animation Test //Template.Abilities.AddItem('Suppression'); // This all the resources; sounds, animations, models, physics, the works. Template.GameArchetype = "AdventScoutWeapons.Archetype.WP_ScoutSMG_MG_Advent"; //Full Archetype Name XComWeapon'AdventScoutWeapons.Archetype.WP_ScoutSMG_MG_Advent' Template.iPhysicsImpulse = 5; Template.CanBeBuilt = false; Template.TradingPostValue = 30; Template.DamageTypeTemplateName = 'Projectile_Conventional'; return Template; } static function X2DataTemplate CreateTemplate_AdventHunter_Rifle() { local X2WeaponTemplate Template; `CREATE_X2TEMPLATE(class'X2WeaponTemplate', Template, 'AdventHunter_Rifle'); Template.WeaponPanelImage = "_ConventionalRifle"; // used by the UI. Probably determines iconview of the weapon. Template.ItemCat = 'weapon'; Template.WeaponCat = 'rifle'; Template.WeaponTech = 'magnetic'; Template.strImage = "img:///UILibrary_Common.AlienWeapons.AdventAssaultRifle"; Template.RemoveTemplateAvailablility(Template.BITFIELD_GAMEAREA_Multiplayer); //invalidates multiplayer availability Template.RangeAccuracy = default.FLAT_CONVENTIONAL_RANGE; Template.BaseDamage = default.HUNTERRIFLE_WPN_BASEDAMAGE; Template.iClipSize = default.HUNTERRIFLE_MAGNETIC_ICLIPSIZE; Template.iSoundRange = default.HUNTERRIFLE_MAGNETIC_ISOUNDRANGE; Template.iEnvironmentDamage = default.HUNTERRIFLE_MAGNETIC_IENVIRONMENTDAMAGE; Template.iIdealRange = default.HUNTERRIFLE_IDEALRANGE; Template.InventorySlot = eInvSlot_PrimaryWeapon; Template.Abilities.AddItem('StandardShot'); Template.Abilities.AddItem('HunterShot2'); Template.Abilities.AddItem('Overwatch'); Template.Abilities.AddItem('OverwatchShot'); Template.Abilities.AddItem('Reload'); Template.Abilities.AddItem('HotLoadAmmo'); //Animation Test //Template.Abilities.AddItem('Suppression'); // This all the resources; sounds, animations, models, physics, the works. Template.GameArchetype = "AdventScoutWeapons.Archetype.WP_ScoutSMG_MG_Advent"; //Full Archetype Name XComWeapon'AdventScoutWeapons.Archetype.WP_ScoutSMG_MG_Advent' Template.iPhysicsImpulse = 5; Template.CanBeBuilt = false; Template.TradingPostValue = 30; Template.DamageTypeTemplateName = 'Projectile_Conventional'; return Template; } defaultproperties { bShouldCreateDifficultyVariants = true } And the ini for the weapon stats [AdventScouts.X2Item_AdventScout_Weapon] ;;;;Scout SMG;;;; ADVTROOPERM1_WPN_BASEDAMAGE=(Damage=3, Spread = 0, PlusOne = 0, Crit = 1, Pierce = 0, Shred = 0, Tag = "", DamageType="Projectile_MagAdvent") FLAT_CONVENTIONAL_RANGE[0]=0 FLAT_CONVENTIONAL_RANGE[1]=0 FLAT_CONVENTIONAL_RANGE[2]=0 FLAT_CONVENTIONAL_RANGE[3]=0 FLAT_CONVENTIONAL_RANGE[4]=0 FLAT_CONVENTIONAL_RANGE[5]=0 FLAT_CONVENTIONAL_RANGE[6]=0 FLAT_CONVENTIONAL_RANGE[7]=0 FLAT_CONVENTIONAL_RANGE[8]=0 FLAT_CONVENTIONAL_RANGE[9]=0 FLAT_CONVENTIONAL_RANGE[10]=0 FLAT_CONVENTIONAL_RANGE[11]=0 FLAT_CONVENTIONAL_RANGE[12]=0 FLAT_CONVENTIONAL_RANGE[13]=0 FLAT_CONVENTIONAL_RANGE[14]=0 FLAT_CONVENTIONAL_RANGE[15]=0 FLAT_CONVENTIONAL_RANGE[16]=0 FLAT_CONVENTIONAL_RANGE[17]=0 FLAT_CONVENTIONAL_RANGE[18]=0 FLAT_CONVENTIONAL_RANGE[19]=0 FLAT_CONVENTIONAL_RANGE[20]=0 FLAT_CONVENTIONAL_RANGE[21]=0 FLAT_CONVENTIONAL_RANGE[22]=0 FLAT_CONVENTIONAL_RANGE[23]=0 FLAT_CONVENTIONAL_RANGE[24]=0 FLAT_CONVENTIONAL_RANGE[25]=0 ASSAULTRIFLE_MAGNETIC_ICLIPSIZE=4 ASSAULTRIFLE_MAGNETIC_ISOUNDRANGE=27 ASSAULTRIFLE_MAGNETIC_IENVIRONMENTDAMAGE=10 ADVTROOPERM1_IDEALRANGE=6 ;;;;Hunter Rifle;;;; HUNTERRIFLE_WPN_BASEDAMAGE=(Damage=2, Spread = 0, PlusOne = 0, Crit = 0, Pierce = 0, Shred = 0, Tag = "", DamageType="Projectile_MagAdvent") HUNTERRIFLE_MAGNETIC_ICLIPSIZE=4 HUNTERRIFLE_MAGNETIC_ISOUNDRANGE=27 HUNTERRIFLE_MAGNETIC_IENVIRONMENTDAMAGE=10 HUNTERRIFLE_IDEALRANGE=6 And the second problem i have is that i don't know how to have the stun effect on my "Proximity Stun Mine" work, if i add it on the item template then it continues to work like it did before, the moment the mine lands it stuns(on that subject it doesn't always affect everyone in the radius of the mine), and if i put it in the ability it just doesn't do anything after the mine blows up. Maybe i'm doing something wrong when i add the "stunned effect" on the ability template but modbuddy doesn't seem to think anything is wrong. So i assume the stunned effect should also be mentioned or something in this gamestate thingy. But i know less than nothing about gamestates, to be honest i never would've figured that's how Proximity mines work. Just in case there's something wrong with my templates i'll post them here though, this is the item template for the proximity stun mine: class X2Item_AdventScout_Grenade extends X2Item config(AdventScout_WeaponData); var config WeaponDamageValue PROXIMITYSTUNMINE_BASEDAMAGE; var config int PROXIMITYSTUNMINE_RANGE; var config int PROXIMITYSTUNMINE_RADIUS; static function array<X2DataTemplate> CreateTemplates() { local array<X2DataTemplate> Grenades; Grenades.AddItem(ProximityStunMine()); return Grenades; } static function X2GrenadeTemplate ProximityStunMine() { local X2GrenadeTemplate Template; local ArtifactCost Resources; //local X2Effect_Stunned StunnedEffect; `CREATE_X2TEMPLATE(class'X2GrenadeTemplate', Template, 'ProximityStunMine'); Template.strImage = "img:///UILibrary_StrategyImages.X2InventoryIcons.Inv_Proximity_Mine"; Template.EquipSound = "StrategyUI_Grenade_Equip"; Template.AddAbilityIconOverride('ThrowGrenade', "img:///UILibrary_PerkIcons.UIPerk_grenade_proximitymine"); Template.AddAbilityIconOverride('LaunchGrenade', "img:///UILibrary_PerkIcons.UIPerk_grenade_proximitymine"); Template.iRange = default.PROXIMITYSTUNMINE_RANGE; Template.iRadius = default.PROXIMITYSTUNMINE_RADIUS; Template.iClipSize = 1; Template.BaseDamage = default.PROXIMITYSTUNMINE_BASEDAMAGE; Template.iSoundRange = 10; Template.iEnvironmentDamage = 20; Template.DamageTypeTemplateName = 'Explosion'; Template.Tier = 2; Template.Abilities.AddItem('ThrowGrenade'); Template.Abilities.AddItem(class'X2Ability_StunMineDetonation'.default.ProximityStunMineDetonationAbilityName); Template.Abilities.AddItem('GrenadeFuse'); //StunnedEffect = class'X2StatusEffects'.static.CreateStunnedStatusEffect(2, 33); ////StunnedEffect.SetDisplayInfo(ePerkBuff_Penalty, class'X2StatusEffects'.default.RoboticStunnedFriendlyName, class'X2StatusEffects'.default.RoboticStunnedFriendlyDesc, "img:///UILibrary_PerkIcons.UIPerk_stun"); ////StunnedEffect.TargetConditions.AddItem(UnitCondition); //Template.ThrownGrenadeEffects.AddItem(StunnedEffect); Template.bOverrideConcealmentRule = true; // override the normal behavior for the throw or launch grenade ability Template.OverrideConcealmentRule = eConceal_Always; // always stay concealed when throwing or launching a proximity mine Template.GameArchetype = "WP_Proximity_Mine.WP_Proximity_Mine"; Template.iPhysicsImpulse = 10; Template.CanBeBuilt = true; Template.TradingPostValue = 25; // Requirements Template.Requirements.RequiredTechs.AddItem('AutopsyAndromedon'); // Cost Resources.ItemTemplateName = 'Supplies'; Resources.Quantity = 100; Template.Cost.ResourceCosts.AddItem(Resources); Template.SetUIStatMarkup(class'XLocalizedData'.default.RangeLabel, , default.PROXIMITYSTUNMINE_RANGE); Template.SetUIStatMarkup(class'XLocalizedData'.default.RadiusLabel, , default.PROXIMITYSTUNMINE_RADIUS); Template.SetUIStatMarkup(class'XLocalizedData'.default.ShredLabel, , default.PROXIMITYSTUNMINE_BASEDAMAGE.Shred); return Template; } defaultproperties { bShouldCreateDifficultyVariants = true } And this is the ability template for the "Stun mine detonation" ability. class X2Ability_StunMineDetonation extends X2Ability config(GameData); var name ProximityStunMineDetonationAbilityName; var config string ProximityMineExplosion; // Particle effect for explosion static function array<X2DataTemplate> CreateTemplates() { local array<X2DataTemplate> Templates; Templates.AddItem(ProximityStunMineDetonation()); return Templates; } static function X2AbilityTemplate ProximityStunMineDetonation() { local X2AbilityTemplate Template; local X2AbilityToHitCalc_StandardAim ToHit; local X2Condition_UnitProperty UnitPropertyCondition; local X2Condition_AbilitySourceWeapon GrenadeCondition; local X2AbilityTarget_Cursor CursorTarget; //local X2AbilityMultiTarget_SoldierBonusRadius RadiusMultiTarget; local X2AbilityMultiTarget_Radius RadiusMultiTarget; local X2Effect_ApplyWeaponDamage WeaponDamage; local X2Effect_Stunned StunnedEffect; `CREATE_X2ABILITY_TEMPLATE(Template, default.ProximityStunMineDetonationAbilityName); ToHit = new class'X2AbilityToHitCalc_StandardAim'; ToHit.bIndirectFire = true; Template.AbilityToHitCalc = ToHit; CursorTarget = new class'X2AbilityTarget_Cursor'; CursorTarget.IncreaseWeaponRange = 2; Template.AbilityTargetStyle = CursorTarget; Template.AddShooterEffect(new class'X2Effect_BreakUnitConcealment'); RadiusMultiTarget = new class'X2AbilityMultiTarget_SoldierBonusRadius'; RadiusMultiTarget.bUseWeaponRadius = true; RadiusMultiTarget.fTargetRadius = 2; //RadiusMultiTarget.SoldierAbilityName = 'VolatileMix'; //RadiusMultiTarget.BonusRadius = class'X2Ability_GrenadierAbilitySet'.default.VOLATILE_RADIUS; Template.AbilityMultiTargetStyle = RadiusMultiTarget; UnitPropertyCondition = new class'X2Condition_UnitProperty'; UnitPropertyCondition.ExcludeDead = true; UnitPropertyCondition.ExcludeFriendlyToSource = false; UnitPropertyCondition.ExcludeHostileToSource = false; UnitPropertyCondition.FailOnNonUnits = false; //The grenade can affect interactive objects, others Template.AbilityMultiTargetConditions.AddItem(UnitPropertyCondition); GrenadeCondition = new class'X2Condition_AbilitySourceWeapon'; GrenadeCondition.CheckGrenadeFriendlyFire = true; Template.AbilityMultiTargetConditions.AddItem(GrenadeCondition); WeaponDamage = new class'X2Effect_ApplyWeaponDamage'; WeaponDamage.bExplosiveDamage = true; Template.AddMultiTargetEffect(WeaponDamage); //Stun Effect //StunnedEffect = class'X2StatusEffects'.static.CreateStunnedStatusEffect(default.GUNPOINT_STUN_DURATION, default.GUNPOINT_STUN_CHANCE); //StunnedEffect.bRemoveWhenSourceDies = false; //Template.AddTargetEffect(StunnedEffect); StunnedEffect = class'X2StatusEffects'.static.CreateStunnedStatusEffect(2, 100); ////StunnedEffect.SetDisplayInfo(ePerkBuff_Penalty, class'X2StatusEffects'.default.RoboticStunnedFriendlyName, class'X2StatusEffects'.default.RoboticStunnedFriendlyDesc, "img:///UILibrary_PerkIcons.UIPerk_stun"); ////StunnedEffect.TargetConditions.AddItem(UnitCondition); //Template.ThrownGrenadeEffects.AddItem(StunnedEffect); Template.AddTargetEffect(StunnedEffect); Template.AbilityTriggers.AddItem(new class'X2AbilityTrigger_Placeholder'); // ability is activated by effect detecting movement in range of mine Template.AbilitySourceName = 'eAbilitySource_Standard'; Template.eAbilityIconBehaviorHUD = eAbilityIconBehavior_NeverShow; Template.IconImage = "img:///UILibrary_PerkIcons.UIPerk_grenade_proximitymine"; Template.ShotHUDPriority = class'UIUtilities_Tactical'.const.STANDARD_GRENADE_PRIORITY; Template.bUseAmmoAsChargesForHUD = true; Template.bDisplayInUITooltip = false; Template.bDisplayInUITacticalText = false; Template.FrameAbilityCameraType = eCameraFraming_Never; Template.ActivationSpeech = 'Explosion'; Template.bSkipFireAction = true; Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; Template.BuildVisualizationFn = ProximityStunMineDetonation_BuildVisualization; // cannot interrupt this explosion return Template; } function ProximityStunMineDetonation_BuildVisualization(XComGameState VisualizeGameState, out array<VisualizationTrack> OutVisualizationTracks) { local XComGameStateContext_Ability AbilityContext; local int ShooterID, ShooterTrackIdx, LoopIdx; local VisualizationTrack VisTrack; local X2Action_PlayEffect EffectAction; local X2Action_SendInterTrackMessage MessageAction; local X2Action_WaitForAbilityEffect WaitAction; local X2Action_CameraLookAt LookAtAction; local X2Action_Delay DelayAction; local X2Action_StartStopSound SoundAction; ShooterTrackIdx = INDEX_NONE; AbilityContext = XComGameStateContext_Ability(VisualizeGameState.GetContext()); ShooterID = AbilityContext.InputContext.SourceObject.ObjectID; TypicalAbility_BuildVisualization(VisualizeGameState, OutVisualizationTracks); //Find and grab the "shooter" track - the unit who threw the proximity mine initially for (LoopIdx = 0; LoopIdx < OutVisualizationTracks.Length; ++LoopIdx) { VisTrack = OutVisualizationTracks[LoopIdx]; if (ShooterID == VisTrack.StateObject_NewState.ObjectID) { ShooterTrackIdx = LoopIdx; break; } } `assert(ShooterTrackIdx != INDEX_NONE); //Clear the track and use it for the camera and detonation OutVisualizationTracks[ShooterTrackIdx].TrackActions.Length = 0; //Camera comes first LookAtAction = X2Action_CameraLookAt(class'X2Action_CameraLookAt'.static.CreateVisualizationAction(AbilityContext)); LookAtAction.LookAtLocation = AbilityContext.InputContext.TargetLocations[0]; LookAtAction.BlockUntilFinished = true; LookAtAction.LookAtDuration = 2.0f; OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(LookAtAction); //Do the detonation EffectAction = X2Action_PlayEffect(class'X2Action_PlayEffect'.static.CreateVisualizationAction(AbilityContext)); EffectAction.EffectName = default.ProximityMineExplosion; EffectAction.EffectLocation = AbilityContext.InputContext.TargetLocations[0]; EffectAction.EffectRotation = Rotator(vect(0, 0, 1)); EffectAction.bWaitForCompletion = false; EffectAction.bWaitForCameraCompletion = false; OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(EffectAction); SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.CreateVisualizationAction(AbilityContext)); SoundAction.Sound = new class'SoundCue'; SoundAction.Sound.AkEventOverride = AkEvent'SoundX2CharacterFX.Proximity_Mine_Explosion'; SoundAction.bIsPositional = true; SoundAction.vWorldPosition = AbilityContext.InputContext.TargetLocations[0]; OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(SoundAction); //Make everyone else wait for the detonation for (LoopIdx = 0; LoopIdx < OutVisualizationTracks.Length; ++LoopIdx) { if (LoopIdx == ShooterTrackIdx) continue; WaitAction = X2Action_WaitForAbilityEffect(class'X2Action_WaitForAbilityEffect'.static.CreateVisualizationAction(AbilityContext)); OutVisualizationTracks[LoopIdx].TrackActions.InsertItem(0, WaitAction); MessageAction = X2Action_SendInterTrackMessage(class'X2Action_SendInterTrackMessage'.static.CreateVisualizationAction(AbilityContext)); MessageAction.SendTrackMessageToRef = OutVisualizationTracks[LoopIdx].StateObject_NewState.GetReference(); OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(MessageAction); } //Keep the camera there after things blow up DelayAction = X2Action_Delay(class'X2Action_Delay'.static.CreateVisualizationAction(AbilityContext)); DelayAction.Duration = 0.5; OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(DelayAction); } function bool GrenadeDamagePreview(XComGameState_Ability AbilityState, StateObjectReference TargetRef, out WeaponDamageValue MinDamagePreview, out WeaponDamageValue MaxDamagePreview, out int AllowsShield) { local XComGameState_Item ItemState; local X2GrenadeTemplate GrenadeTemplate; local XComGameState_Ability DetonationAbility; local XComGameState_Unit SourceUnit; local XComGameStateHistory History; local StateObjectReference AbilityRef; ItemState = AbilityState.GetSourceAmmo(); if (ItemState == none) ItemState = AbilityState.GetSourceWeapon(); if (ItemState == none) return false; GrenadeTemplate = X2GrenadeTemplate(ItemState.GetMyTemplate()); if (GrenadeTemplate == none) return false; if (GrenadeTemplate.DataName != 'ProximityStunMine') return false; History = `XCOMHISTORY; SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(AbilityState.OwnerStateObject.ObjectID)); AbilityRef = SourceUnit.FindAbility(default.ProximityStunMineDetonationAbilityName); DetonationAbility = XComGameState_Ability(History.GetGameStateForObjectID(AbilityRef.ObjectID)); if (DetonationAbility == none) return false; DetonationAbility.GetDamagePreview(TargetRef, MinDamagePreview, MaxDamagePreview, AllowsShield); return true; } DefaultProperties { ProximityStunMineDetonationAbilityName = "ProximityStunMineDetonation" } Any help would be appreciated. If anything, i now know to not bite more than i can chew, i think if i ever make another alien mod in the future i'll just make something ridiculously simple. Link to comment Share on other sites More sharing options...
robojumper Posted November 2, 2016 Share Posted November 2, 2016 You are missing several parts from my mod. I am just going to paste them here, you need to take care of renaming it. 1. X2DownloadableContentInfo_Mod.uc static event OnPostTemplatesCreated() { // Fix up our vanilla throws to account for Pulse Bombs local X2AbilityTemplateManager AbilityTemplateManager; AbilityTemplateManager = class'X2AbilityTemplateManager'.static.GetAbilityTemplateManager(); FixUpAbility(AbilityTemplateManager.FindAbilityTemplate('ThrowGrenade')); FixUpAbility(AbilityTemplateManager.FindAbilityTemplate('LaunchGrenade')); } static function FixUpAbility(X2AbilityTemplate Template) { local X2Effect_PulseBomb PulseBombEffect; local X2Condition_AbilitySourceWeapon PulseBombCondition; PulseBombEffect = new class'X2Effect_PulseBomb'; PulseBombEffect.BuildPersistentEffect(1, true, false, false); PulseBombCondition = new class'X2Condition_AbilitySourceWeapon'; PulseBombCondition.MatchGrenadeType = 'OWPULSEBOMB'; PulseBombEffect.TargetConditions.AddItem(PulseBombCondition); Template.AddShooterEffect(PulseBombEffect); } 2. X2Effect_PulseBomb.uc //--------------------------------------------------------------------------------------- // FILE: X2Effect_ProximityMine.uc // AUTHOR: Joshua Bouscher -- 3/24/2015 // PURPOSE: This effect will persist on a unit that uses a proximity mine. // It is in charge of detecting enemy movement within the appropriate // range and issuing the corresponding detonation ability. // //--------------------------------------------------------------------------------------- // Copyright (c) 2016 Firaxis Games, Inc. All rights reserved. //--------------------------------------------------------------------------------------- class X2Effect_PulseBomb extends X2Effect_Persistent config(Config_OWPULSEBOMB_Grenade); var config string PulseBombPersistentParticles; simulated protected function OnEffectAdded(const out EffectAppliedData ApplyEffectParameters, XComGameState_BaseObject kNewTargetState, XComGameState NewGameState, XComGameState_Effect NewEffectState) { local XComGameState_Effect_PulseBomb EffectState; local X2EventManager EventMgr; local Object ListenerObj; EventMgr = `XEVENTMGR; if (GetPulseBombComponent(NewEffectState) == none) { //create component and attach it to GameState_Effect, adding the new state object to the NewGameState container EffectState = XComGameState_Effect_PulseBomb(NewGameState.CreateStateObject(class'XComGameState_Effect_PulseBomb')); EffectState.InitComponent(); NewEffectState.AddComponentObject(EffectState); NewGameState.AddStateObject(EffectState); } //add listener to new component effect -- do it here because the RegisterForEvents call happens before OnEffectAdded, so component doesn't yet exist ListenerObj = EffectState; if (ListenerObj == none) { `Redscreen("PulseBomb: Failed to find Pulse Component when registering listener"); return; } EventMgr.RegisterForEvent(ListenerObj, 'ObjectMoved', EffectState.PulseBomb_ObjectMoved, ELD_OnStateSubmitted); EventMgr.RegisterForEvent(ListenerObj, 'AbilityActivated', EffectState.PulseBomb_AbilityActivated, ELD_OnStateSubmitted); } static function XComGameState_Effect_PulseBomb GetPulseBombComponent(XComGameState_Effect Effect) { if (Effect != none) return XComGameState_Effect_PulseBomb(Effect.FindComponentObject(class'XComGameState_Effect_PulseBomb')); return none; } simulated function AddX2ActionsForVisualization(XComGameState VisualizeGameState, out VisualizationTrack BuildTrack, name EffectApplyResult) { local XComGameState_Effect MineEffect, EffectState; local X2Action_PlayEffect EffectAction; local X2Action_StartStopSound SoundAction; if (EffectApplyResult != 'AA_Success' || BuildTrack.TrackActor == none) return; foreach VisualizeGameState.IterateByClassType(class'XComGameState_Effect', EffectState) { if (EffectState.GetX2Effect() == self) { MineEffect = EffectState; break; } } `assert(MineEffect != none); //For multiplayer: don't visualize mines on the enemy team. if (MineEffect.GetSourceUnitAtTimeOfApplication().ControllingPlayer.ObjectID != `TACTICALRULES.GetLocalClientPlayerObjectID()) return; EffectAction = X2Action_PlayEffect(class'X2Action_PlayEffect'.static.AddToVisualizationTrack(BuildTrack, VisualizeGameState.GetContext())); EffectAction.EffectName = default.PulseBombPersistentParticles; EffectAction.EffectLocation = MineEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations[0]; SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.AddToVisualizationTrack(BuildTrack, VisualizeGameState.GetContext())); SoundAction.Sound = new class'SoundCue'; SoundAction.Sound.AkEventOverride = AkEvent'SoundX2CharacterFX.Item_Proximity_Mine_Active_Ping'; SoundAction.iAssociatedGameStateObjectId = MineEffect.ObjectID; SoundAction.bStartPersistentSound = true; SoundAction.bIsPositional = true; SoundAction.vWorldPosition = MineEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations[0]; } simulated function AddX2ActionsForVisualization_Sync(XComGameState VisualizeGameState, out VisualizationTrack BuildTrack) { //We assume 'AA_Success', because otherwise the effect wouldn't be here (on load) to get sync'd AddX2ActionsForVisualization(VisualizeGameState, BuildTrack, 'AA_Success'); } simulated function AddX2ActionsForVisualization_Removed(XComGameState VisualizeGameState, out VisualizationTrack BuildTrack, const name EffectApplyResult, XComGameState_Effect RemovedEffect) { local XComGameState_Effect MineEffect, EffectState; local X2Action_PlayEffect EffectAction; local X2Action_StartStopSound SoundAction; if (EffectApplyResult != 'AA_Success' || BuildTrack.TrackActor == none) return; foreach VisualizeGameState.IterateByClassType(class'XComGameState_Effect', EffectState) { if (EffectState.GetX2Effect() == self) { MineEffect = EffectState; break; } } `assert(MineEffect != none); //For multiplayer: don't visualize mines on the enemy team. if (MineEffect.GetSourceUnitAtTimeOfApplication().ControllingPlayer.ObjectID != `TACTICALRULES.GetLocalClientPlayerObjectID()) return; EffectAction = X2Action_PlayEffect(class'X2Action_PlayEffect'.static.AddToVisualizationTrack(BuildTrack, VisualizeGameState.GetContext())); EffectAction.EffectName = default.PulseBombPersistentParticles; EffectAction.EffectLocation = MineEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations[0]; EffectAction.bStopEffect = true; SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.AddToVisualizationTrack(BuildTrack, VisualizeGameState.GetContext())); SoundAction.Sound = new class'SoundCue'; SoundAction.Sound.AkEventOverride = AkEvent'SoundX2CharacterFX.Stop_Proximity_Mine_Active_Ping'; SoundAction.iAssociatedGameStateObjectId = MineEffect.ObjectID; SoundAction.bIsPositional = true; SoundAction.bStopPersistentSound = true; } DefaultProperties { EffectName="PulseBomb" DuplicateResponse = eDupe_Allow; bCanBeRedirected = false; } 3. XComGameState_Effect_PulseBomb.uc class XComGameState_Effect_PulseBomb extends XComGameState_BaseObject; function XComGameState_Effect_PulseBomb InitComponent() { return self; } function XComGameState_Effect GetOwningEffect() { return XComGameState_Effect(`XCOMHISTORY.GetGameStateForObjectID(OwningObjectId)); } function EventListenerReturn PulseBomb_AbilityActivated(Object EventData, Object EventSource, XComGameState GameState, Name EventID) { local XComGameState_Ability AbilityState; local XComGameState_Unit AbilityUnit, SourceUnit, SourceUnitAtTimeOfLaunch; local XComGameStateHistory History; local XComGameStateContext_Ability AbilityContext; local TTile CheckTile, AffectedTile; local bool bLocationMatch; local int LocationIdx; local vector TargetLoc; local bool bAbilityUnitCaughtInDetonation; local XComGameState_Effect OwningEffect; OwningEffect = GetOwningEffect(); if (OwningEffect.bRemoved) return ELR_NoInterrupt; AbilityContext = XComGameStateContext_Ability(GameState.GetContext()); // Proximity mine should not blow up as a pre-emptive strike; only blow up after the ability has successfully executed if (AbilityContext.InterruptionStatus == eInterruptionStatus_Interrupt) return ELR_NoInterrupt; History = `XCOMHISTORY; SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(OwningEffect.ApplyEffectParameters.SourceStateObjectRef.ObjectID)); AbilityUnit = XComGameState_Unit(EventSource); AbilityState = XComGameState_Ability(EventData); if (SourceUnit != none && AbilityUnit != none && AbilityState != none && AbilityContext != none) { SourceUnitAtTimeOfLaunch = OwningEffect.GetSourceUnitAtTimeOfApplication(); if (SourceUnitAtTimeOfLaunch.IsEnemyUnit(AbilityUnit) && AbilityState.IsAbilityInputTriggered() && AbilityState.GetMyTemplate().Hostility == eHostility_Offensive) { foreach OwningEffect.ApplyEffectParameters.AbilityResultContext.RelevantEffectTiles(CheckTile) { if (CheckTile == AbilityUnit.TileLocation) { bLocationMatch = true; bAbilityUnitCaughtInDetonation = true; //The unit itself tripped the mine; it must be caught in the explosion break; } } if (!bLocationMatch) { for (LocationIdx = 0; LocationIdx < AbilityContext.InputContext.TargetLocations.Length; ++LocationIdx) { TargetLoc = AbilityContext.InputContext.TargetLocations[LocationIdx]; AffectedTile = `XWORLD.GetTileCoordinatesFromPosition(TargetLoc); foreach OwningEffect.ApplyEffectParameters.AbilityResultContext.RelevantEffectTiles(CheckTile) { if (CheckTile == AffectedTile) { bLocationMatch = true; bAbilityUnitCaughtInDetonation = false; break; } } } } if (bLocationMatch) { DetonatePulseBomb(SourceUnit, bAbilityUnitCaughtInDetonation?AbilityUnit:None, GameState); } } } return ELR_NoInterrupt; } function EventListenerReturn PulseBomb_ObjectMoved(Object EventData, Object EventSource, XComGameState GameState, Name EventID) { local XComGameStateHistory History; local XComGameState_Unit MovedUnit, SourceUnit, SourceUnitAtTimeOfLaunch; local TTile AffectedTile; local bool bTileMatches; local XComGameState_Effect OwningEffect; OwningEffect = GetOwningEffect(); if (OwningEffect.bRemoved) return ELR_NoInterrupt; History = `XCOMHISTORY; SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(OwningEffect.ApplyEffectParameters.SourceStateObjectRef.ObjectID)); MovedUnit = XComGameState_Unit(EventData); // This never gets triggered, because MovedUnit is always none. Why? if (MovedUnit != none && SourceUnit != none) { foreach OwningEffect.ApplyEffectParameters.AbilityResultContext.RelevantEffectTiles(AffectedTile) { if (AffectedTile == MovedUnit.TileLocation) { bTileMatches = true; break; } } if (bTileMatches) { SourceUnitAtTimeOfLaunch = OwningEffect.GetSourceUnitAtTimeOfApplication(); `log("tile matches"); if (MovedUnit.IsEnemyUnit(SourceUnitAtTimeOfLaunch) && MovedUnit.IsAlive()) // friendlies will not trigger the proximity mine { DetonatePulseBomb(SourceUnit, MovedUnit, GameState); } } } return ELR_NoInterrupt; } private function DetonatePulseBomb(XComGameState_Unit SourceUnit, XComGameState_Unit TriggeringUnit, XComGameState RespondingToGameState) { local XComGameState_Ability AbilityState; local AvailableAction Action; local AvailableTarget Target; local XComGameStateContext_EffectRemoved EffectRemovedState; local XComGameState NewGameState; local XComGameStateHistory History; local TTile AffectedTile; local XComGameState_Unit UnitState; local XComGameState_Effect OwningEffect; `log("BOOOOOMMMM!"); History = `XCOMHISTORY; Action.AbilityObjectRef = SourceUnit.FindAbility(class'X2Ability_PulseBomb'.default.PulseBombDetonationAbilityName); if (Action.AbilityObjectRef.ObjectID != 0) { AbilityState = XComGameState_Ability(History.GetGameStateForObjectID(Action.AbilityObjectRef.ObjectID)); if (AbilityState != none) { OwningEffect = GetOwningEffect(); // manually check the unit states being modified by the event as they may not be properly updated in the world data until the event is complete foreach RespondingToGameState.IterateByClassType(class'XComGameState_Unit', UnitState) { foreach OwningEffect.ApplyEffectParameters.AbilityResultContext.RelevantEffectTiles(AffectedTile) { if (UnitState.TileLocation == AffectedTile) { if (Target.AdditionalTargets.Find('ObjectID', UnitState.ObjectID) == INDEX_NONE) Target.AdditionalTargets.AddItem(UnitState.GetReference()); break; // no need to keep checking tiles for this unit } } } Action.AvailableCode = 'AA_Success'; AbilityState.GatherAdditionalAbilityTargetsForLocation(OwningEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations[0], Target); //Ensure that the triggering unit is caught in the blast. if (TriggeringUnit != None && Target.AdditionalTargets.Find('ObjectID', TriggeringUnit.ObjectID) == INDEX_NONE) Target.AdditionalTargets.AddItem(TriggeringUnit.GetReference()); Action.AvailableTargets.AddItem(Target); if (class'XComGameStateContext_Ability'.static.ActivateAbility(Action, 0, OwningEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations)) { EffectRemovedState = class'XComGameStateContext_EffectRemoved'.static.CreateEffectRemovedContext(OwningEffect); NewGameState = History.CreateNewGameState(true, EffectRemovedState); OwningEffect.RemoveEffect(NewGameState, RespondingToGameState); SubmitNewGameState(NewGameState); } } } } private function SubmitNewGameState(out XComGameState NewGameState) { local X2TacticalGameRuleset TacticalRules; local XComGameStateHistory History; if (NewGameState.GetNumGameStateObjects() > 0) { TacticalRules = `TACTICALRULES; TacticalRules.SubmitGameState(NewGameState); // effects may have changed action availability - if a unit died, took damage, etc. } else { History = `XCOMHISTORY; History.CleanupPendingGameState(NewGameState); } } The thing is that the vanilla Mine works this way: When the Mine is launched, an effect is attached to the throwing unit.This effect listens for movement / ability activation inside of that radius.If the mine should detonate, the Detonation ability is triggered.The vanilla effect can only work with the vanilla mine, which is why we need a new effect, and, to ensure the listeners work, a new Effect State Component.Also, we need to tell the Launch Grenade / Throw Grenade Ability to add the listener effect. Link to comment Share on other sites More sharing options...
Shuma22 Posted November 2, 2016 Author Share Posted November 2, 2016 I do have those, i guess i should've posted them above too but i'll post them here: X2DownloadableContentInfo static event OnPostTemplatesCreated() { // Fix up our vanilla throws to account for Pulse Bombs local X2AbilityTemplateManager AbilityTemplateManager; AbilityTemplateManager = class'X2AbilityTemplateManager'.static.GetAbilityTemplateManager(); FixUpAbility(AbilityTemplateManager.FindAbilityTemplate('ThrowGrenade')); FixUpAbility(AbilityTemplateManager.FindAbilityTemplate('LaunchGrenade')); } static function FixUpAbility(X2AbilityTemplate Template) { local X2Effect_ProximityStunMine ProximityStunMineEffect; local X2Condition_AbilitySourceWeapon ProximityStunMineCondition; ProximityStunMineEffect = new class'X2Effect_ProximityStunMine'; ProximityStunMineEffect.BuildPersistentEffect(1, true, false, false); ProximityStunMineCondition = new class'X2Condition_AbilitySourceWeapon'; ProximityStunMineCondition.MatchGrenadeType = 'ProximityStunMine'; ProximityStunMineEffect.TargetConditions.AddItem(ProximityStunMineCondition); Template.AddShooterEffect(ProximityStunMineEffect); } X2Effect_ProximityStunMine // This is an Unreal Script class X2Effect_ProximityStunMine extends X2Effect_Persistent config(AdventScout_WeaponData); var config string ProximityStunMinePersistentParticles; simulated protected function OnEffectAdded(const out EffectAppliedData ApplyEffectParameters, XComGameState_BaseObject kNewTargetState, XComGameState NewGameState, XComGameState_Effect NewEffectState) { local XComGameState_Effect_ProximityStunMine EffectState; local X2EventManager EventMgr; local Object ListenerObj; EventMgr = `XEVENTMGR; if (GetProximityStunMineComponent(NewEffectState) == none) { //create component and attach it to GameState_Effect, adding the new state object to the NewGameState container EffectState = XComGameState_Effect_ProximityStunMine(NewGameState.CreateStateObject(class'XComGameState_Effect_ProximityStunMine')); EffectState.InitComponent(); NewEffectState.AddComponentObject(EffectState); NewGameState.AddStateObject(EffectState); } //add listener to new component effect -- do it here because the RegisterForEvents call happens before OnEffectAdded, so component doesn't yet exist ListenerObj = EffectState; if (ListenerObj == none) { `Redscreen("ProximityStunMine: Failed to find ProximityStunMine Component when registering listener"); return; } EventMgr.RegisterForEvent(ListenerObj, 'ObjectMoved', EffectState.ProximityStunMine_ObjectMoved, ELD_OnStateSubmitted); EventMgr.RegisterForEvent(ListenerObj, 'AbilityActivated', EffectState.ProximityStunMine_AbilityActivated, ELD_OnStateSubmitted); } static function XComGameState_Effect_ProximityStunMine GetProximityStunMineComponent(XComGameState_Effect Effect) { if (Effect != none) return XComGameState_Effect_ProximityStunMine(Effect.FindComponentObject(class'XComGameState_Effect_ProximityStunMine')); return none; } simulated function AddX2ActionsForVisualization(XComGameState VisualizeGameState, out VisualizationTrack BuildTrack, name EffectApplyResult) { local XComGameState_Effect MineEffect, EffectState; local X2Action_PlayEffect EffectAction; local X2Action_StartStopSound SoundAction; if (EffectApplyResult != 'AA_Success' || BuildTrack.TrackActor == none) return; foreach VisualizeGameState.IterateByClassType(class'XComGameState_Effect', EffectState) { if (EffectState.GetX2Effect() == self) { MineEffect = EffectState; break; } } `assert(MineEffect != none); //For multiplayer: don't visualize mines on the enemy team. if (MineEffect.GetSourceUnitAtTimeOfApplication().ControllingPlayer.ObjectID != `TACTICALRULES.GetLocalClientPlayerObjectID()) return; EffectAction = X2Action_PlayEffect(class'X2Action_PlayEffect'.static.AddToVisualizationTrack(BuildTrack, VisualizeGameState.GetContext())); EffectAction.EffectName = default.ProximityStunMinePersistentParticles; EffectAction.EffectLocation = MineEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations[0]; SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.AddToVisualizationTrack(BuildTrack, VisualizeGameState.GetContext())); SoundAction.Sound = new class'SoundCue'; SoundAction.Sound.AkEventOverride = AkEvent'SoundX2CharacterFX.Item_Proximity_Mine_Active_Ping'; SoundAction.iAssociatedGameStateObjectId = MineEffect.ObjectID; SoundAction.bStartPersistentSound = true; SoundAction.bIsPositional = true; SoundAction.vWorldPosition = MineEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations[0]; } simulated function AddX2ActionsForVisualization_Sync(XComGameState VisualizeGameState, out VisualizationTrack BuildTrack) { //We assume 'AA_Success', because otherwise the effect wouldn't be here (on load) to get sync'd AddX2ActionsForVisualization(VisualizeGameState, BuildTrack, 'AA_Success'); } simulated function AddX2ActionsForVisualization_Removed(XComGameState VisualizeGameState, out VisualizationTrack BuildTrack, const name EffectApplyResult, XComGameState_Effect RemovedEffect) { local XComGameState_Effect MineEffect, EffectState; local X2Action_PlayEffect EffectAction; local X2Action_StartStopSound SoundAction; if (EffectApplyResult != 'AA_Success' || BuildTrack.TrackActor == none) return; foreach VisualizeGameState.IterateByClassType(class'XComGameState_Effect', EffectState) { if (EffectState.GetX2Effect() == self) { MineEffect = EffectState; break; } } `assert(MineEffect != none); //For multiplayer: don't visualize mines on the enemy team. if (MineEffect.GetSourceUnitAtTimeOfApplication().ControllingPlayer.ObjectID != `TACTICALRULES.GetLocalClientPlayerObjectID()) return; EffectAction = X2Action_PlayEffect(class'X2Action_PlayEffect'.static.AddToVisualizationTrack(BuildTrack, VisualizeGameState.GetContext())); EffectAction.EffectName = default.ProximityStunMinePersistentParticles; EffectAction.EffectLocation = MineEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations[0]; EffectAction.bStopEffect = true; SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.AddToVisualizationTrack(BuildTrack, VisualizeGameState.GetContext())); SoundAction.Sound = new class'SoundCue'; SoundAction.Sound.AkEventOverride = AkEvent'SoundX2CharacterFX.Stop_Proximity_Mine_Active_Ping'; SoundAction.iAssociatedGameStateObjectId = MineEffect.ObjectID; SoundAction.bIsPositional = true; SoundAction.bStopPersistentSound = true; } DefaultProperties { EffectName="ProximityStunMine" DuplicateResponse = eDupe_Allow; bCanBeRedirected = false; } And XcomGameState_Effect_ProximityStunMine // This is an Unreal Script class XComGameState_Effect_ProximityStunMine extends XComGameState_BaseObject; function XComGameState_Effect_ProximityStunMine InitComponent() { return self; } function XComGameState_Effect GetOwningEffect() { return XComGameState_Effect(`XCOMHISTORY.GetGameStateForObjectID(OwningObjectId)); } function EventListenerReturn ProximityStunMine_AbilityActivated(Object EventData, Object EventSource, XComGameState GameState, Name EventID) { local XComGameState_Ability AbilityState; local XComGameState_Unit AbilityUnit, SourceUnit, SourceUnitAtTimeOfLaunch; local XComGameStateHistory History; local XComGameStateContext_Ability AbilityContext; local TTile CheckTile, AffectedTile; local bool bLocationMatch; local int LocationIdx; local vector TargetLoc; local bool bAbilityUnitCaughtInDetonation; local XComGameState_Effect OwningEffect; OwningEffect = GetOwningEffect(); if (OwningEffect.bRemoved) return ELR_NoInterrupt; AbilityContext = XComGameStateContext_Ability(GameState.GetContext()); // Proximity mine should not blow up as a pre-emptive strike; only blow up after the ability has successfully executed if (AbilityContext.InterruptionStatus == eInterruptionStatus_Interrupt) return ELR_NoInterrupt; History = `XCOMHISTORY; SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(OwningEffect.ApplyEffectParameters.SourceStateObjectRef.ObjectID)); AbilityUnit = XComGameState_Unit(EventSource); AbilityState = XComGameState_Ability(EventData); if (SourceUnit != none && AbilityUnit != none && AbilityState != none && AbilityContext != none) { SourceUnitAtTimeOfLaunch = OwningEffect.GetSourceUnitAtTimeOfApplication(); if (SourceUnitAtTimeOfLaunch.IsEnemyUnit(AbilityUnit) && AbilityState.IsAbilityInputTriggered() && AbilityState.GetMyTemplate().Hostility == eHostility_Offensive) { foreach OwningEffect.ApplyEffectParameters.AbilityResultContext.RelevantEffectTiles(CheckTile) { if (CheckTile == AbilityUnit.TileLocation) { bLocationMatch = true; bAbilityUnitCaughtInDetonation = true; //The unit itself tripped the mine; it must be caught in the explosion break; } } if (!bLocationMatch) { for (LocationIdx = 0; LocationIdx < AbilityContext.InputContext.TargetLocations.Length; ++LocationIdx) { TargetLoc = AbilityContext.InputContext.TargetLocations[LocationIdx]; AffectedTile = `XWORLD.GetTileCoordinatesFromPosition(TargetLoc); foreach OwningEffect.ApplyEffectParameters.AbilityResultContext.RelevantEffectTiles(CheckTile) { if (CheckTile == AffectedTile) { bLocationMatch = true; bAbilityUnitCaughtInDetonation = false; break; } } } } if (bLocationMatch) { DetonateProximityStunMine(SourceUnit, bAbilityUnitCaughtInDetonation?AbilityUnit:None, GameState); } } } return ELR_NoInterrupt; } function EventListenerReturn ProximityStunMine_ObjectMoved(Object EventData, Object EventSource, XComGameState GameState, Name EventID) { local XComGameStateHistory History; local XComGameState_Unit MovedUnit, SourceUnit, SourceUnitAtTimeOfLaunch; local TTile AffectedTile; local bool bTileMatches; local XComGameState_Effect OwningEffect; OwningEffect = GetOwningEffect(); if (OwningEffect.bRemoved) return ELR_NoInterrupt; History = `XCOMHISTORY; SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(OwningEffect.ApplyEffectParameters.SourceStateObjectRef.ObjectID)); MovedUnit = XComGameState_Unit(EventData); // This never gets triggered, because MovedUnit is always none. Why? if (MovedUnit != none && SourceUnit != none) { foreach OwningEffect.ApplyEffectParameters.AbilityResultContext.RelevantEffectTiles(AffectedTile) { if (AffectedTile == MovedUnit.TileLocation) { bTileMatches = true; break; } } if (bTileMatches) { SourceUnitAtTimeOfLaunch = OwningEffect.GetSourceUnitAtTimeOfApplication(); `log("tile matches"); if (MovedUnit.IsEnemyUnit(SourceUnitAtTimeOfLaunch) && MovedUnit.IsAlive()) // friendlies will not trigger the proximity mine { DetonateProximityStunMine(SourceUnit, MovedUnit, GameState); } } } return ELR_NoInterrupt; } private function DetonateProximityStunMine(XComGameState_Unit SourceUnit, XComGameState_Unit TriggeringUnit, XComGameState RespondingToGameState) { local XComGameState_Ability AbilityState; local AvailableAction Action; local AvailableTarget Target; local XComGameStateContext_EffectRemoved EffectRemovedState; local XComGameState NewGameState; local XComGameStateHistory History; local TTile AffectedTile; local XComGameState_Unit UnitState; local XComGameState_Effect OwningEffect; `log("BOOOOOMMMM!"); History = `XCOMHISTORY; Action.AbilityObjectRef = SourceUnit.FindAbility(class'X2Ability_StunMineDetonation'.default.ProximityStunMineDetonationAbilityName); if (Action.AbilityObjectRef.ObjectID != 0) { AbilityState = XComGameState_Ability(History.GetGameStateForObjectID(Action.AbilityObjectRef.ObjectID)); if (AbilityState != none) { OwningEffect = GetOwningEffect(); // manually check the unit states being modified by the event as they may not be properly updated in the world data until the event is complete foreach RespondingToGameState.IterateByClassType(class'XComGameState_Unit', UnitState) { foreach OwningEffect.ApplyEffectParameters.AbilityResultContext.RelevantEffectTiles(AffectedTile) { if (UnitState.TileLocation == AffectedTile) { if (Target.AdditionalTargets.Find('ObjectID', UnitState.ObjectID) == INDEX_NONE) Target.AdditionalTargets.AddItem(UnitState.GetReference()); break; // no need to keep checking tiles for this unit } } } Action.AvailableCode = 'AA_Success'; AbilityState.GatherAdditionalAbilityTargetsForLocation(OwningEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations[0], Target); //Ensure that the triggering unit is caught in the blast. if (TriggeringUnit != None && Target.AdditionalTargets.Find('ObjectID', TriggeringUnit.ObjectID) == INDEX_NONE) Target.AdditionalTargets.AddItem(TriggeringUnit.GetReference()); Action.AvailableTargets.AddItem(Target); if (class'XComGameStateContext_Ability'.static.ActivateAbility(Action, 0, OwningEffect.ApplyEffectParameters.AbilityInputContext.TargetLocations)) { EffectRemovedState = class'XComGameStateContext_EffectRemoved'.static.CreateEffectRemovedContext(OwningEffect); NewGameState = History.CreateNewGameState(true, EffectRemovedState); OwningEffect.RemoveEffect(NewGameState, RespondingToGameState); SubmitNewGameState(NewGameState); } } } } private function SubmitNewGameState(out XComGameState NewGameState) { local X2TacticalGameRuleset TacticalRules; local XComGameStateHistory History; if (NewGameState.GetNumGameStateObjects() > 0) { TacticalRules = `TACTICALRULES; TacticalRules.SubmitGameState(NewGameState); // effects may have changed action availability - if a unit died, took damage, etc. } else { History = `XCOMHISTORY; History.CleanupPendingGameState(NewGameState); } } Like i said though, the mine does work like a mine, it lands, it stays there and it blows up when something within it's radius moves. The problem i have with the mine is that i don't know how to apply the stun effect after it blows up. If it's on the item it stuns when the mine lands, if it's on the ability it doesn't do anything. So i guess i need to put it in either the gamestate or the effect? But i don't know how. Link to comment Share on other sites More sharing options...
robojumper Posted November 3, 2016 Share Posted November 3, 2016 Stun effects on your mine shouldn't be an AbilityTargetEffect, but an AbilityMultitTargetEffect. Also, X2AbilityMultiTarget_SoldierBonusRadius is deprecated, and the file says that X2AbilityMultiTarget_Radius has the same functionality. Link to comment Share on other sites More sharing options...
Shuma22 Posted November 3, 2016 Author Share Posted November 3, 2016 (edited) Ah you're a gentleman and a scholar, it works now, kinda. Right now only one unit in the radius gets stunned, i assume the one that triggers the detonation, and there's a redscreen error when it happens: http://puu.sh/s58sf/8f691d7849.jpg I guess it's pretty self explanatory, they take too long to go into their stunned animation and the game doesn't like that. They still go into their stun animation eventually though, but i guess this is what normally happens when you are playing where the game just gets stuck after certain actions for a little while? Also i figured that all aliens always deal at least 1 damage through armor, at least i think so? I spawned an enemy andromedon and some advent troopers and they always deal 1 damage through the andromedon's armor. So i guess my weapons dealing damage through armor is normal? Unless my mod broke something that is causing ALL aliens to deal damage through armor. If they always deal 1 damage through armor then i can't believe i never noticed. http://puu.sh/s58Aj/89e47b9d73.jpg Well now i just have to figure out how to make it so that every unit caught in the radius of the stun mine gets stunned and to make a couple more items(hopefully these will be easy) and then i can just keep working on the 3d assets and finish the mod. Edit: I got it working, now it works properly and stuns everything caught in the AoE. Here's the final code, just posting it here in case there's still something wrong with it: Proximity Stun Mine Ability: // This is an Unreal Script class X2Ability_StunMineDetonation extends X2Ability config(GameData); var name ProximityStunMineDetonationAbilityName; var config string ProximityMineExplosion; // Particle effect for explosion static function array<X2DataTemplate> CreateTemplates() { local array<X2DataTemplate> Templates; Templates.AddItem(ProximityStunMineDetonation()); return Templates; } static function X2AbilityTemplate ProximityStunMineDetonation() { local X2AbilityTemplate Template; local X2AbilityToHitCalc_StandardAim ToHit; local X2Condition_UnitProperty UnitPropertyCondition; local X2Condition_AbilitySourceWeapon GrenadeCondition; local X2AbilityTarget_Cursor CursorTarget; local X2AbilityMultiTarget_Radius RadiusMultiTarget; local X2Effect_ApplyWeaponDamage WeaponDamage; local X2Effect_Stunned StunnedEffect; `CREATE_X2ABILITY_TEMPLATE(Template, default.ProximityStunMineDetonationAbilityName); ToHit = new class'X2AbilityToHitCalc_StandardAim'; ToHit.bIndirectFire = true; Template.AbilityToHitCalc = ToHit; CursorTarget = new class'X2AbilityTarget_Cursor'; CursorTarget.IncreaseWeaponRange = 2; Template.AbilityTargetStyle = CursorTarget; Template.AddShooterEffect(new class'X2Effect_BreakUnitConcealment'); RadiusMultiTarget = new class'X2AbilityMultiTarget_Radius'; RadiusMultiTarget.bUseWeaponRadius = true; RadiusMultiTarget.fTargetRadius = 2; Template.AbilityMultiTargetStyle = RadiusMultiTarget; UnitPropertyCondition = new class'X2Condition_UnitProperty'; UnitPropertyCondition.ExcludeDead = true; UnitPropertyCondition.ExcludeFriendlyToSource = false; UnitPropertyCondition.ExcludeHostileToSource = false; UnitPropertyCondition.FailOnNonUnits = false; //The grenade can affect interactive objects, others Template.AbilityMultiTargetConditions.AddItem(UnitPropertyCondition); GrenadeCondition = new class'X2Condition_AbilitySourceWeapon'; GrenadeCondition.CheckGrenadeFriendlyFire = true; Template.AbilityMultiTargetConditions.AddItem(GrenadeCondition); WeaponDamage = new class'X2Effect_ApplyWeaponDamage'; WeaponDamage.bExplosiveDamage = true; Template.AddMultiTargetEffect(WeaponDamage); //Stun Effect StunnedEffect = class'X2StatusEffects'.static.CreateStunnedStatusEffect(2, 100); Template.AddMultiTargetEffect(StunnedEffect); Template.AbilityTriggers.AddItem(new class'X2AbilityTrigger_Placeholder'); // ability is activated by effect detecting movement in range of mine Template.AbilitySourceName = 'eAbilitySource_Standard'; Template.eAbilityIconBehaviorHUD = eAbilityIconBehavior_NeverShow; Template.IconImage = "img:///UILibrary_PerkIcons.UIPerk_grenade_proximitymine"; Template.ShotHUDPriority = class'UIUtilities_Tactical'.const.STANDARD_GRENADE_PRIORITY; Template.bUseAmmoAsChargesForHUD = true; Template.bDisplayInUITooltip = false; Template.bDisplayInUITacticalText = false; Template.FrameAbilityCameraType = eCameraFraming_Never; Template.ActivationSpeech = 'Explosion'; Template.bSkipFireAction = true; Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; Template.BuildVisualizationFn = ProximityStunMineDetonation_BuildVisualization; // cannot interrupt this explosion return Template; } function ProximityStunMineDetonation_BuildVisualization(XComGameState VisualizeGameState, out array<VisualizationTrack> OutVisualizationTracks) { local XComGameStateContext_Ability AbilityContext; local int ShooterID, ShooterTrackIdx, LoopIdx; local VisualizationTrack VisTrack; local X2Action_PlayEffect EffectAction; local X2Action_SendInterTrackMessage MessageAction; local X2Action_WaitForAbilityEffect WaitAction; local X2Action_CameraLookAt LookAtAction; local X2Action_Delay DelayAction; local X2Action_StartStopSound SoundAction; ShooterTrackIdx = INDEX_NONE; AbilityContext = XComGameStateContext_Ability(VisualizeGameState.GetContext()); ShooterID = AbilityContext.InputContext.SourceObject.ObjectID; TypicalAbility_BuildVisualization(VisualizeGameState, OutVisualizationTracks); //Find and grab the "shooter" track - the unit who threw the proximity mine initially for (LoopIdx = 0; LoopIdx < OutVisualizationTracks.Length; ++LoopIdx) { VisTrack = OutVisualizationTracks[LoopIdx]; if (ShooterID == VisTrack.StateObject_NewState.ObjectID) { ShooterTrackIdx = LoopIdx; break; } } `assert(ShooterTrackIdx != INDEX_NONE); //Clear the track and use it for the camera and detonation OutVisualizationTracks[ShooterTrackIdx].TrackActions.Length = 0; //Camera comes first LookAtAction = X2Action_CameraLookAt(class'X2Action_CameraLookAt'.static.CreateVisualizationAction(AbilityContext)); LookAtAction.LookAtLocation = AbilityContext.InputContext.TargetLocations[0]; LookAtAction.BlockUntilFinished = true; LookAtAction.LookAtDuration = 2.0f; OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(LookAtAction); //Do the detonation EffectAction = X2Action_PlayEffect(class'X2Action_PlayEffect'.static.CreateVisualizationAction(AbilityContext)); EffectAction.EffectName = default.ProximityMineExplosion; EffectAction.EffectLocation = AbilityContext.InputContext.TargetLocations[0]; EffectAction.EffectRotation = Rotator(vect(0, 0, 1)); EffectAction.bWaitForCompletion = false; EffectAction.bWaitForCameraCompletion = false; OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(EffectAction); SoundAction = X2Action_StartStopSound(class'X2Action_StartStopSound'.static.CreateVisualizationAction(AbilityContext)); SoundAction.Sound = new class'SoundCue'; SoundAction.Sound.AkEventOverride = AkEvent'SoundX2CharacterFX.Proximity_Mine_Explosion'; SoundAction.bIsPositional = true; SoundAction.vWorldPosition = AbilityContext.InputContext.TargetLocations[0]; OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(SoundAction); //Make everyone else wait for the detonation for (LoopIdx = 0; LoopIdx < OutVisualizationTracks.Length; ++LoopIdx) { if (LoopIdx == ShooterTrackIdx) continue; WaitAction = X2Action_WaitForAbilityEffect(class'X2Action_WaitForAbilityEffect'.static.CreateVisualizationAction(AbilityContext)); OutVisualizationTracks[LoopIdx].TrackActions.InsertItem(0, WaitAction); MessageAction = X2Action_SendInterTrackMessage(class'X2Action_SendInterTrackMessage'.static.CreateVisualizationAction(AbilityContext)); MessageAction.SendTrackMessageToRef = OutVisualizationTracks[LoopIdx].StateObject_NewState.GetReference(); OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(MessageAction); } //Keep the camera there after things blow up DelayAction = X2Action_Delay(class'X2Action_Delay'.static.CreateVisualizationAction(AbilityContext)); DelayAction.Duration = 0.5; OutVisualizationTracks[ShooterTrackIdx].TrackActions.AddItem(DelayAction); } function bool GrenadeDamagePreview(XComGameState_Ability AbilityState, StateObjectReference TargetRef, out WeaponDamageValue MinDamagePreview, out WeaponDamageValue MaxDamagePreview, out int AllowsShield) { local XComGameState_Item ItemState; local X2GrenadeTemplate GrenadeTemplate; local XComGameState_Ability DetonationAbility; local XComGameState_Unit SourceUnit; local XComGameStateHistory History; local StateObjectReference AbilityRef; ItemState = AbilityState.GetSourceAmmo(); if (ItemState == none) ItemState = AbilityState.GetSourceWeapon(); if (ItemState == none) return false; GrenadeTemplate = X2GrenadeTemplate(ItemState.GetMyTemplate()); if (GrenadeTemplate == none) return false; if (GrenadeTemplate.DataName != 'ProximityStunMine') return false; History = `XCOMHISTORY; SourceUnit = XComGameState_Unit(History.GetGameStateForObjectID(AbilityState.OwnerStateObject.ObjectID)); AbilityRef = SourceUnit.FindAbility(default.ProximityStunMineDetonationAbilityName); DetonationAbility = XComGameState_Ability(History.GetGameStateForObjectID(AbilityRef.ObjectID)); if (DetonationAbility == none) return false; DetonationAbility.GetDamagePreview(TargetRef, MinDamagePreview, MaxDamagePreview, AllowsShield); return true; } DefaultProperties { ProximityStunMineDetonationAbilityName = "ProximityStunMineDetonation" } Redscreen error still shows up though, but doesn't seem to cause any problems aside from having to wait for units to go into their stunned animation. Edited November 3, 2016 by Shuma22 Link to comment Share on other sites More sharing options...
robojumper Posted November 3, 2016 Share Posted November 3, 2016 The redscreen is a bug somewhere in the vanilla visualizers. You can use the console command X2DebugVisualizers and take a screenshot BEFORE the unit triggers the explosion, but after it starts to move, and find out what exactly goes wrong, but I'm afraid I don't know the exact cause of that. Regarding that armor migitation thing - it's right there, in X2Effect_ApplyWeaponDamage.uc if (ArmorMitigation >= WeaponDamage) ArmorMitigation = WeaponDamage - 1; Link to comment Share on other sites More sharing options...
Recommended Posts