I have a bit of vJASS for a spell I am making.
The spell targets a point and creates a "gravity well" at the location. For as long as the well is alive, it pulls in enemies of the caster.
To implement this, what I am trying to do is start two timers for each well: one for its total duration and one for its periodic interval that will cause the pull-in effect.
The callbacks for the timers are easy enough to handle -- I can use a TimerUtils library I got from wc3c to attach index information to each spell instance's timers and get any required information from there, a la array of structs.
However, I am having trouble in recovering the same information in the callback for a group enumeration. I want to get all of the enemies near the spell's target point. However, to do that, I need to know the casting unit (in order to get its player), and I do not know how to do this.
My original plan was to save the caster in a hashtable under the key represented by the enumeration condition's handle, and in the callback-condition itself, get its own handle which would be the correct key and then from there, getting the caster unit.
Unfortunately, I do not know of any "this" keyword like from Java. I tried a workaround but it looks to me that I'm basically just using a global bucket to solve the problem, which is /not/ what I want because I want to ensure MUI.
How can I push sufficient information to the callback such that the spell is entirely MUI? (Do I even need to do that? Is a temporary global variable enough?) Do I have any alternatives here?
The spell targets a point and creates a "gravity well" at the location. For as long as the well is alive, it pulls in enemies of the caster.
To implement this, what I am trying to do is start two timers for each well: one for its total duration and one for its periodic interval that will cause the pull-in effect.
The callbacks for the timers are easy enough to handle -- I can use a TimerUtils library I got from wc3c to attach index information to each spell instance's timers and get any required information from there, a la array of structs.
However, I am having trouble in recovering the same information in the callback for a group enumeration. I want to get all of the enemies near the spell's target point. However, to do that, I need to know the casting unit (in order to get its player), and I do not know how to do this.
My original plan was to save the caster in a hashtable under the key represented by the enumeration condition's handle, and in the callback-condition itself, get its own handle which would be the correct key and then from there, getting the caster unit.
Unfortunately, I do not know of any "this" keyword like from Java. I tried a workaround but it looks to me that I'm basically just using a global bucket to solve the problem, which is /not/ what I want because I want to ensure MUI.
How can I push sufficient information to the callback such that the spell is entirely MUI? (Do I even need to do that? Is a temporary global variable enough?) Do I have any alternatives here?
JASS:
scope GravityWell
globals
// properties
private real array rDur // duration based on level. Match with A00E
private real rInterval = 1/16 // second/frames
private real rRadius = 300 // area of effect
private real array rForce // force pulling PER SECOND
private conditionfunc cf
// array
private unit array uCasters
private location array lPoints // target points
private timer array tTimers // count duration of well
private timer array tIntervals // counting intervals
private integer array iLvls // level of ability at time of cast
private integer iSize = 0
// other
private hashtable htCb = InitHashtable() // hashtable to hold info needed in callbacks
endglobals
function GravityWell_cbInterval_Condition takes nothing returns boolean
local unit u = GetFilterUnit()
local unit cb_uCaster = LoadUnitHandle(htCb, GetHandleId(cf), 0)
local player p = GetOwningPlayer(cb_uCaster)
if (IsUnitEnemy(u, p) == false) then
return false
endif
if (GetUnitTypeId(u) == 'u004' or IsUnitType(u, UNIT_TYPE_STRUCTURE)) then // this will work on pretty much anything.
return false
endif
// clean up
set p = null
set u = null
set cb_uCaster = null
return true
endfunction
function GravityWell_Init takes nothing returns nothing
set rDur[1] = 4
set rDur[2] = 8
set rDur[3] = 12
set rForce[1] = 100 / rInterval
set rForce[2] = 160 / rInterval
set rForce[3] = 220 / rInterval
endfunction
function Trig_GravityWell_Conditions takes nothing returns boolean
if (GetSpellAbilityId() != 'A00E') then
return false
endif
return true
endfunction
function GravityWell_cbEnd takes nothing returns nothing
local integer i = GetTimerData(GetExpiredTimer())
// clean up
set uCasters[i] = null
call ReleaseTimer(tTimers[i])
call ReleaseTimer(tIntervals[i])
call RemoveLocation(lPoints[i])
endfunction
function GravityWell_cbInterval_cbGroup takes nothing returns nothing
local unit u = GetEnumUnit()
local location loc = GetUnitLoc(u)
local location mov // where to move the unit?
// clean up
call RemoveLocation(loc)
set u = null
endfunction
function GravityWell_cbInterval takes nothing returns nothing
local integer i = GetTimerData(GetExpiredTimer())
local group ug = CreateGroup()
set cf = Condition(function GravityWell_cbInterval_Condition)
call SaveUnitHandle(htCb, GetHandleId(cf), 0, uCasters[i])
call GroupEnumUnitsInRangeOfLoc(ug, lPoints[i], rRadius, cf)
call ForGroup(ug, function GravityWell_cbInterval_cbGroup)
// clean up
call DestroyCondition(cf)
call DestroyGroup(ug)
endfunction
function Trig_GravityWell_Actions takes nothing returns nothing
local integer i = 0
// Find empty array location.
loop
exitwhen (i > C_ARRAYLIMIT) // no free space
exitwhen (uCasters[i] == null) // found free space
set i = i + 1
endloop
if (i >= iSize and iSize <= C_ARRAYLIMIT) then // index indicates that size must be increased
set iSize = iSize + 1
endif
if (i > C_ARRAYLIMIT) then
call BJDebugMsg("WARNING: GravityWell: no free space for spell")
else
set uCasters[i] = GetTriggerUnit()
set iLvls[i] = GetUnitAbilityLevel(uCasters[i], 'A00E')
set lPoints[i] = LocFix(GetSpellTargetLoc())
set tTimers[i] = NewTimerEx(i)
set tIntervals[i] = NewTimerEx(i)
call TimerStart(tTimers[i], rDur[iLvls[i]], false, function GravityWell_cbEnd)
call TimerStart(tIntervals[i], rInterval, true, function GravityWell_cbInterval)
endif
endfunction
endscope