• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] Spell problems

Status
Not open for further replies.
Level 20
Joined
Jul 6, 2009
Messages
1,885
I seem to be stuck on a little problem (or 2) so i could use some help.
I made a simple spell that fires a projectile damaging each unit it hits and dealing damage upon reaching certain range.

First problem is that i wanted the projectile to damage each unit once by adding damaged units to a group in struct instance and each next time checking whether units hit by projectile are in the group, but it doesn't work properly (Units that were supposed to be in the group pass the check).

Second problem is that when the spell is casted several times, it seems that...instances mess up. Sometimes projectiles move twice as fast and according to debug messages, at those times more than one instances refers to same unit ;/
JASS:
library Arcyling initializer Init

    private keyword Instance
    private keyword l

    //Configuration Part

    globals
        private constant integer SPELL_RAW       = 'A003'
        private constant integer SPIRIT_RAW      = 'h001'
        private constant real SPEED              = 10.0
        private constant real BASE_DAMAGE_1      = 20.0
        private constant real DAMAGE_1_INCREMENT = 50.0
        private constant real BASE_DAMAGE_2      = 25.0
        private constant real DAMAGE_2_INCREMENT = 20.0
        private constant real MAX_RANGE          = 600.0
        private constant real EXPLOSION_AOE      = 200.0
        private constant real MOVE_AOE           = 100.0
        private constant string EFFECT_1         = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
        private constant string EFFECT_2         = "Units\\NightElf\\Wisp\\WispExplode.mdl"
    endglobals
    
    private function filter takes unit u returns boolean
        return IsUnitType(u,UNIT_TYPE_DEAD) == false and IsPlayerEnemy(GetOwningPlayer(u),GetOwningPlayer(l)) and IsUnitType(u,UNIT_TYPE_STRUCTURE) == false
    endfunction
    
    //End of configuration
    //===========================================================================
    
    globals
        private Instance array I
        private integer Running = 0
        private integer Max = 0
        private group g = CreateGroup()
        private group d  //Variables d,l and i are used to pass data to a filter function since filter functions can't have parameters
        private unit l
        private integer i
    endglobals
    
    function Damage1 takes nothing returns boolean
        local unit u = GetFilterUnit()
        if filter(u) then
            call UnitDamageTarget(l,u,BASE_DAMAGE_1 + i * DAMAGE_1_INCREMENT,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
        endif
        return false
    endfunction
    
    function Damage2 takes nothing returns boolean
        local unit u = GetFilterUnit()
        if filter(u) and IsUnitInGroup(u,d) == false then //This is one of the problems, units pass this check even though i added them to the group
            call UnitDamageTarget(l,u,BASE_DAMAGE_2 + i * DAMAGE_2_INCREMENT,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
            call GroupAddUnit(d,u)
            call DestroyEffect(AddSpecialEffectTarget(EFFECT_1,u,"chest"))
        endif
        return false
    endfunction
    
    private struct Instance
        unit u
        integer lvl
        real r = 0.00
        group g
        
        method move takes nothing returns nothing
            local real x = GetUnitX(.u)
            local real y = GetUnitY(.u)
            local real angle = GetUnitFacing(.u) * bj_DEGTORAD
            call SetUnitX(.u,x + SPEED * Cos(angle))
            call SetUnitY(.u,y + SPEED * Sin(angle))
            set .r = .r + SPEED
            set l = .u
            set d = .g
            set i = .lvl
            call GroupEnumUnitsInRange(g,x,y,MOVE_AOE,function Damage2)
            if .r >= MAX_RANGE then
                set x = GetUnitX(.u)
                set y = GetUnitY(.u)
                call GroupEnumUnitsInRange(g,x,y,MOVE_AOE,function Damage1)
                call DestroyEffect(AddSpecialEffect(EFFECT_2,x,y))
                call KillUnit(.u)
                call .destroy()
            endif
        endmethod
    
        static method create takes unit u,integer lvl returns thistype
            local thistype this = thistype.allocate()
            set .u = CreateUnit(GetOwningPlayer(u),SPIRIT_RAW,GetUnitX(u),GetUnitY(u),GetUnitFacing(u))
            set .lvl = lvl
            set .g = CreateGroup()
            set I[Max] = this
            set Running = Running + 1
            set Max = Max + 1
            return this
        endmethod
    
        method onDestroy takes nothing returns nothing
            set Running = Running - 1
            set .u = null
            call DestroyGroup(.g)
        endmethod
        
    endstruct
    
    private function Cast takes nothing returns boolean
        if GetSpellAbilityId() == SPELL_RAW then
            call Instance.create(GetTriggerUnit(),GetUnitAbilityLevel(GetTriggerUnit(),SPELL_RAW))
        endif
        return false
    endfunction
    
    public function Loop takes nothing returns nothing
        local integer i = 0
        if Running != 0 then
            loop
                exitwhen i + 1 > Max
                if I[i].u != null then
                    call I[i].move()
                endif
                set i = i + 1
                if Running == 0 then
                    set Max = 0
                endif
            endloop
        endif
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        loop
            call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
            set i = i + 1
            exitwhen i == 16
        endloop
        call TriggerAddCondition(t,function Cast)
        set t = null
    endfunction

endlibrary
(Note: Loop function is ran outside of library)
Here's also a test map with the spell.
 

Attachments

  • Spells.w3x
    29.5 KB · Views: 51
Level 20
Joined
Jul 6, 2009
Messages
1,885
GroupEnum...() functions auto-clear groups, that should fix your first problem. (you'll need a separate group for GroupEnumeration stuff)
But i'm using GroupEnum with a dummy group, i do all the things in filter and return false in the end.
I use another group, though, that is a member of struct in which i place units that were hit.
For the second problem, why not just use something like TimerUtils or T32?
I don't really see their purpose. I saw TimerUtils on wc3c without an explanation what is it for, i only know it uses timers, but i can use them natively anyway :S
I don't know what's T32 though; heard of it.

EDIT: Solved the first problem by renaming the global variable named 'g'. Struct had a member named 'g' too (renaming either global or the member fixes the problem), but i didn't think those would interfere as i thought they compile under different names (a global variable and a struct member).

EDIT2: Solved the second problem too ^^
The thing was that multiple variables of Struct array were pointing to same instance due to deallocation and some omgwtfbbq stuff.
 
Last edited:
Status
Not open for further replies.
Top