[del]As the title says, my jass spell I'm working on stops after some 30+ uses. And I'm having trouble finding the exact cause...I am still relatively new to JASS...so any help would be appreciated. According to the debugger, it is unable to allocate a new id for the struct then causing it to die when trying to destroy a null object. The units still manage to get created at the right angle some how.[/del]
My spell is my favorite off of Diablo 2, Ice Orb, I had to make it myself, I never found one so far to be the same as Diablo 2's.
Btw I looked at Diablo 2's Ice Orb over and over to verify - It is not random. They spawn at a set angle of 35, 50, 55, or any other angle that does not hit 360 normally, and it spawns according to the orb's direction.
My spell is my favorite off of Diablo 2, Ice Orb, I had to make it myself, I never found one so far to be the same as Diablo 2's.
JASS:
// Used ideas from the Frost Sphere by Hanky aka OrkOfMordor aka MDZ-OrkOfMordor and used functions from Vex's Caster System
// Made this one a self contained orb and closer to that of the original Diablo 2
// This is all hand written by me, only copied and pasted Caster System's functions
// Cidzero
scope IceOrb
globals
private constant gamecache cache = InitGameCache("IceOrb")
// These are all you need to change to make the orb go further faster or different end effect
private constant integer DumID = 039;d000039; // Must use the dummy.mdl
private constant integer SpellID = 039;A000039; // Your Spell
private constant integer ColdID = 039;A003039; // Your cold spell (base it off shadow strike, 0 cost, 0 cooldown, 0 casting time, 0 damage, no animation, your buff choice)
private constant real maxOrbDist = 1200
private constant real maxBoltDist = 800
private constant real OrbSpeed = 5
private constant real BoltSpeed = 10
private constant real orbDmg = 0.1
private constant real boltDmg = 15
private constant real specialAngle = 35.0
private constant real height = 50.0
private constant real periodicCheck = 0.02
private constant real spawnRate = 0.10
private constant real boltHitRadius = 50.0
private constant real orbHitRadius = 100.0
private constant real orbSize = 1.00
private constant real boltSize = 0.50
private constant string orbMdl = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
private constant string boltMdl = "Abilities\\Weapons\\LichMissile\\LichMissile.mdl"
private unit tu = null
private real tl = 0
endglobals
//=================== Some functions from Vex's Caster System =========================\\
private function H2I takes handle h returns integer
return h
return 0
endfunction
private function HS takes handle i returns string
return I2S(H2I(i))
endfunction
//=================== Checks the spell being casted =========================\\
private function Conditions takes nothing returns boolean
return (GetSpellAbilityId()==SpellID)
endfunction
//=================== This checks to make sure the unit is not immune to magic (From Vex's Caster System) =========================\\
private function IsUnitNotImmun takes unit c,unit u returns boolean
return ((c!=u) and not (IsUnitType(u,UNIT_TYPE_STRUCTURE)==true) and not (IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)==true) and (IsUnitEnemy(u, GetOwningPlayer(c))==true) and ((GetUnitState(u,UNIT_STATE_LIFE)>0.00)==true))
endfunction
private function notDead takes nothing returns boolean
return (not (IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)==true) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE)==true) and ((GetUnitState(GetFilterUnit(),UNIT_STATE_LIFE)>0.00)==true))
endfunction
//=================== The struct =========================\\
private struct Missile
unit missile
real angle
real dist
real special
real stime
real ttime
real lvl
real x1
real x2
real y1
real y2
effect mdl
timer t
boolean hit
boolean d
method make takes real x, real y, real a,real h, real s, string m, player p, real l returns nothing
set this.missile = CreateUnit(p,DumID,x,y,a)
call UnitAddAbility(this.missile,ColdID)
call SetUnitAbilityLevel(this.missile,ColdID,R2I(l))
call SetUnitFlyHeight(this.missile,h,0)
set this.mdl = AddSpecialEffectTarget(m,this.missile,"chest")
call SetUnitScale(this.missile,s,s,s)
set this.angle = a
set this.dist = 0
set this.special = 0
set this.stime = 0
set this.ttime = 0
set this.lvl = l
set this.hit = false
set this.d = false
set this.t = CreateTimer()
call StoreInteger(cache,HS(this.t),"missile",this)
endmethod
method move takes real speed returns nothing
set this.x1 = GetUnitX(this.missile)
set this.y1 = GetUnitY(this.missile)
set this.x2 = this.x1+speed*Cos(this.angle*bj_DEGTORAD)
set this.y2 = this.y1+speed*Sin(this.angle*bj_DEGTORAD)
if (RectContainsCoords(bj_mapInitialPlayableArea,this.x2,this.y2)==false) then
set this.d = true
else
call SetUnitX(this.missile,this.x2)
call SetUnitY(this.missile,this.y2)
set this.dist = this.dist+speed
endif
endmethod
method remove takes nothing returns nothing
call RemoveUnit(this.missile)
set this.missile = null
call DestroyEffect(this.mdl)
set this.mdl = null
call PauseTimer(this.t)
call FlushStoredMission(cache,HS(this.t))
call DestroyTimer(this.t)
set this.t = null
call this.destroy()
endmethod
endstruct
//=================== This tells the bolts what to damage =========================\\
private function dmgTrgBolt takes nothing returns nothing
local unit t = tu
local real l = tl
local unit u = GetEnumUnit()
if IsUnitNotImmun(t,u) then
call UnitDamageTarget(t,u,(l*boltDmg),false,false,ATTACK_TYPE_MAGIC,DAMAGE_TYPE_COLD,null)
call IssueTargetOrder(t,"shadowstrike",u)
call StoreBoolean(cache,HS(t),"hit",true)
endif
set u = null
set t = null
endfunction
//=================== This tells the orb what to damage =========================\\
private function dmgTrgOrb takes nothing returns nothing
local unit t = tu
local real l = tl
local unit u = GetEnumUnit()
if IsUnitNotImmun(t,u) then
call UnitDamageTarget(t,u,(l*orbDmg),false,false,ATTACK_TYPE_MAGIC,DAMAGE_TYPE_COLD,null)
call IssueTargetOrder(t,"shadowstrike",u)
endif
set u = null
set t = null
endfunction
//=================== This Controls the Bolts =========================\\
private function BoltMotion takes nothing returns nothing
local timer t = GetExpiredTimer()
local Missile bolt = GetStoredInteger(cache,HS(t),"missile")
local group g = CreateGroup()
call bolt.move(BoltSpeed)
call GroupEnumUnitsInRange(g,bolt.x2,bolt.y2,boltHitRadius,Condition(function notDead))
if (CountUnitsInGroup(g)>0) then
set tu = bolt.missile
set tl = bolt.lvl
call ForGroup(g,function dmgTrgBolt)
set bolt.hit = GetStoredBoolean(cache,HS(bolt.missile),"hit")
call FlushStoredMission(cache,HS(bolt.missile))
endif
call DestroyGroup(g)
set g = null
if ((bolt.dist>=maxBoltDist) or bolt.hit or bolt.d) then
call bolt.remove()
else
call TimerStart(t,periodicCheck,false,function BoltMotion)
endif
set t = null
endfunction
//=================== Creates a Bolt without constantly making a Missile =========================\\
private function CreateBolt takes real x, real y, real a,real h, real s, string m, player p, real l returns nothing
local Missile bolt = Missile.create()
call bolt.make(x,y,a,h,s,m,p,l)
call TimerStart(bolt.t,0,false,function BoltMotion)
endfunction
//=================== The final boom as seen in Diablo 2 =========================\\
private function Final takes real x, real y, player p, real l returns nothing
local real c = 1
loop
exitwhen (c>24)
call CreateBolt(x,y,(c*15.0),height,boltSize,boltMdl,p,l)
set c = c + 1
endloop
endfunction
//=================== This controls the orb =========================\\
private function IceOrbMotion takes nothing returns nothing
local timer t = GetExpiredTimer()
local Missile sphere = GetStoredInteger(cache,HS(t),"missile")
local group g = CreateGroup()
call sphere.move(OrbSpeed)
call GroupEnumUnitsInRange(g,sphere.x2,sphere.y2,orbHitRadius,Condition(function notDead))
if (CountUnitsInGroup(g)>0) then
set tu = sphere.missile
set tl = sphere.lvl
call ForGroup(g,function dmgTrgOrb)
endif
call DestroyGroup(g)
set g = null
if (sphere.dist>=maxOrbDist or sphere.d) then
call Final(sphere.x2,sphere.y2,GetOwningPlayer(sphere.missile),sphere.lvl)
call sphere.remove()
elseif (sphere.ttime>=sphere.stime) then
set sphere.stime = sphere.stime + spawnRate
set sphere.ttime = sphere.ttime + periodicCheck
set sphere.special = sphere.special + 1
call CreateBolt(sphere.x2,sphere.y2,(sphere.special*specialAngle+sphere.angle),height,boltSize,boltMdl,GetOwningPlayer(sphere.missile),sphere.lvl)
call TimerStart(t,periodicCheck,false,function IceOrbMotion)
else
set sphere.ttime = sphere.ttime + periodicCheck
call TimerStart(t,periodicCheck,false,function IceOrbMotion)
endif
set t = null
endfunction
//=================== Starts it all right here =========================\\
private function IceOrbActions takes nothing returns nothing
local real cx
local real cy
local real tx
local real ty
local Missile sphere = Missile.create()
set cx = GetUnitX(GetTriggerUnit())
set cy = GetUnitY(GetTriggerUnit())
set tx = GetLocationX(GetSpellTargetLoc())
set ty = GetLocationY(GetSpellTargetLoc())
call sphere.make(cx,cy,(bj_RADTODEG*Atan2(ty-cy,tx-cx)),height,orbSize,orbMdl,GetOwningPlayer(GetTriggerUnit()),I2R(GetUnitAbilityLevel(GetTriggerUnit(),SpellID)))
set sphere.stime = spawnRate
call TimerStart(sphere.t,0,false,function IceOrbMotion)
endfunction
//====================== You know what this is ============================\\
public function InitTrig takes nothing returns nothing
local trigger FrozenOrb = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(FrozenOrb,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(FrozenOrb,Condition(function Conditions))
call TriggerAddAction(FrozenOrb,function IceOrbActions)
endfunction
endscope
Btw I looked at Diablo 2's Ice Orb over and over to verify - It is not random. They spawn at a set angle of 35, 50, 55, or any other angle that does not hit 360 normally, and it spawns according to the orb's direction.