scope FuriousFire initializer init
globals
private constant integer SPELL_ID = 'A00F'
private constant integer DUMMY_SPELL_ID = 'A00E'
private constant integer HIDE_ABILITY = 'A00G'
private constant integer HIDE_DUMMY = 'A00H'
private constant integer ANIM_INDEX = 5
private constant integer FX_COUNT = 5
private constant real RANGE = 500
private constant real RADIUS = 100
private constant real RANGE_BOOST = 500
private constant real RADIUS_BOOST = 100
private constant real GAP = 125
private constant real CHANNEL_TIME = 3
private constant real WAIT = 0.1
private constant real DURATION = 0.25
private constant real TIM_PERIODIC = 0.04
private constant string FX_PATH = "war3mapImported\\Firaga.mdx"
private constant string FX_PATH2 = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
private constant string CHARGE_PATH = "Abilities\\Weapons\\LordofFlameMissile\\LordofFlameMissile.mdl"
private constant string ATTACH_PATH = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
private constant real SPEED = (RANGE/FX_COUNT)
private Table a
endglobals
private struct Spell
unit c
real x
real y
effect attach
xedamage d
xefx chargefx
real scale
group hit
real angle
real range
real radius
real speed
real dist
real chargeBuff
real fxScale
integer fxCount
xefx fx
xefx fx2
static method boom takes nothing returns nothing
local timer tim = GetExpiredTimer()
local Spell this = GetTimerData(tim)
local unit t
set .x = .x + .speed*Cos(angle)
set .y = .y + .speed*Sin(angle)
set .dist = .dist + .speed
if .dist > .fxCount*GAP then
set fx = xefx.create(x,y,0)
set fx.fxpath = FX_PATH
set fx.scale = .fxScale
set fx.z = 0
call fx.destroy()
set fx2 = xefx.create(x,y,0)
set fx2.fxpath = FX_PATH2
set fx2.scale = .fxScale-0.3
set fx2.z = 0
call fx2.destroy()
set .fxCount = .fxCount + 1
endif
call GroupClear(ENUM_GROUP)
call GroupEnumUnitsInRange(ENUM_GROUP,x,y,.radius,null)
loop
set t = FirstOfGroup(ENUM_GROUP)
exitwhen t == null
if not IsUnitInGroup(t,hit) and IsUnitEnemy(t,GetOwningPlayer(.c)) then
call d.damageTarget(.c,t,200)
call GroupAddUnit(hit,t)
endif
call GroupRemoveUnit(ENUM_GROUP,t)
endloop
if .dist >= .range then
call ReleaseTimer(tim)
call ReleaseGroup(hit)
call .deallocate()
endif
set tim = null
endmethod
static method wait takes nothing returns nothing
local timer tim = GetExpiredTimer()
local Spell this = GetTimerData(tim)
call DestroyEffect(.attach)
set .chargeBuff = .chargeBuff/(CHANNEL_TIME/TIM_PERIODIC)
set .radius = RADIUS+(RADIUS_BOOST*.chargeBuff)
set .range = RANGE+(RANGE_BOOST*.chargeBuff)-.radius
set .speed = (.range/DURATION)*TIM_PERIODIC
set .fxScale = (.radius/125)
call TimerStart(NewTimerEx(this),TIM_PERIODIC,true,function Spell.boom)
call ReleaseTimer(tim)
set tim = null
endmethod
static method charge takes nothing returns nothing
local timer tim = GetExpiredTimer()
local Spell this = GetTimerData(tim)
set .scale = .scale + (1/CHANNEL_TIME)*TIM_PERIODIC
set .chargefx.scale = .scale
if a[GetHandleId(.c)] == 0 then
call ReleaseTimer(tim)
call SetUnitAnimationByIndex(.c,ANIM_INDEX)
call QueueUnitAnimation(.c,"stand")
call chargefx.hiddenDestroy()
set .attach = AddSpecialEffectTarget(ATTACH_PATH,.c,"right hand")
set .hit = NewGroup()
call TimerStart(NewTimerEx(this),WAIT,false,function Spell.wait)
else
set .chargeBuff = .chargeBuff + 1
endif
set tim = null
endmethod
static method create takes unit c, real angle returns Spell
local Spell this = Spell.allocate()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real angleFX = angle-(80*bj_DEGTORAD)
local xecast caster = xecast.createBasicA('A00I',OrderId("ensnare"),Player(0))
call caster.castOnTarget(c)
set .c = c
set .x = x
set .y = y
set .d = xedamage.create()
set .angle = angle
set .dist = 0
set .chargeBuff = 0
set .fxCount = 1
set .chargefx = xefx.create(x+50*Cos(angleFX),y+50*Sin(angleFX),angle)
set .scale = 0
set .chargefx.scale = .scale
set .chargefx.z = 100
set .chargefx.fxpath = CHARGE_PATH
set d.dtype = DAMAGE_TYPE_FIRE
set d.wtype = WEAPON_TYPE_WHOKNOWS
set d.atype = ATTACK_TYPE_MAGIC
call TimerStart(NewTimerEx(this),TIM_PERIODIC,true,function Spell.charge)
return this
endmethod
endstruct
private function cond takes nothing returns boolean
local unit c = GetTriggerUnit()
local real tx = GetSpellTargetX()
local real ty = GetSpellTargetY()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real angle
local integer unitId = GetHandleId(c)
local timer tim
if GetSpellAbilityId() == DUMMY_SPELL_ID and a[unitId] == 0 then
set angle = Atan2(ty-y,tx-x)
call UnitAddAbility(c,HIDE_ABILITY)
call UnitAddAbility(c,SPELL_ID)
call IssueImmediateOrderById(c,OrderId("flare"))
call SetUnitFacing(c,angle*bj_RADTODEG)
set a[unitId] = 1
call Spell.create(c,angle)
endif
set c = null
return true
endfunction
private function cleanUp takes nothing returns boolean
local unit c = GetTriggerUnit()
local integer unitId = GetHandleId(c)
if GetSpellAbilityId() == SPELL_ID and a[unitId] != 0 then
call UnitRemoveAbility(c,SPELL_ID)
call UnitRemoveAbility(c,HIDE_ABILITY)
call UnitAddAbility(c,DUMMY_SPELL_ID)
call IssueImmediateOrder(c,"stop")
set a[unitId] = 0
endif
return true
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local trigger t2 = CreateTrigger()
set a = Table.create()
call SetPlayerAbilityAvailable(Player(0),HIDE_DUMMY,false)
call SetPlayerAbilityAvailable(Player(0),HIDE_ABILITY,false)
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function cond))
call TriggerRegisterAnyUnitEventBJ(t2,EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t2,Condition(function cleanUp))
set t = null
set t2 = null
endfunction
endscope
library xefx initializer init requires xebasic, optional xedummy
//**************************************************
// xefx 0.9
// --------
// Recommended: ARGB (adds ARGBrecolor method)
// For your movable fx needs
//
//**************************************************
//==================================================
globals
private constant integer MAX_INSTANCES = 8190 //change accordingly.
//Delay in order to show the death animation of the effect correctly when xefx is destroyed.
//You may need to increase this if you are using effects with longer death animations.
private constant real MIN_RECYCLE_DELAY = 4.0
//The delay does not need to be exact so we do cleanup in batches instead of individually.
//This determines how often the recycler runs, should be less than MIN_RECYCLE_DELAY.
private constant real RECYCLE_INTERVAL = 0.5
//if this is true and the xedummy library is present, units will be recycled instead of removed.
private constant boolean RECYCLE_DUMMY_UNITS = true
private timer recycler
endglobals
private struct recyclebin
unit u
private recyclebin next=0
private static recyclebin array list
private static integer readindex=1
private static integer writeindex=0
private static integer count=0
static method Recycle takes nothing returns nothing
local recyclebin this = .list[readindex]
loop
exitwhen this==0
static if RECYCLE_DUMMY_UNITS and LIBRARY_xedummy then
call XE_ReleaseDummyUnit(this.u)
else
call RemoveUnit(this.u)
endif
set this.u=null
set .count=.count-1
call this.destroy()
set this=this.next
endloop
set .list[readindex]=0
set .writeindex=.readindex
set .readindex=.readindex+1
if .readindex>R2I(MIN_RECYCLE_DELAY/RECYCLE_INTERVAL+1.0) then
set .readindex=0
endif
if count!=0 then
call TimerStart(recycler, RECYCLE_INTERVAL, false, function recyclebin.Recycle)
endif
endmethod
static method create takes unit u returns recyclebin
local recyclebin this=recyclebin.allocate()
if .count==0 then
call TimerStart(recycler, RECYCLE_INTERVAL, false, function recyclebin.Recycle)
endif
set .count=.count+1
set .u=u
call SetUnitOwner(u,Player(15),false)
set .next=.list[.writeindex]
set .list[.writeindex]=this
return this
endmethod
endstruct
private function init takes nothing returns nothing
set recycler=CreateTimer()
endfunction
struct xefx[MAX_INSTANCES]
public integer tag=0
private unit dummy
private effect fx=null
private real zang=0.0
private integer r=255
private integer g=255
private integer b=255
private integer a=255
private integer abil=0
static method create takes real x, real y, real facing returns xefx
local xefx this=xefx.allocate()
static if RECYCLE_DUMMY_UNITS and LIBRARY_xedummy then
set this.dummy= XE_NewDummyUnit(Player(15), x,y, facing*bj_RADTODEG)
else
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, facing*bj_RADTODEG)
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
endif
return this
endmethod
method operator owner takes nothing returns player
return GetOwningPlayer(this.dummy)
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(this.dummy,p,false)
endmethod
method operator teamcolor= takes playercolor c returns nothing
call SetUnitColor(this.dummy,c)
endmethod
method operator scale= takes real value returns nothing
call SetUnitScale(this.dummy,value,value,value)
endmethod
//! textmacro XEFX_colorstuff takes colorname, colorvar
method operator $colorname$ takes nothing returns integer
return this.$colorvar$
endmethod
method operator $colorname$= takes integer value returns nothing
set this.$colorvar$=value
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
//! endtextmacro
//! runtextmacro XEFX_colorstuff("red","r")
//! runtextmacro XEFX_colorstuff("green","g")
//! runtextmacro XEFX_colorstuff("blue","b")
//! runtextmacro XEFX_colorstuff("alpha","a")
method recolor takes integer r, integer g , integer b, integer a returns nothing
set this.r=r
set this.g=g
set this.b=b
set this.a=a
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
implement optional ARGBrecolor
method operator abilityid takes nothing returns integer
return this.abil
endmethod
method operator abilityid= takes integer a returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(a!=0) then
call UnitAddAbility(this.dummy,a)
endif
set this.abil=a
endmethod
method operator getUnit takes nothing returns unit
return this.dummy
endmethod
method operator abilityLevel takes nothing returns integer
return GetUnitAbilityLevel( this.dummy, this.abil)
endmethod
method operator abilityLevel= takes integer newLevel returns nothing
call SetUnitAbilityLevel(this.dummy, this.abil, newLevel)
endmethod
method flash takes string fx returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx,this.dummy,"origin"))
endmethod
method operator xyangle takes nothing returns real
return GetUnitFacing(this.dummy)*bj_DEGTORAD
endmethod
method operator xyangle= takes real value returns nothing
call SetUnitFacing(this.dummy,value*bj_RADTODEG)
endmethod
method operator zangle takes nothing returns real
return this.zang
endmethod
method operator zangle= takes real value returns nothing
local integer i=R2I(value*bj_RADTODEG+90.5)
set this.zang=value
if(i>=180) then
set i=179
elseif(i<0) then
set i=0
endif
call SetUnitAnimationByIndex(this.dummy, i )
endmethod
method operator x takes nothing returns real
return GetUnitX(this.dummy)
endmethod
method operator y takes nothing returns real
return GetUnitY(this.dummy)
endmethod
method operator z takes nothing returns real
return GetUnitFlyHeight(this.dummy)
endmethod
method operator z= takes real value returns nothing
call SetUnitFlyHeight(this.dummy,value,0)
endmethod
method operator x= takes real value returns nothing
call SetUnitX(this.dummy,value)
endmethod
method operator y= takes real value returns nothing
call SetUnitY(this.dummy,value)
endmethod
method operator fxpath= takes string newpath returns nothing
if (this.fx!=null) then
call DestroyEffect(this.fx)
endif
if (newpath=="") then
set this.fx=null
else
set this.fx=AddSpecialEffectTarget(newpath,this.dummy,"origin")
endif
endmethod
method hiddenReset takes string newfxpath, real newfacing returns nothing
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
local real z = this.z
local real za = this.zangle
local integer level = this.abilityLevel
set .fxpath=null
static if RECYCLE_DUMMY_UNITS and LIBRARY_xedummy then
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
call recyclebin.create(this.dummy)
set this.dummy=XE_NewDummyUnit(Player(15), x,y, newfacing*bj_RADTODEG)
else
call RemoveUnit(this.dummy)
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, newfacing*bj_RADTODEG)
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
endif
set .fxpath=newfxpath
if(level != 0) then
call UnitAddAbility(this.dummy, this.abil)
call SetUnitAbilityLevel(this.dummy, this.abil, level)
endif
set this.z = z
set zangle = za
endmethod
method destroy takes nothing returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(this.fx!=null) then
call DestroyEffect(this.fx)
set this.fx=null
endif
call recyclebin.create(this.dummy)
set this.dummy=null
call this.deallocate()
endmethod
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
endstruct
endlibrary
library xedummy requires xebasic
//******************************************************************************
// xedummy 0.9
// ------
// For all your xe dummy recycling needs.
//
//******************************************************************************
//==============================================================================
globals
// The number of different angles at which the dummy units will be stored.
private constant integer ANGLE_RESOLUTION = 12
// The total number of xe dummy units that will be preloaded on map initialization.
private constant integer INITIAL_DUMMY_COUNT = 36
// Don't allow to keep more than DUMMY_STACK_LIMIT inactive dummy units.
private constant integer DUMMY_STACK_LIMIT = 240
endglobals
// END OF CALIBRATION SECTION
// ================================================================
private keyword xedummy
private struct recycleQueue extends array
recycleQueue next
recycleQueue prev
real angle
integer size
xedummy first
xedummy last
static method onInit takes nothing returns nothing
local integer i=0
loop
exitwhen i==ANGLE_RESOLUTION
set i=i+1
set recycleQueue(i).prev=recycleQueue(i-1)
set recycleQueue(i).next=recycleQueue(i+1)
set recycleQueue(i).angle=(i-0.5)*(360.0/ANGLE_RESOLUTION)
endloop
set recycleQueue(1).prev=recycleQueue(i)
set recycleQueue(i).next=recycleQueue(1)
endmethod
static method get takes real angle returns recycleQueue
return recycleQueue(R2I(angle/360.0*ANGLE_RESOLUTION)+1)
endmethod
endstruct
// ================================================================
struct xedummy
private static group g=CreateGroup()
private unit u
// ----------------------------------------------------------------
private xedummy next
private method queueInsert takes recycleQueue q returns nothing
call SetUnitFacing(.u, q.angle)
if q.size==0 then
set q.first=this
else
set q.last.next=this
endif
set q.last=this
set .next=0
// Recursively check adajcent queues and migrate xedummies as needed.
if q.size>q.next.size then
set this=q.first
set q.first=.next
call .queueInsert(q.next)
elseif q.size>q.prev.size then
set this=q.first
set q.first=.next
call .queueInsert(q.prev)
else
set q.size=q.size+1
endif
endmethod
private static method queueRemove takes recycleQueue q returns xedummy
// Recursively check adajcent queues and migrate xedummies as needed.
local xedummy this
if q.size<q.next.size then
set this=q.last
set q.last=.queueRemove(q.next)
set .next=q.last
call SetUnitFacing(q.last.u, q.angle)
elseif q.size<q.prev.size then
set this=q.last
set q.last=.queueRemove(q.prev)
set .next=q.last
call SetUnitFacing(q.last.u, q.angle)
else
set q.size=q.size-1
if q.size==0 then
set q.last=0
endif
endif
set this=q.first
set q.first=.next
set .next=0
return this
endmethod
// ----------------------------------------------------------------
private static method create takes unit u returns xedummy
local xedummy this
if GetUnitTypeId(u)!=XE_DUMMY_UNITID then
debug call BJDebugMsg("ReleaseXEDummy error: Method called on a unit of an incorrect type.")
elseif IsUnitInGroup(u, .g) then
debug call BJDebugMsg("ReleaseXEDummy error: Method called on an already released unit.")
else
set this=.allocate()
if integer(this)>DUMMY_STACK_LIMIT then
call RemoveUnit(u)
call .deallocate()
return 0
endif
set .u=u
call GroupAddUnit(.g, u)
call .queueInsert(recycleQueue.get(GetUnitFacing(u)))
call SetUnitAnimationByIndex(u, 90)
call SetUnitScale(u, 1, 0, 0)
call SetUnitVertexColor(u, 255, 255, 255, 255)
// call ShowUnit(u, false) // Do not hide the unit, it is rather costly and not needed.
return this
endif
return 0
endmethod
private method destroy takes nothing returns nothing
call GroupRemoveUnit(.g, .u)
call ShowUnit(.u, true) // Show the unit in case it was hidden before being recycled.
set .u=null
call .deallocate()
endmethod
// ----------------------------------------------------------------
private static unit dummy
private static method onInit takes nothing returns nothing
local integer i=INITIAL_DUMMY_COUNT
local recycleQueue q=recycleQueue(1)
if i>DUMMY_STACK_LIMIT then
debug call BJDebugMsg("xedummy error: INITIAL_DUMMY_COUNT can not be larger than DUMMY_STACK_LIMIT.")
set i=DUMMY_STACK_LIMIT
endif
loop
exitwhen i==0
set .dummy = CreateUnit(Player(15), XE_DUMMY_UNITID, 0.0,0.0,q.angle)
call UnitAddAbility(.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(.dummy,'Aloc')
call UnitRemoveAbility(.dummy,XE_HEIGHT_ENABLER)
call .create(.dummy)
set i=i-1
set q=q.next
endloop
endmethod
// ----------------------------------------------------------------
static method new takes player p, real x, real y, real face returns unit
local recycleQueue q
local xedummy this
loop
exitwhen face>0.0
set face=face+360.0
endloop
loop
exitwhen face<360.0
set face=face-360.0
endloop
set q=recycleQueue.get(face)
if q.size==0 then
set .dummy = CreateUnit(p, XE_DUMMY_UNITID, x,y,face)
call UnitAddAbility(.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(.dummy,'Aloc')
call UnitRemoveAbility(.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(.dummy, x)
call SetUnitY(.dummy, y)
else
set this=.queueRemove(q)
set .dummy=.u
call .destroy()
call SetUnitX(.dummy, x)
call SetUnitY(.dummy, y)
call SetUnitFacing(.dummy, face)
call SetUnitOwner(.dummy, p, true)
endif
return .dummy
endmethod
static method release takes unit u returns nothing
call .create(u)
endmethod
endstruct
// ================================================================
function XE_NewDummyUnit takes player p, real x, real y, real face returns unit
return xedummy.new( p,x,y,face )
endfunction
function XE_ReleaseDummyUnit takes unit u returns nothing
call xedummy.release( u )
endfunction
endlibrary
call SetPlayerAbilityAvailable(Player(0),HIDE_DUMMY,false)
if GetSpellAbilityId() == SPELL_ID and a[unitId] != 0 then
sorry, the name is not rly well chosen. HIDE_DUMMY just represents an ability id. I'm using the engineering upgrade because within the spell, the hero ability gets replaced.So i'm not hiding any unit. Though the problem seems to be that the dummy loses the locust ability.
If you're interested, you can read the part about "Aneg" on the link you posted.
call chargefx.hiddenDestroy()
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
private method destroy takes nothing returns nothing
call GroupRemoveUnit(.g, .u)
call ShowUnit(.u, true) // Show the unit in case it was hidden before being recycled.
set .u=null
call .deallocate()
endmethod
Maybe that's a little confusing. When the hero charges the ability, a[unitId] gets 1 so that checkes if the casted spell is the spell that ends the charge and if the unit is charging. Then the value changes again to 0 to indicate that the unit isn't charging anymore.shouldnt thishave an or not an and ? this is in ur cleanUp method.JASS:if GetSpellAbilityId() == SPELL_ID and a[unitId] != 0 then
Nah I read it quite a lot of times already =D
I can only find one thing related to hiding units in your code here
JASS:call chargefx.hiddenDestroy()
which is linked to
JASS:method hiddenDestroy takes nothing returns nothing call ShowUnit(dummy,false) call destroy() endmethod
and because you have xedummy
JASS:private method destroy takes nothing returns nothing call GroupRemoveUnit(.g, .u) call ShowUnit(.u, true) // Show the unit in case it was hidden before being recycled. set .u=null call .deallocate() endmethod
Thus, I believe that is what is causing your problem. You can simply try an alternative method for removing chargefx and see what turns out.
Maybe that's a little confusing. When the hero charges the ability, a[unitId] gets 1 so that checkes if the casted spell is the spell that ends the charge and if the unit is charging. Then the value changes again to 0 to indicate that the unit isn't charging anymore.
Wow, that seems to solve the problem.I'm wondering why Vex didn't fix that yet..
Thanks alot to both of you.
Wow, that seems to solve the problem.I'm wondering why Vex didn't fix that yet..
Thanks alot to both of you.