Ayanami
칼리
- Reaction score
- 288
Need help finding out why this happens. So basically, I have a spell where it forms a straight path. Then after 1 second, the path explodes, stunning and damaging all enemies within the area of the path. Note that damage is dealt during the stun duration, so over time. Note that I used firebolt with 99999 stun seconds for the stun. I manually remove the stun buff when the stun duration is up.
What I've been wondering is that sometimes, very rarely, the enemies do get stunned, however, they don't receive damage and they're stunned forever. Can't seem to figure out where I went wrong. Thanks in advance!
What I've been wondering is that sometimes, very rarely, the enemies do get stunned, however, they don't receive damage and they're stunned forever. Can't seem to figure out where I went wrong. Thanks in advance!
JASS:
scope AquaField // uses T32, TimerUtils, GroupUtils
// optional Damage
native UnitAlive takes unit id returns boolean
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 039;ABAF039; // raw code of ability "Aqua Field"
private constant integer DUMABILID = 039;AAF0039; // raw code of ability "Aqua Field (Stun)"
private constant integer BUFFID = 039;BAF0039; // raw code of buff "Aqua Field"
private constant integer DUMMYID = 039;duAF039; // raw code of unit "Aqua Field Dummy"
private constant integer CASTERID = 039;cAST039; // raw code of unit "Caster Dummy"
private constant real DINTERVAL = 50.0 // every distance interval where an effect will be created
// smaller value is more accurate, but might cause framerate to drop
private constant string EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // effect used for explosion
private constant boolean USE_DAMAGE = true // true if you want to this spell to support the Damage library
private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage dealt
private constant damagetype DMG = DAMAGE_TYPE_NORMAL // damage type of damage dealt
private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage dealt
endglobals
private function TargetFilter takes unit caster, unit target returns boolean
return IsUnitEnemy(target, GetOwningPlayer(caster)) /* target is an enemy
*/ and UnitAlive(target) /* target is alive
*/ and not IsUnitType(target, UNIT_TYPE_STRUCTURE) /* target is not a structure
*/
endfunction
private function GetArea takes integer level returns real
return 200.0 // radius of field
endfunction
private function GetDamage takes integer level returns real
return 100.0 // damage dealt per second
endfunction
private function GetDelay takes integer level returns real
return 1.0 // delay time before aqua field explodes
endfunction
private function GetDistance takes integer level returns real
return 1000.0 // distance of the field
endfunction
private function GetDuration takes integer level returns real
return 0.6 + (0.4 * level) // duration of stun
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
private struct Data
unit caster
group dummyGroup
group targetGroup
real area
real damage
real duration
static thistype tempData
static hashtable dataHash = InitHashtable()
static unit tempCaster
private static method groupFlush takes nothing returns nothing
local unit u = GetEnumUnit()
call UnitRemoveAbility(u, BUFFID)
call FlushChildHashtable(dataHash, GetHandleId(u))
set u = null
endmethod
private static method periodicFunc takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
if not UnitAlive(u) or GetUnitAbilityLevel(u, BUFFID) == 0 then
call GroupRemoveUnit(this.targetGroup, u)
call FlushChildHashtable(dataHash, GetHandleId(u))
else
static if USE_DAMAGE then
call UnitDamageTargetEx(this.caster, u, this.damage, true, false, ATK, DMG, WEP)
else
call UnitDamageTarget(this.caster, u, this.damage, true, false, ATK, DMG, WEP)
endif
endif
set u = null
endmethod
private method periodic takes nothing returns nothing
if this.duration <= 0 then
call ForGroup(this.targetGroup, function thistype.groupFlush)
call ReleaseGroup(this.targetGroup)
call this.stopPeriodic()
call this.deallocate()
else
set tempData = this
call ForGroup(this.targetGroup, function thistype.periodicFunc)
set this.duration = this.duration - T32_PERIOD
endif
endmethod
implement T32x
private static method filterFunc takes nothing returns boolean
local thistype this = tempData
local thistype that
local unit u = GetFilterUnit()
local integer id = GetHandleId(u)
if TargetFilter(this.caster, u) and not IsUnitInGroup(u, this.targetGroup) then
if HaveSavedInteger(dataHash, id, 0) then
set that = LoadInteger(dataHash, id, 0)
call GroupRemoveUnit(that.targetGroup, u)
endif
call SaveInteger(dataHash, id, 0, this)
call GroupAddUnit(this.targetGroup, u)
call IssueTargetOrder(tempCaster, "firebolt", u)
endif
set u = null
return false
endmethod
private static method groupFunc takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
call DestroyEffect(AddSpecialEffect(EFFECT, x, y))
call GroupEnumUnitsInArea(ENUM_GROUP, x, y, this.area, Filter(function thistype.filterFunc))
call GroupRemoveUnit(this.dummyGroup, u)
call RemoveUnit(u)
set u = null
endmethod
private static method timeOut takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set tempData = this
call ForGroup(this.dummyGroup, function thistype.groupFunc)
call this.startPeriodic()
call ReleaseGroup(this.dummyGroup)
call ReleaseTimer(t)
set t = null
endmethod
private static method actions takes nothing returns boolean
local thistype this
local timer t
local player p
local integer i
local real a
local real x
local real y
local real r
if GetSpellAbilityId() == ABILID then
set this = thistype.allocate()
set this.caster = GetTriggerUnit()
set i = GetUnitAbilityLevel(this.caster, ABILID)
set this.area = GetArea(i)
set this.damage = GetDamage(i) * T32_PERIOD
set this.duration = GetDuration(i)
set this.dummyGroup = NewGroup()
set this.targetGroup = NewGroup()
set p = GetOwningPlayer(this.caster)
set x = GetUnitX(this.caster)
set y = GetUnitY(this.caster)
set a = Atan2(GetSpellTargetY() - y, GetSpellTargetX() - x)
set t = NewTimer()
call SetTimerData(t, this)
call TimerStart(t, GetDelay(i), false, function thistype.timeOut)
set i = R2I(GetDistance(i) / DINTERVAL)
loop
exitwhen i == 0
set r = DINTERVAL * i
call GroupAddUnit(this.dummyGroup, CreateUnit(p, DUMMYID, x + r * Cos(a), y + r * Sin(a), 0))
set i = i - 1
endloop
endif
set t = null
set p = null
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.actions))
set tempCaster = CreateUnit(Player(13), CASTERID, 0, 0, 0)
call UnitAddAbility(tempCaster, DUMABILID)
set t = null
endmethod
endstruct
endscope