Yesterday I decided to learn vJass. I read some tutorials and looked at various approved vJass stuff in the hive's spell section which I felt gave me a pretty good idea of how to do things.
Earlier today I decided to try it out for myself by creating my first vJass spell!
Needless to say, I've come across a few problems...
The spell is supposed to create a (somewhat) random pattern of cracks in the ground that will explode after a set duration, dealing damage to enemy units.
The problems:
1) While the spell itself works fairly well Im having a hard time figuring out a way of making sure that each unit is only damaged ONCE.
I initially wanted to do this by creating an additional group (this.g) in which all units that have been damaged would be stored and thereby excluded from future instances of the spell. This worked... to some extend... as the cleanup for this.g would trigger 8 times towards the end of the spell! I tried to implement some work around by adding an additional condition to the cleanup: if this.g != null but that did not seem to have any effect.
How should I go about fixxing this? I feel that the cause of this problem is somewhat tied to the way that I have coded and structured the spell - which brings me to the next question:
2 Overall code structure.. looking over my own code it feels so incomprehensible and I can't help but feel that I should have approached this spell in an entirely different manner. Did I do something fundamentally wrong?
Here is the code
I realise that asking people to look through that abomination is quite the thing to ask. Im still hoping that someone will take the time and help me out.
Earlier today I decided to try it out for myself by creating my first vJass spell!
Needless to say, I've come across a few problems...
The spell is supposed to create a (somewhat) random pattern of cracks in the ground that will explode after a set duration, dealing damage to enemy units.
The problems:
1) While the spell itself works fairly well Im having a hard time figuring out a way of making sure that each unit is only damaged ONCE.
I initially wanted to do this by creating an additional group (this.g) in which all units that have been damaged would be stored and thereby excluded from future instances of the spell. This worked... to some extend... as the cleanup for this.g would trigger 8 times towards the end of the spell! I tried to implement some work around by adding an additional condition to the cleanup: if this.g != null but that did not seem to have any effect.
How should I go about fixxing this? I feel that the cause of this problem is somewhat tied to the way that I have coded and structured the spell - which brings me to the next question:
2 Overall code structure.. looking over my own code it feels so incomprehensible and I can't help but feel that I should have approached this spell in an entirely different manner. Did I do something fundamentally wrong?
Here is the code
JASS:
scope SeismicSlam
globals
private constant integer ABILITY_ID = 'A01A'
private constant integer DUMMY_ID = 'h000'
private constant string MODEL_PATH = "war3mapImported\\DustAndRocks.mdx"
private constant string MODEL_PATH2 = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl"
private constant string MODEL_PATH3 = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
private constant integer INSTANCES = 9
private constant real DISTANCE = 100.
private constant real INTERVAL = 0.10
private constant real DURATION = 1.00
private constant real AOE = 175.
private constant real SPMOD = 1.00
private constant attacktype A_TYPE = ATTACK_TYPE_HERO
private constant damagetype D_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype W_TYPE = null
endglobals
private constant function Damage takes integer level returns real
return 45 * level
endfunction
private function Spellpower takes unit u returns real
return GetHeroInt(u, true) * SPMOD
endfunction
private function UnitFilter takes player source, unit t, group g returns boolean
return IsUnitEnemy(t, source) and UnitAlive(t) and not IsUnitInGroup(t, g)
endfunction
//! textmacro_once ALLOCATE
if thistype(0).prev == 0 then
set count = count + 1
set this = count
else
set this = thistype(0).prev
set thistype(0).prev = thistype(0).prev.prev
endif
if thistype(0).next == 0 then
call TimerStart(period, INTERVAL, true, function thistype.periodic)
else
set thistype(0).next.prev = this
endif
set this.next = thistype(0).next
set thistype(0).next = this
set this.prev = thistype(0)
//! endtextmacro
private struct Spell extends array
unit u
unit d
real angle
integer instance
real dur
effect fx
group g
integer s
static unit t
static unit nd
static real x
static real y
static real x1
static real y1
static real a
static real damage
static integer lvl
static integer split
static player p
thistype prev
thistype next
static integer count
static timer period
static group filter
method destroy takes nothing returns nothing
if this.next != 0 then
set this.next.prev = this.prev
endif
set this.prev.next = this.next
set this.prev = thistype(0).prev
set thistype(0).prev = this
if thistype(0).next == 0 then
call PauseTimer(period)
endif
set this.u = null
set this.d = null
set this.fx = null
set this.g = null
endmethod
static method periodic takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen this == 0
set this.dur = this.dur - INTERVAL
set x = GetUnitX(this.d)
set y = GetUnitY(this.d)
if this.instance > 0 and this.s == 1 then
set this.instance = this.instance - 1
if this.instance != 7 and this.instance != 3 then
set this.angle = this.angle + (GetRandomReal(-20., 20.))
set x1 = x + DISTANCE * Cos(this.angle * 0.01745)
set y1 = y + DISTANCE * Sin(this.angle * 0.01745)
set nd = CreateUnit(GetOwningPlayer(u), DUMMY_ID, x1, y1, this.angle)
call SetUnitScale(nd, 0.75, 0.75, 0.75)
call Spell.add(this.u,nd,this.angle,this.instance,DURATION,this.g,1)
set nd = null
set this.s = 0
else
set split = 0
loop
exitwhen split == 2
set split = split + 1
set a = this.angle + (GetRandomReal(75., 105.) * split - 135.)
set x1 = x + DISTANCE * Cos(a * 0.01745)
set y1 = y + DISTANCE * Sin(a * 0.01745)
set nd = CreateUnit(GetOwningPlayer(u), DUMMY_ID, x1, y1, a)
call SetUnitScale(nd, 0.75, 0.75, 0.75)
set a = this.angle + (90. * split - 135.) / 2
call Spell.add(this.u,nd,a,this.instance,DURATION,this.g,1)
set nd = null
endloop
set this.s = 0
endif
endif
if this.dur < 0. then
call DestroyEffect(AddSpecialEffectTarget(MODEL_PATH2, this.d, "origin"))
call DestroyEffect(this.fx)
call UnitApplyTimedLife(this.d, 'BLTF', 0.75)
set p = GetOwningPlayer(this.u)
set lvl = GetUnitAbilityLevel(this.u, ABILITY_ID)
set damage = Damage(lvl) + Spellpower(this.u)
set udg_DamageType = 1
call GroupEnumUnitsInRange(filter, x, y, AOE, null)
loop
set t = FirstOfGroup(filter)
exitwhen t == null
call GroupRemoveUnit(filter,t)
if UnitFilter(p,t,this.g) then
call GroupAddUnit(this.g, t)
call UnitDamageTarget(this.u, t, damage, true, false, A_TYPE, D_TYPE, W_TYPE)
call IssueTargetOrder(this.d, ORDER_ID, t)
endif
endloop
set udg_DamageType = 0
if this.instance == 0 and FirstOfGroup(this.g) != null then
call GroupClear(this.g)
call ReleaseGroup(this.g)
call DisplayTextToForce(GetPlayersAll(), "this.g cleared")
call this.destroy()
elseif this.instance >= 0 then
call this.destroy()
call DisplayTextToForce(GetPlayersAll(), "this.destroy triggered (i >= 0)")
endif
endif
set this = this.next
endloop
endmethod
static method add takes unit u, unit d, real a, integer i, real dur, group g, integer s returns nothing
local thistype this
//! runtextmacro ALLOCATE()
set this.u = u
set this.d = d
set this.angle = a
set this.instance = i
set this.dur = dur
set this.fx = AddSpecialEffectTarget(MODEL_PATH, d, "origin")
set this.g = g
set this.s = s
endmethod
static method run takes nothing returns boolean
local unit u = GetTriggerUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real angle = AngleBetweenXY(x, y, GetSpellTargetX(), GetSpellTargetY())
local group g = NewGroup()
local unit d
set x = x + DISTANCE * Cos(angle * 0.01745)
set y = y + DISTANCE * Sin(angle * 0.01745)
set d = CreateUnit(GetOwningPlayer(u), DUMMY_ID, x, y, angle)
call SetUnitScale(d, 0.75, 0.75, 0.75)
call DestroyEffect(AddSpecialEffectTarget(MODEL_PATH3, d, "origin"))
call Spell.add(u,d,angle,INSTANCES,DURATION,g,1)
set u = null
set d = null
set g = null
return false
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(ABILITY_ID, function thistype.run)
set count = 0
set period = CreateTimer()
set filter = CreateGroup()
endmethod
endstruct
endscope
I realise that asking people to look through that abomination is quite the thing to ask. Im still hoping that someone will take the time and help me out.