- Joined
- Jan 9, 2005
- Messages
- 2,126
UPDATE: Working code. Requires this in a separate trigger to work:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
I want to make a function repeat X times with Y seconds interval time. More specifically, I want to repeat the Actions (function Trig_VolleyAttacks_Actions takes nothing returns nothing):
As you can see, I've tried using TimerUtils to do that but I do understand timers or how to use them, so you can imagine how well that went. If you further up, you will see a struct that was commented out - this was supposed to hold the content of Trig_VolleyAttacks_Actions but I don't know how to do that either.
Basically what needs to happen is that if the variable udg_VA_Config_NumberOfVolleys[id] is higher than 1, then create a timer that repeats as many times as udg_VA_Config_NumberOfVolleys[id], with a time of udg_VA_Config_TimeBetweenVolleys[id]. Every time it expires, it calls Trig_VolleyAttacks_Actions, or the Struct VolleyCast if you think it's better to have the actions in the struct instead. It needs to be MUI.
PS: Since this resource is going to be rather performance-heavy, squeezing the most performance out of this would be most desireable. TimerTools is less performance-heavy than TimerUtils from what I've read, so if anyone can help me use that instead, it would be greatly appareciated.
JASS:
//! textmacro NewIndexGenerator takes NAME
struct $NAME$
public static method create takes nothing returns VolleyInstance
return .allocate()
endmethod
public method destroy takes nothing returns nothing
call this.deallocate()
endmethod
endstruct
//! endtextmacro
JASS:
scope VolleyAttackScope initializer InitTrig_VolleyAttacks
globals
private trigger VolleyAttacks = CreateTrigger()
private group grp = CreateGroup()
private unit array MissileTarget
private real array MissileScale
private unit array TimerSource
private unit array TimerTarget
private real array TimerDamage
private real array TimerDistance
private integer array TimerCount
endglobals
//========================================================================
// Filtering and AoE Damage Functions
// Filter Alive, Enemies, Vulnerable Targets, and Visible Targets
private function ViableTargets takes integer missile, unit target, player owner returns boolean
return UnitAlive(target) and (IsUnitEnemy(target, owner) or (IsUnitAlly(target, owner) and target == MissileTarget[missile]) ) and /* [Target is Alive and an Enemy]
[Vulnerable] */GetUnitAbilityLevel(target, 'Bvul') <= 0 and GetUnitAbilityLevel(target, 'Avul') <= 0 and /*
[Visible] */not IsUnitInvisible(target, owner)
endfunction
// if target is a ground unit or a structure
private function IsGround takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_GROUND) or IsUnitType(u, UNIT_TYPE_STRUCTURE)// and not IsUnitType(u, UNIT_TYPE_FLYING))
endfunction
// if target is a flying unit
private function IsFlying takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_FLYING) // assuming this include flying buildings?
endfunction
// Damage Ground Units AOE
private function GroundDamage takes unit s, integer m, real AoE, real d, real x, real y, player o, attacktype AttackType returns nothing
local unit FoG=null
call GroupEnumUnitsInRange(grp,x,y,AoE,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitType(FoG, UNIT_TYPE_GROUND) and ViableTargets(m,FoG,o) then
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget( s, FoG, d, true, true, AttackType, DAMAGE_TYPE_UNIVERSAL, null )
call TriggerEvaluate(udg_ClearDamageEvent)
endif
call GroupRemoveUnit(grp,FoG)
endloop
endfunction
// Damage Flying Units AOE
private function FlyingDamage takes unit s, integer m, real AoE, real d, real x, real y, player o, attacktype AttackType returns nothing
local unit FoG=null
call GroupEnumUnitsInRange(grp,x,y,AoE,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitType(FoG, UNIT_TYPE_FLYING) and ViableTargets(m,FoG,o) then
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget( s, FoG, d, true, true, AttackType, DAMAGE_TYPE_UNIVERSAL, null )
call TriggerEvaluate(udg_ClearDamageEvent)
endif
call GroupRemoveUnit(grp,FoG)
endloop
endfunction
// Damage Ground and Flying simultaneously
private function AllDamage takes unit s, integer m, real AoE, real d, real x, real y, player o, attacktype AttackType returns nothing
local unit FoG=null
call GroupEnumUnitsInRange(grp,x,y,AoE,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if ViableTargets(m,FoG,o) then
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget( s, FoG, d, true, true, AttackType, DAMAGE_TYPE_UNIVERSAL, null )
call TriggerEvaluate(udg_ClearDamageEvent)
endif
call GroupRemoveUnit(grp,FoG)
endloop
endfunction
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/* VOLLEY MISSILES */
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
private struct VolleyAttack extends array
//========================================================================
// onDestructable
static method onDestructable takes Missile this, destructable hit returns boolean
local integer id = GetUnitUserData(this.source)
if udg_VA_Config_AllowDESTRUCTIBLE[id] then
call UnitDamageTarget(this.source, hit, this.damage, true, true, udg_VA_Config_AttackType[id], DAMAGE_TYPE_UNIVERSAL, null)
if udg_VA_Config_DeathOnCollision[id] then
return true
else
return false
endif
endif
return false
endmethod
//========================================================================
// onCollide
private static method onCollide takes Missile this, unit hit returns boolean
local integer id = GetUnitUserData(this.source)
if udg_VA_Config_MissileCollisionSize[id] > 0.00 then
if ViableTargets(this, hit, this.owner) then
// if missile can only hit ground units or structures
if udg_VA_AllowGROUND[this] and not udg_VA_AllowFLYING[this] and IsGround(hit) then
if udg_VA_Config_MissileImpactAOE[id] > 0.00 then
call GroundDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner, udg_VA_Config_AttackType[id])
else
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget(this.source, hit, this.damage, true, true, udg_VA_Config_AttackType[id], DAMAGE_TYPE_UNIVERSAL, null)
call TriggerEvaluate(udg_ClearDamageEvent)
endif
if udg_VA_Config_DeathOnCollision[id] then
return true
else
return false
endif
// if missile can only hit flying units
elseif not udg_VA_AllowGROUND[this] and udg_VA_AllowFLYING[this] and IsFlying(hit) then
if udg_VA_Config_MissileImpactAOE[id] > 0.00 then
call FlyingDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner, udg_VA_Config_AttackType[id])
else
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget(this.source, hit, this.damage, true, true, udg_VA_Config_AttackType[id], DAMAGE_TYPE_UNIVERSAL, null)
call TriggerEvaluate(udg_ClearDamageEvent)
endif
if udg_VA_Config_DeathOnCollision[id] then
return true
else
return false
endif
// if missile can hit both ground units (and structures) and flying units. This is in the event a unit is both ground and air.
elseif udg_VA_AllowGROUND[this] and udg_VA_AllowFLYING[this] and IsFlying(hit) or IsGround(hit) then
if udg_VA_Config_MissileImpactAOE[id] > 0.00 then
call AllDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner, udg_VA_Config_AttackType[id])
else
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget(this.source, hit, this.damage, true, true, udg_VA_Config_AttackType[id], DAMAGE_TYPE_UNIVERSAL, null)
call TriggerEvaluate(udg_ClearDamageEvent)
endif
if udg_VA_Config_DeathOnCollision[id] then
return true
else
return false
endif
endif
return false
endif
endif
return false
endmethod
//========================================================================
// onPeriod
private static method onPeriod takes Missile this returns boolean
local integer id = GetUnitUserData(this.source)
if udg_VA_Config_MissileScale_INC[id] != 0.00 then
set MissileScale[this] = MissileScale[this] + udg_VA_Config_MissileScale_INC[id]
call SetUnitScale(this.dummy, MissileScale[this], MissileScale[this], MissileScale[this])
endif
if udg_VA_Config_MissileCollision_INC[id] != 0.00 then
set this.collision = this.collision + udg_VA_Config_MissileCollision_INC[id]
endif
return false
endmethod
//========================================================================
// onRemove
private static method onRemove takes Missile this returns boolean
local integer id = GetUnitUserData(this.source)
local effect fx = null
//call SetUnitScale(this.dummy, 1.00, 1.00, 1.00)
if udg_VA_Config_MissileDeath_String[id] != null then
// if the death art is
if udg_VA_Config_DeathArtOnGround[id] then
set fx = AddSpecialEffect(udg_VA_Config_MissileDeath_String[id], GetUnitX(this.dummy), GetUnitY(this.dummy))
else
set fx = AddSpecialEffectTarget(udg_VA_Config_MissileDeath_String[id], this.dummy, "origin")
endif
call DestroyEffect(fx)
endif
return false
endmethod
//========================================================================
// onFinish
private static method onFinish takes Missile this returns boolean
local integer id = GetUnitUserData(this.source)
if udg_VA_Config_MissileImpactAOE[id] > 0.00 then
if udg_VA_AllowGROUND[this] and not udg_VA_AllowFLYING[this] then
call GroundDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner, udg_VA_Config_AttackType[id])
elseif not udg_VA_AllowGROUND[this] and udg_VA_AllowFLYING[this] then
call FlyingDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner, udg_VA_Config_AttackType[id])
elseif udg_VA_AllowGROUND[this] and udg_VA_AllowFLYING[this] then
call AllDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner, udg_VA_Config_AttackType[id])
endif
else
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget( this.source, this.target, this.damage, true, true, udg_VA_Config_AttackType[id], DAMAGE_TYPE_UNIVERSAL, null )
call TriggerEvaluate(udg_ClearDamageEvent)
endif
return true
endmethod
implement MissileStruct
endstruct
//======================================================================================================================================
//======================================================================================================================================
function VolleyCaster takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer tID = GetTimerData(t)
local integer id = GetUnitUserData(TimerSource[tID])
local real SOURCE_X = GetUnitX(TimerSource[tID])
local real SOURCE_Y = GetUnitY(TimerSource[tID])
local real TARGET_X = GetUnitX(TimerTarget[tID])
local real TARGET_Y = GetUnitY(TimerTarget[tID])
local real Facing = Atan2(TARGET_Y - SOURCE_Y, TARGET_X - SOURCE_X)
local real AngleCorrection = Facing + udg_VA_Config_LaunchAngle[id] * bj_DEGTORAD
local real SpreadAngle = 0.00
local real LAUNCH_X = 0.00
local real LAUNCH_Y = 0.00
local real IMPACT_X = 0.00
local real IMPACT_Y = 0.00
local real MissileAngle = 0.00
local real MinDist = udg_VA_Config_MissileDistance[id] - udg_VA_Config_MissileDistanceVary[id]
local real MaxDist = udg_VA_Config_MissileDistance[id] + udg_VA_Config_MissileDistanceVary[id]
local real MinHeight = udg_VA_Config_MissileEndHeight[id] - udg_VA_Config_MissileEndHeightVary[id] + GetUnitFlyHeight(TimerTarget[tID])
local real MaxHeight = udg_VA_Config_MissileEndHeight[id] + udg_VA_Config_MissileEndHeightVary[id] + GetUnitFlyHeight(TimerTarget[tID])
local real dx = 0.00
local real dy = 0.00
local real MissileDistance = 0.00
local integer MissileCount = 0
local VolleyInstance volley = tID
local Missile m
set TimerDistance[tID] = TimerDistance[tID] + udg_VA_Config_MissileDistance_INC[id]
loop //Missiles Per Volley Loop
set MissileCount = MissileCount + 1
exitwhen MissileCount > udg_VA_Config_MissilesPerVolley[id]
set SpreadAngle = Deg2Rad(Facing + GetRandomReal(-90, 90))
if udg_VA_Config_IgnoreTargetDistance[id] then
set TARGET_X = SOURCE_X + Cos(Facing) * (GetRandomReal(MinDist, MaxDist) + TimerDistance[tID])
set TARGET_Y = SOURCE_Y + Sin(Facing) * (GetRandomReal(MinDist, MaxDist) + TimerDistance[tID])
else
set TARGET_X = TARGET_X + Cos(Facing) * TimerDistance[tID]
set TARGET_Y = TARGET_Y + Sin(Facing) * TimerDistance[tID]
endif
set LAUNCH_X = SOURCE_X + Cos(AngleCorrection) * udg_VA_Config_LaunchOffset[id]
set LAUNCH_Y = SOURCE_Y + Sin(AngleCorrection) * udg_VA_Config_LaunchOffset[id]
set IMPACT_X = TARGET_X + Cos(SpreadAngle) * GetRandomReal(0, udg_VA_Config_MissileSpread[id])
set IMPACT_Y = TARGET_Y + Sin(SpreadAngle) * GetRandomReal(0, udg_VA_Config_MissileSpread[id])
set dx = LAUNCH_X - TARGET_X
set dx = LAUNCH_Y - TARGET_Y
set MissileDistance = SquareRoot(dy*dy+dx*dx)
set MissileAngle = Atan2(TARGET_Y - LAUNCH_Y, TARGET_X - LAUNCH_X)
if udg_VA_Config_IsMissileGrounded[id] then
set MinHeight = 0.00
set MaxHeight = 0.00
endif
if udg_VA_Config_IsMissileHoming[id] then
set m = Missile.create(LAUNCH_X, LAUNCH_Y, udg_VA_Config_LaunchHeight[id], MissileAngle, MissileDistance,/*
[Height] */ GetRandomReal(MinHeight, MaxHeight) )
set m.target = TimerTarget[tID]
else
set m = Missile.createXYZ(LAUNCH_X, LAUNCH_Y, udg_VA_Config_LaunchHeight[id] + GetUnitFlyHeight(TimerSource[tID]), IMPACT_X, IMPACT_Y,/*
[Height] */ GetRandomReal(MinHeight, MaxHeight) )
endif
/*call SetUnitPathing(m.dummy, false)
call SetUnitX(m.dummy, LAUNCH_X)
call SetUnitY(m.dummy, LAUNCH_Y)*/
set m.speed = GetRandomReal(udg_VA_Config_MissileSpeed_MIN[id], udg_VA_Config_MissileSpeed_MAX[id])
set m.acceleration = udg_VA_Config_MissileAcceleration[id]
set m.scale = GetRandomReal(udg_VA_Config_MissileScale_MIN[id], udg_VA_Config_MissileScale_MAX[id])
set m.collision = udg_VA_Config_MissileCollisionSize[id]
set m.damage = TimerDamage[tID]
set m.model = udg_VA_Config_MissileArtString[id]
set m.arc = GetRandomReal(udg_VA_Config_MissileArc_MIN[id], udg_VA_Config_MissileArc_MAX[id]) * bj_DEGTORAD
set m.curve = GetRandomReal(udg_VA_Config_MissileCurve_MIN[id], udg_VA_Config_MissileCurve_MAX[id]) * bj_DEGTORAD
set m.source = TimerSource[tID]
set m.owner = GetOwningPlayer(TimerSource[tID])
set MissileScale[m] = m.scale
set MissileTarget[m] = TimerTarget[tID]
if udg_VA_Config_HitAllTargets[id] then
set udg_VA_AllowFLYING[m] = true
set udg_VA_AllowGROUND[m] = true
else
if IsGround(TimerTarget[tID]) then
set udg_VA_AllowGROUND[m] = true
else
set udg_VA_AllowFLYING[m] = true
endif
endif
call VolleyAttack.launch(m)
endloop
set TimerCount[tID] = TimerCount[tID] - 1
if TimerCount[tID] <= 0 or not UnitAlive(TimerSource[tID]) then
call ReleaseTimer(t)
call volley.destroy()
endif
endfunction
//CONDITIONS
function Trig_VolleyAttacks_Conditions takes nothing returns boolean
if udg_VA_UsesVolleyAttack[GetUnitUserData(udg_DamageEventSource)] and udg_DamageEventType == 0 and not udg_IsDamageSpell then
return true
endif
return false
endfunction
//ACTIONS
function Trig_VolleyAttacks_Actions takes nothing returns nothing
// Because volleys are on a timer, the first volley would be delayed, so the first volley launches in the actions.
// This also removed the need to have a timer for units that launch just 1 volley, like the Rifleman
local timer t = GetExpiredTimer()
local integer tID = VolleyInstance.create()
local integer id = GetUnitUserData(udg_DamageEventSource)
local real SOURCE_X = GetUnitX(udg_DamageEventSource)
local real SOURCE_Y = GetUnitY(udg_DamageEventSource)
local real TARGET_X = GetUnitX(udg_DamageEventTarget)
local real TARGET_Y = GetUnitY(udg_DamageEventTarget)
local real Facing = Atan2(TARGET_Y - SOURCE_Y, TARGET_X - SOURCE_X)
local real AngleCorrection = Facing + Deg2Rad(udg_VA_Config_LaunchAngle[id])
local real SpreadAngle = 0.00
local real LAUNCH_X = 0.00
local real LAUNCH_Y = 0.00
local real IMPACT_X = 0.00
local real IMPACT_Y = 0.00
local real MissileAngle = 0.00
local real MinDist = udg_VA_Config_MissileDistance[id] - udg_VA_Config_MissileDistanceVary[id]
local real MaxDist = udg_VA_Config_MissileDistance[id] + udg_VA_Config_MissileDistanceVary[id]
local real MinHeight = udg_VA_Config_MissileEndHeight[id] - udg_VA_Config_MissileEndHeightVary[id] + GetUnitFlyHeight(udg_DamageEventTarget)
local real MaxHeight = udg_VA_Config_MissileEndHeight[id] + udg_VA_Config_MissileEndHeightVary[id] + GetUnitFlyHeight(udg_DamageEventTarget)
local real dx = 0.00
local real dy = 0.00
local real MissileDistance = 0.00
local integer MissileCount = 0
local Missile m
set TimerDistance[tID] = udg_VA_Config_MissileDistance_INC[id]
//call BJDebugMsg(R2S(GetFullDamage(udg_DamageEventPrevAmt, GetUnitArmor(udg_DamageEventTarget))))
//call BJDebugMsg(R2S(GetReducedDamage(udg_DamageEventPrevAmt, GetUnitArmor(udg_DamageEventTarget))))
if udg_VA_Config_SplitDmgBwMissiles[id] and not udg_VA_Config_SplitDmgBwVolleys[id] then
set TimerDamage[tID] = GetFullDamage(udg_DamageEventPrevAmt, GetUnitArmor(udg_DamageEventTarget)) / udg_VA_Config_MissilesPerVolley[id]
elseif not udg_VA_Config_SplitDmgBwMissiles[id] and udg_VA_Config_SplitDmgBwVolleys[id] then
set TimerDamage[tID] = GetFullDamage(udg_DamageEventPrevAmt, GetUnitArmor(udg_DamageEventTarget)) / udg_VA_Config_NumberOfVolleys[id]
elseif udg_VA_Config_SplitDmgBwMissiles[id] and udg_VA_Config_SplitDmgBwVolleys[id] then
set TimerDamage[tID] = (GetFullDamage(udg_DamageEventPrevAmt, GetUnitArmor(udg_DamageEventTarget)) / udg_VA_Config_MissilesPerVolley[id]) / udg_VA_Config_NumberOfVolleys[id]
elseif not udg_VA_Config_SplitDmgBwMissiles[id] and not udg_VA_Config_SplitDmgBwVolleys[id] then
set TimerDamage[tID] = GetFullDamage(udg_DamageEventPrevAmt, GetUnitArmor(udg_DamageEventTarget))
endif
loop //Missiles Per Volley Loop
set MissileCount = MissileCount + 1
exitwhen MissileCount > udg_VA_Config_MissilesPerVolley[id]
set SpreadAngle = Deg2Rad(Facing + GetRandomReal(-90, 90))
if udg_VA_Config_IgnoreTargetDistance[id] then
set TARGET_X = SOURCE_X + Cos(Facing) * (GetRandomReal(MinDist, MaxDist) + TimerDistance[tID])
set TARGET_Y = SOURCE_Y + Sin(Facing) * (GetRandomReal(MinDist, MaxDist) + TimerDistance[tID])
else
set TARGET_X = TARGET_X + Cos(Facing) * TimerDistance[tID]
set TARGET_Y = TARGET_Y + Sin(Facing) * TimerDistance[tID]
endif
set LAUNCH_X = SOURCE_X + Cos(AngleCorrection) * udg_VA_Config_LaunchOffset[id]
set LAUNCH_Y = SOURCE_Y + Sin(AngleCorrection) * udg_VA_Config_LaunchOffset[id]
set IMPACT_X = TARGET_X + Cos(SpreadAngle) * GetRandomReal(0, udg_VA_Config_MissileSpread[id])
set IMPACT_Y = TARGET_Y + Sin(SpreadAngle) * GetRandomReal(0, udg_VA_Config_MissileSpread[id])
set dx = LAUNCH_X - TARGET_X
set dx = LAUNCH_Y - TARGET_Y
set MissileDistance = SquareRoot(dy*dy+dx*dx)
set MissileAngle = Atan2(TARGET_Y - LAUNCH_Y, TARGET_X - LAUNCH_X)
if udg_VA_Config_IsMissileGrounded[id] then
set MinHeight = 0.00
set MaxHeight = 0.00
endif
if udg_VA_Config_IsMissileHoming[id] then
set m = Missile.create(LAUNCH_X, LAUNCH_Y, udg_VA_Config_LaunchHeight[id], MissileAngle, MissileDistance,/*
[Height] */ GetRandomReal(MinHeight, MaxHeight) )
set m.target = udg_DamageEventTarget
else
set m = Missile.createXYZ(LAUNCH_X, LAUNCH_Y, udg_VA_Config_LaunchHeight[id] + GetUnitFlyHeight(udg_DamageEventSource), IMPACT_X, IMPACT_Y,/*
[Height] */ GetRandomReal(MinHeight, MaxHeight) )
endif
/*call SetUnitPathing(m.dummy, false)
call SetUnitX(m.dummy, LAUNCH_X)
call SetUnitY(m.dummy, LAUNCH_Y)*/
set m.speed = GetRandomReal(udg_VA_Config_MissileSpeed_MIN[id], udg_VA_Config_MissileSpeed_MAX[id])
set m.acceleration = udg_VA_Config_MissileAcceleration[id]
set m.scale = GetRandomReal(udg_VA_Config_MissileScale_MIN[id], udg_VA_Config_MissileScale_MAX[id])
set m.collision = udg_VA_Config_MissileCollisionSize[id]
set m.damage = TimerDamage[tID]
set m.model = udg_VA_Config_MissileArtString[id]
set m.arc = GetRandomReal(udg_VA_Config_MissileArc_MIN[id], udg_VA_Config_MissileArc_MAX[id]) * bj_DEGTORAD
set m.curve = GetRandomReal(udg_VA_Config_MissileCurve_MIN[id], udg_VA_Config_MissileCurve_MAX[id]) * bj_DEGTORAD
set m.source = udg_DamageEventSource
set m.owner = GetOwningPlayer(udg_DamageEventSource)
set MissileScale[m] = m.scale
set MissileTarget[m] = udg_DamageEventTarget
if udg_VA_Config_HitAllTargets[id] then
set udg_VA_AllowFLYING[m] = true
set udg_VA_AllowGROUND[m] = true
else
if IsGround(udg_DamageEventTarget) then
set udg_VA_AllowGROUND[m] = true
else
set udg_VA_AllowFLYING[m] = true
endif
endif
call VolleyAttack.launch(m)
endloop
set udg_DamageEventAmount = 0.00
if udg_VA_Config_NumberOfVolleys[id] > 1 then
set t = NewTimerEx(tID)
set TimerSource[tID] = udg_DamageEventSource
set TimerTarget[tID] = udg_DamageEventTarget
set TimerCount[tID] = udg_VA_Config_NumberOfVolleys[id] - 1
call TimerStart(t, udg_VA_Config_TimeBetweenVolleys[id], true, function VolleyCaster)
set t = null
endif
endfunction
//Events
function InitTrig_VolleyAttacks takes nothing returns nothing
call TriggerRegisterVariableEvent( VolleyAttacks, "udg_DamageModifierEvent", EQUAL, 1.00 )
call TriggerAddCondition( VolleyAttacks, Condition(function Trig_VolleyAttacks_Conditions) )
call TriggerAddAction( VolleyAttacks, function Trig_VolleyAttacks_Actions )
endfunction
endscope
//! runtextmacro NewIndexGenerator("VolleyInstance")
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
I want to make a function repeat X times with Y seconds interval time. More specifically, I want to repeat the Actions (function Trig_VolleyAttacks_Actions takes nothing returns nothing):
JASS:
scope VolleyAttackScope initializer InitTrig_VolleyAttacks
globals
public trigger VolleyAttacks = CreateTrigger()
public group grp = CreateGroup()
public unit array MissileTarget
public real array MissileScale
endglobals
//========================================================================
// Filtering and AoE Damage Functions
// Filter Alive, Enemies, Vulnerable Targets, and Visible Targets
private function ViableTargets takes integer missile, unit target, player owner returns boolean
return UnitAlive(target) and (IsUnitEnemy(target, owner) or (IsUnitAlly(target, owner) and target == MissileTarget[missile]) ) and /* [Target is Alive and an Enemy]
[Vulnerable] */GetUnitAbilityLevel(target, 'Bvul') <= 0 and GetUnitAbilityLevel(target, 'Avul') <= 0 and /*
[Visible] */not IsUnitInvisible(target, owner)
endfunction
// if target is a ground unit or a structure
private function IsGround takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_GROUND) or IsUnitType(u, UNIT_TYPE_STRUCTURE)// and not IsUnitType(u, UNIT_TYPE_FLYING))
endfunction
// if target is a flying unit
private function IsFlying takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_FLYING) // assuming this include flying buildings?
endfunction
// Damage Ground Units AOE
private function GroundDamage takes unit s, integer m, real AoE, real d, real x, real y, player o returns nothing
local unit FoG=null
call GroupEnumUnitsInRange(grp,x,y,AoE,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitType(FoG, UNIT_TYPE_GROUND) and ViableTargets(m,FoG,o) then
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget( s, FoG, d, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null )
call TriggerEvaluate(udg_ClearDamageEvent)
endif
call GroupRemoveUnit(grp,FoG)
endloop
endfunction
// Damage Flying Units AOE
private function FlyingDamage takes unit s, integer m, real AoE, real d, real x, real y, player o returns nothing
local unit FoG=null
call GroupEnumUnitsInRange(grp,x,y,AoE,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitType(FoG, UNIT_TYPE_FLYING) and ViableTargets(m,FoG,o) then
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget( s, FoG, d, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null )
call TriggerEvaluate(udg_ClearDamageEvent)
endif
call GroupRemoveUnit(grp,FoG)
endloop
endfunction
// Damage Ground and Flying simultaneously
private function AllDamage takes unit s, integer m, real AoE, real d, real x, real y, player o returns nothing
local unit FoG=null
call GroupEnumUnitsInRange(grp,x,y,AoE,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if ViableTargets(m,FoG,o) then
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget( s, FoG, d, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null )
call TriggerEvaluate(udg_ClearDamageEvent)
endif
call GroupRemoveUnit(grp,FoG)
endloop
endfunction
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/* VOLLEY MISSILES */
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
private struct VolleyAttack extends array
//========================================================================
// onDestructable
static method onDestructable takes Missile this, destructable hit returns boolean
local integer id = GetUnitUserData(this.source)
if udg_VA_Config_AllowDESTRUCTIBLE[id] then
call UnitDamageTarget(this.source, hit, this.damage, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
if udg_VA_Config_DeathOnCollision[id] then
return true
else
return false
endif
endif
return false
endmethod
//========================================================================
// onCollide
private static method onCollide takes Missile this, unit hit returns boolean
local integer id = GetUnitUserData(this.source)
if udg_VA_Config_MissileCollisionSize[id] > 0.00 then
if ViableTargets(this, hit, this.owner) then
// if missile can only hit ground units or structures
if udg_VA_AllowGROUND[this] and not udg_VA_AllowFLYING[this] and IsGround(hit) then
if udg_VA_Config_MissileImpactAOE[id] > 0.00 then
call GroundDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner)
else
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget(this.source, hit, this.damage, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
call TriggerEvaluate(udg_ClearDamageEvent)
endif
if udg_VA_Config_DeathOnCollision[id] then
return true
else
return false
endif
// if missile can only hit flying units
elseif not udg_VA_AllowGROUND[this] and udg_VA_AllowFLYING[this] and IsFlying(hit) then
if udg_VA_Config_MissileImpactAOE[id] > 0.00 then
call FlyingDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner)
else
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget(this.source, hit, this.damage, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
call TriggerEvaluate(udg_ClearDamageEvent)
endif
if udg_VA_Config_DeathOnCollision[id] then
return true
else
return false
endif
// if missile can hit both ground units (and structures) and flying units. This is in the event a unit is both ground and air.
elseif udg_VA_AllowGROUND[this] and udg_VA_AllowFLYING[this] and IsFlying(hit) or IsGround(hit) then
if udg_VA_Config_MissileImpactAOE[id] > 0.00 then
call AllDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner)
else
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget(this.source, hit, this.damage, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
call TriggerEvaluate(udg_ClearDamageEvent)
endif
if udg_VA_Config_DeathOnCollision[id] then
return true
else
return false
endif
endif
return false
endif
endif
return false
endmethod
//========================================================================
// onPeriod
private static method onPeriod takes Missile this returns boolean
local integer id = GetUnitUserData(this.source)
if udg_VA_Config_MissileScale_INC[id] != 0.00 then
set MissileScale[this] = MissileScale[this] + udg_VA_Config_MissileScale_INC[id]
call SetUnitScale(this.dummy, MissileScale[this], MissileScale[this], MissileScale[this])
endif
if udg_VA_Config_MissileCollision_INC[id] != 0.00 then
set this.collision = this.collision + udg_VA_Config_MissileCollision_INC[id]
endif
return false
endmethod
//========================================================================
// onRemove
private static method onRemove takes Missile this returns boolean
local integer id = GetUnitUserData(this.source)
local effect fx = null
//call SetUnitScale(this.dummy, 1.00, 1.00, 1.00)
if udg_VA_Config_MissileDeath_String[id] != null then
// if the death art is
if udg_VA_Config_DeathArtOnGround[id] then
set fx = AddSpecialEffect(udg_VA_Config_MissileDeath_String[id], GetUnitX(this.dummy), GetUnitY(this.dummy))
else
set fx = AddSpecialEffectTarget(udg_VA_Config_MissileDeath_String[id], this.dummy, "origin")
endif
call DestroyEffect(fx)
endif
return false
endmethod
//========================================================================
// onFinish
private static method onFinish takes Missile this returns boolean
local integer id = GetUnitUserData(this.source)
if udg_VA_Config_MissileImpactAOE[id] > 0.00 then
if udg_VA_AllowGROUND[this] and not udg_VA_AllowFLYING[this] then
call GroundDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner)
elseif not udg_VA_AllowGROUND[this] and udg_VA_AllowFLYING[this] then
call FlyingDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner)
elseif udg_VA_AllowGROUND[this] and udg_VA_AllowFLYING[this] then
call AllDamage(this.source, this, udg_VA_Config_MissileImpactAOE[id], this.damage, this.x, this.y, this.owner)
endif
else
set udg_NextDamageType = udg_DamageTypeCode
call UnitDamageTarget( this.source, this.target, this.damage, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null )
call TriggerEvaluate(udg_ClearDamageEvent)
endif
return true
endmethod
implement MissileStruct
endstruct
//======================================================================================================================================
//======================================================================================================================================
//I've disabled this part because I don't know if it's necessary
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/* VOLLEY CAST */
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/*private struct VolleyCast extends array
static method VolleyCast takes unit source, unit target, real damage, timer TIMER returns nothing
endmethod
endstruct*/
//======================================================================================================================================
//======================================================================================================================================
/*function VolleyCaster takes unit source, unit target, real damage, integer id, integer VolleyCount, timer TIMER returns nothing
endfunction*/
//CONDITIONS
function Trig_VolleyAttacks_Conditions takes nothing returns boolean
if udg_VA_UsesVolleyAttack[GetUnitUserData(udg_DamageEventSource)] and udg_DamageEventType == 0 and not udg_IsDamageSpell then
return true
endif
return false
endfunction
//ACTIONS
function Trig_VolleyAttacks_Actions takes nothing returns nothing
//call VolleyCast(udg_DamageEventSource, udg_DamageEventTarget, udg_DamageEventPrevAmt, GetUnitUserData(udg_DamageEventSource)
local integer id = GetUnitUserData(udg_DamageEventSource)
local timer t = CreateTimer()
local real SOURCE_X = GetUnitX(udg_DamageEventSource)
local real SOURCE_Y = GetUnitY(udg_DamageEventSource)
local real TARGET_X = GetUnitX(udg_DamageEventTarget)
local real TARGET_Y = GetUnitY(udg_DamageEventTarget)
local real Facing = Atan2(TARGET_Y - SOURCE_Y, TARGET_X - SOURCE_X)
local real AngleCorrection = Facing + udg_VA_Config_LaunchAngle[id] * bj_DEGTORAD
local real SpreadAngle = 0.00
local real LAUNCH_X = 0.00
local real LAUNCH_Y = 0.00
local real IMPACT_X = 0.00
local real IMPACT_Y = 0.00
local real MissileAngle = 0.00
local real MinDist = udg_VA_Config_MissileDistance[id] - udg_VA_Config_MissileDistanceVary[id]
local real MaxDist = udg_VA_Config_MissileDistance[id] + udg_VA_Config_MissileDistanceVary[id]
local real MinHeight = udg_VA_Config_MissileEndHeight[id] - udg_VA_Config_MissileEndHeightVary[id] + GetUnitFlyHeight(udg_DamageEventTarget)
local real MaxHeight = udg_VA_Config_MissileEndHeight[id] + udg_VA_Config_MissileEndHeightVary[id] + GetUnitFlyHeight(udg_DamageEventTarget)
local real dx = 0.00
local real dy = 0.00
local real MissileDistance = 0.00
local integer VolleyCount = 1
local integer MissileCount = 0
local Missile m
set udg_DamageEventAmount = 0.00
loop //Missiles Per Volley Loop
set MissileCount = MissileCount + 1
exitwhen MissileCount > udg_VA_Config_MissilesPerVolley[id]
set SpreadAngle = Deg2Rad(Facing + GetRandomReal(-90, 90))
if udg_VA_Config_IgnoreTargetDistance[id] then
set TARGET_X = SOURCE_X + Cos(Facing) * GetRandomReal(MinDist, MaxDist)
set TARGET_Y = SOURCE_Y + Sin(Facing) * GetRandomReal(MinDist, MaxDist)
endif
set LAUNCH_X = SOURCE_X + Cos(Facing + AngleCorrection) * udg_VA_Config_LaunchOffset[id]
set LAUNCH_Y = SOURCE_Y + Sin(Facing + AngleCorrection) * udg_VA_Config_LaunchOffset[id]
set IMPACT_X = TARGET_X + Cos(SpreadAngle) * GetRandomReal(0, udg_VA_Config_MissileSpread[id])
set IMPACT_Y = TARGET_Y + Sin(SpreadAngle) * GetRandomReal(0, udg_VA_Config_MissileSpread[id])
set dx = LAUNCH_X - TARGET_X
set dx = LAUNCH_Y - TARGET_Y
set MissileDistance = SquareRoot(dy*dy+dx*dx)
set MissileAngle = Atan2(TARGET_Y - LAUNCH_Y, TARGET_X - LAUNCH_X)
if udg_VA_Config_IsMissileGrounded[id] then
set MinHeight = 0.00
set MaxHeight = 0.00
endif
if udg_VA_Config_IsMissileHoming[id] then
set m = Missile.create(LAUNCH_X, LAUNCH_Y, udg_VA_Config_LaunchHeight[id], MissileAngle, MissileDistance,/*
[Height] */ GetRandomReal(MinHeight, MaxHeight) )
set m.target = udg_DamageEventTarget
else
set m = Missile.createXYZ(LAUNCH_X, LAUNCH_Y, udg_VA_Config_LaunchHeight[id] + GetUnitFlyHeight(udg_DamageEventSource), IMPACT_X, IMPACT_Y,/*
[Height] */ GetRandomReal(MinHeight, MaxHeight) )
endif
set m.speed = GetRandomReal(udg_VA_Config_MissileSpeed_MIN[id], udg_VA_Config_MissileSpeed_MAX[id])
set m.acceleration = udg_VA_Config_MissileAcceleration[id]
set m.scale = GetRandomReal(udg_VA_Config_MissileScale_MIN[id], udg_VA_Config_MissileScale_MAX[id])
set m.collision = udg_VA_Config_MissileCollisionSize[id]
set m.damage = udg_DamageEventPrevAmt / udg_VA_Config_MissilesPerVolley[id]
set m.model = udg_VA_Config_MissileArtString[id]
set m.arc = GetRandomReal(udg_VA_Config_MissileArc_MIN[id], udg_VA_Config_MissileArc_MAX[id]) * bj_DEGTORAD
set m.curve = GetRandomReal(udg_VA_Config_MissileCurve_MIN[id], udg_VA_Config_MissileCurve_MAX[id]) * bj_DEGTORAD
set m.source = udg_DamageEventSource
set m.owner = GetOwningPlayer(udg_DamageEventSource)
set MissileScale[m] = m.scale
set MissileTarget[m] = udg_DamageEventTarget
if udg_VA_Config_HitAllTargets[id] then
set udg_VA_AllowFLYING[m] = true
set udg_VA_AllowGROUND[m] = true
else
if IsGround(udg_DamageEventTarget) then
set udg_VA_AllowGROUND[m] = true
else//if IsFlying(udg_DamageEventTarget) then
set udg_VA_AllowFLYING[m] = true
endif
endif
call VolleyAttack.launch(m)
endloop
//check if there is more than 1 volley. If so, start a loop that will fire a timer for every volley.
if udg_VA_Config_NumberOfVolleys[id] > 1 and t != GetExpiredTimer() then
loop
exitwhen VolleyCount > udg_VA_Config_NumberOfVolleys[id]
set VolleyCount = VolleyCount + 1
set t = NewTimerEx(id)
call SetTimerData(t, id)
call TimerStart(t, udg_VA_Config_TimeBetweenVolleys[id]*VolleyCount, false, function Trig_VolleyAttacks_Actions)
set t = null
endloop
endif
endfunction
//Events
function InitTrig_VolleyAttacks takes nothing returns nothing
call TriggerRegisterVariableEvent( VolleyAttacks, "udg_DamageModifierEvent", EQUAL, 1.00 )
call TriggerAddCondition( VolleyAttacks, Condition(function Trig_VolleyAttacks_Conditions) )
call TriggerAddAction( VolleyAttacks, function Trig_VolleyAttacks_Actions )
endfunction
endscope
As you can see, I've tried using TimerUtils to do that but I do understand timers or how to use them, so you can imagine how well that went. If you further up, you will see a struct that was commented out - this was supposed to hold the content of Trig_VolleyAttacks_Actions but I don't know how to do that either.
Basically what needs to happen is that if the variable udg_VA_Config_NumberOfVolleys[id] is higher than 1, then create a timer that repeats as many times as udg_VA_Config_NumberOfVolleys[id], with a time of udg_VA_Config_TimeBetweenVolleys[id]. Every time it expires, it calls Trig_VolleyAttacks_Actions, or the Struct VolleyCast if you think it's better to have the actions in the struct instead. It needs to be MUI.
PS: Since this resource is going to be rather performance-heavy, squeezing the most performance out of this would be most desireable. TimerTools is less performance-heavy than TimerUtils from what I've read, so if anyone can help me use that instead, it would be greatly appareciated.
Last edited: