Jump to content

Help with abilities and alien gear and stuff and things


Shuma22

Recommended Posts

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

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 by Shuma22
Link to comment
Share on other sites

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

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

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

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 by Shuma22
Link to comment
Share on other sites

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...