Spell Forestborn

Discussion in 'Spells' started by tommerbob, Jun 22, 2010.

  1. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    Like this?

    JASS:
    
    private function Actions takes nothing returns boolean
        local unit u = GetTriggerUnit()
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real m = GetUnitMoveSpeed(u)
        local player p = GetOwningPlayer(u)
        local data d = data.create(u, x, y, m)
        
        if GetSpellAbilityId() != SPELL_ID then
            return false
        endif
        
        call SetPlayerAbilityAvailable(p, BOOK_ID, false) //Remove icons from hero UI
        call SetTimerData(d.t, d)
        call TimerStart(d.t, INTERVAL, true, function NearTree)
        
        set p = null    
        set u = null
        
        return false
    endfunction
    
    //======================================================
    
    private function init takes nothing returns nothing
        local trigger t  = CreateTrigger()
        local unit u
    
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(t, Condition(function Actions))
        call TriggerAddAction(t, function Actions)
        
        set u = CreateUnit(Player(15), 'Hpal', 0., 0., 0.)  //Preload ability
        call UnitAddAbility(u, BOOK_ID)
        call SetUnitAbilityLevel(u, MOVE_ID, MOVE_BASE)
        call SetUnitAbilityLevel(u, EVADE_ID, EVADE_BASE+1)
        call UnitRemoveAbility(u, MOVE_ID)
        call UnitRemoveAbility(u, BOOK_ID)
        call RemoveUnit(u)
        
        set u = null
        set t = null
    endfunction
    
    endscope


    Done.

    Done. What do you mean by "instance" variables? Like the variables that are used throughout the spell? Like d.caster, d.timer, d.blahblah?

    I actually thought about including invisibility as an option, the same way I included an option for the caster to remain stationary or not. But after several hours already trying to finish it up, I don't feel like doing it at the moment. Besides, it would require one more Object Editor data, and there are already 5. :eek: I might include it as a later update though. Thoughts?

    What? I must be blind. I don't see a level 26 anywhere...
     
  2. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    The problem with that is you are calling all those natives, which takes time, and you don't even know if the ability will fire. That means you're firing those off when any spell runs.

    Here's a tip, use the keyword thistype rather than data in your struct. That's because if you rename your struct later to Pizzacakeuobengtbewjngw or anything, it won't matter, because you used thistype. Also, rename it to Data. Capitalized struct names are all the rage (convention). Use this too.

    Now, change your create method. You can gather the information you provide as parameters with one parameter, the triggering unit, that should make the code much simpler.

    JASS:
    
    private struct data
        unit caster
        integer level
        integer evasion
        integer movebonus
        boolean tree
        real heal
        real move
        real time
        real duration
        real oldx
        real oldy
        real newx
        real newy
        timer t
        
        static method create takes unit u returns thistype
            local thistype this = thistype.allocate()
            set this.caster = u 
            set this.level = GetUnitAbilityLevel(this.caster, SPELL_ID)
            set this.time = 0.
            set this.duration = Duration(this.level)
            set this.evasion = Evasion(this.level)
            set this.heal = Heal(this.level)
            set this.move = GetUnitMoveSpeed(this.caster)
            set this.movebonus = MoveBonus(this.level)
            set this.oldx = GetUnitX(this.caster)
            set this.oldy = GetUnitY(this.caster)
            set this.tree = false
            set this.t = NewTimer()
            return this
        endmethod
            
        method onDestroy takes nothing returns nothing
            set .caster = null
            call ReleaseTimer(.t)
        endmethod
            
    endstruct


    You may have to modify it a bit but you get the idea.

    Moving on; now you can change your actions functions to look more like this:
    JASS:
    
    local Data data // better than "d"; this takes about 0 computing power to declare
    if (GetSpellAbilityId() != SPELL_ID) then
        return false
    endif
    set data = Data.create(GetTriggerUnit())


    More efficient. Then you can just reference the locals that you need in that function as data.x.

    >Done. What do you mean by "instance" variables? Like the variables that are used throughout the spell? Like d.caster, d.timer, d.blahblah?
    The instance variables are the ones that each instance has uniquely, so yeah.
    JASS:
    
    struct Data
        real x // <---- instance variable


    >What? I must be blind. I don't see a level 26 anywhere...
    Probably my mistake then.

    >set u = CreateUnit(Player(15), 'Hpal', 0., 0., 0.) //Preload ability
    Using Player 14 as well as the rawcode of the dummy should be configurable, too.

    > local trigger t = CreateTrigger()
    You might want to make this a global trigger (new thing I want to push) so people can modify it dynamically (add this under configuration)
    JASS:
    
    globals
         trigger ForestbornTrigger
    endglobals

    That's just helpful if I want to do something like disable this spell from my own JASS without throwing in some fancy booleans.

    Maybe you should add more bling? right now it's a bit more bland than I remember :p I vote magical footprints!
     
  3. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    From the code you posted in post #21, you can remove the TriggerAddAction call - it won't be necessary if you are handling your spell instantiation through the trigger condition.


    Is it, really? I can't see how shuffling around the position of assignments and calls would improve efficiency. Unless you are referring to this
    when you're talking about being more efficient


    The preloaded player doesn't need to be a configurable - it'd be a waste of time since it makes little difference what player is given the preloader unit due to the fact that said unit doesn't hang around for long enough for anybody to notice.

    Similar can be said for the rawcode. Unless, of course, you have some reason for deleting the Paladin from the Object Editor (assuming built-in objects can be deleted) :p
     
  4. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    Firing off five unneccesary natives each time any spell is cast sounds inefficient to me.

    Reasonable.

    What if I use the paladin for some other entering the world trigger? Like I give all units a bonus whenever their Holy Father the great paladin enters the map, and the effect doesn't vanish when the paladin is killed or removed from the game. Then any units already created at that point in initlization will get the bonus, others won't, and it is bad in the first place to give the bonus accidently. Could be avoided by changing the constant to another hero.
     
  5. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    Er... just to get things straightened out - are you talking about the natives called here (from the code in the OP)
    JASS:
    private function Actions takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real m = GetUnitMoveSpeed(u)
        local player p = GetOwningPlayer(u)
        local data d = data.create(u, x, y, m)
        
        call SetPlayerAbilityAvailable(p, BOOK_ID, false) //Remove icons from hero UI
        call SetTimerData(d.t, d)
        call TimerStart(d.t, INTERVAL, true, function NearTree)
        
        set p = null    
        set u = null
    endfunction

    If those are the natives you are talking about, they're going to have to be called one way or another (whether it be from function Actions ... or from within the create method), right?

    If not... you've lost me :eek:

    Hmmm, perhaps... optional preload is also a possibility (if, for whatever mad reasons one may have, insist on going about something like you described).
    Or not preloading at all - I'm sure there are some who would disapprove, but I don't think any computer capable of running WC3 in a playable manner is going to stutter from loading an additional 2 (I think) abilities
     
  6. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    >efficiency
    Well, look at this code he has like this.

    JASS:
    private function Actions takes nothing returns boolean
        local unit u = GetTriggerUnit()
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real m = GetUnitMoveSpeed(u)
        local player p = GetOwningPlayer(u)
        local data d = data.create(u, x, y, m)
        
        if GetSpellAbilityId() != SPELL_ID then
            return false
        endif
        
        call SetPlayerAbilityAvailable(p, BOOK_ID, false) //Remove icons from hero UI
        call SetTimerData(d.t, d)
        call TimerStart(d.t, INTERVAL, true, function NearTree)
        
        set p = null    
        set u = null
        
        return false
    endfunction


    =>


    So either way, if you are casting any spell (even dummy spells) or casting Forestborn, you're calling the natives. But you could change it to check first then call like I said above and only call the natives if you have to.
     
  7. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    Hmm, unless I'm mistaken those natives will fire when ANY spell is cast, so thats why put it in the "method create" rather than the function Condition, then they will fire when the this spell is cast.

    EDIT: Yeah what tooltiperror said ^ , that makes sense.



    All the changes are making sense and I've done it all, but now I'm getting a syntax error for the timer. It says "t is not a static member of Forestborn_data" from here:

    JASS:
    private function Actions takes nothing returns boolean
        if GetSpellAbilityId() != SPELL_ID then
            return false
        endif
        
        set data = data.create(GetTriggerUnit())
        
        call SetPlayerAbilityAvailable(GetOwningPlayer(GetTriggerUnit()), BOOK_ID, false) //Remove icons from hero UI
        call SetTimerData(data.t, data)
        call TimerStart(data.t, INTERVAL, true, function NearTree)
        
        return false
    endfunction


    But it is?

    JASS:
    static method create takes unit u returns thistype
            local thistype this = thistype.allocate()
        
            set this.caster = u
            set this.level = GetUnitAbilityLevel(this.caster, SPELL_ID)
            set this.time = 0.
            set this.duration = Duration(this.level)
            set this.evasion = Evasion(this.level)
            set this.heal = Heal(this.level)
            set this.move = GetUnitMoveSpeed(this.caster)
            set this.movebonus = MoveBonus(this.level)
            set this.oldx = GetUnitX(this.caster)
            set this.oldy = GetUnitY(this.caster)
            set this.tree = false
    =>   set this.t = NewTimer()
                
            return this
        endmethod


    :confused:
     
  8. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    It helps to post your entire code.
     
  9. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    JASS:
    //=============================//
    //  F O R E S T B O R N        //
    //       by Tommerbob v2.0     //
    //=============================//
    //
    //Required: Jass NewGen, DestructableLib, TimerUtils  (All included)
    //
    //Implementation:
    //  1. Copy the "Spell" Category from the demo map.  It includes everything you need from the trigger editor.
    //  2. Copy the custom buff in the Object Editor.
    //  3. Copy the 4 custom abilities in the Object Editor.
    //  4. In the Configeration Section below, make sure all 4 ability ID's and the buff ID match up with the rawcodes 
    //  in the Object Editor.  You can see the rawcodes by pressing CTRL+D.
    //  5.  Edit the Configeration Section below to your liking and enjoy!
    //
    //Credits: Vexorian for TimerUtils, PitzerMike for DestructableLib
    //Thanks to many others for helping me with the code!    
    //
    scope Forestborn initializer init
    //
    //CONFIGERATION SECTION
    //
    globals
        private constant integer SPELL_ID =  'A000'  //rawcode of the Forestborn spell
        private constant integer BOOK_ID =   'A001'  //rawcode of the Evasion spell book
        private constant integer EVADE_ID =  'A002'  //rawcode of the evasion ability
        private constant integer MOVE_ID =   'A003'  //rawcode of the move speed bonus ability 
        private constant integer BUFF_ID =   'B000'  //rawcode of the Forestborn buff
            
        private constant real    INTERVAL =  0.5     //How often it checks for trees around caster.
        private constant real    AOE =       200.    //Max distance from a tree the caster may be.
            
        private constant real    DURATION_BASE = 10. //Base duration of spell.
        private constant real    DURATION_LVL =  5.  //Added duration for each level of spell.
            
        private constant integer EVADE_BASE =  50    //Base chance for evasion.  Set to 0 if you don't want the caster gaining evasion.
        private constant integer EVADE_LVL  =  0     //Amount added to base evasion for each level of spell.
            
        private constant real    HEAL_BASE =  8.     //Base amount that the caster is healed for each second.
        private constant real    HEAL_LVL =   4.     //Adds this amount to the base heal amount for each level of spell.
                                                     //If you don't want the caster healed, set both HEAL_BASE and HEAL_LVL to 0.
                                                     
        private constant integer MOVE_BASE = 25   //This is the move speed percent bonus the caster receives.  Set to 0 if no bonus.
        private constant integer MOVE_LVL =  0    //Amount added to base speed bonus for each level of spell.
        
        private constant boolean CAN_MOVE =  true  //Setting this to false will require the caster to remain stationary.
        
        private constant integer TRANSPARENCY = 25  //Caster becomes this transparent for some extra bling bling.
    endglobals
    //
    //END CONFIGERATION SECTION.  Do not edit past this point unless you know what you are doing.  
    //  
    private function Duration takes integer level returns real
        return DURATION_BASE + DURATION_LVL * (level)
    endfunction
    
    private function Evasion takes integer level returns integer
        return 1 + EVADE_BASE + EVADE_LVL * (level)
    endfunction
    
    private function Heal takes integer level returns real
        return (HEAL_BASE + HEAL_LVL * (level)) * INTERVAL
    endfunction
    
    private function MoveBonus takes integer level returns integer
        return 1 + MOVE_BASE + MOVE_LVL * (level)
    endfunction
    
    private struct data
        unit caster
        integer level
        integer evasion
        integer movebonus
        boolean tree
        real heal
        real move
        real time
        real duration
        real oldx
        real oldy
        real newx
        real newy
        timer t
        
        static method create takes unit u returns thistype
            local thistype this = thistype.allocate()
        
            set this.caster = u
            set this.level = GetUnitAbilityLevel(this.caster, SPELL_ID)
            set this.time = 0.
            set this.duration = Duration(this.level)
            set this.evasion = Evasion(this.level)
            set this.heal = Heal(this.level)
            set this.move = GetUnitMoveSpeed(this.caster)
            set this.movebonus = MoveBonus(this.level)
            set this.oldx = GetUnitX(this.caster)
            set this.oldy = GetUnitY(this.caster)
            set this.tree = false
            set this.t = NewTimer()
                
            return this
        endmethod
            
        method destroy takes nothing returns nothing
            call ReleaseTimer(.t)
        endmethod
            
    endstruct
    
    globals
        trigger ForestbornTrigger 
        private data D
    endglobals
        
    private function TreeCheck takes nothing returns nothing
        local destructable dest = GetEnumDestructable()
        
        if IsDestructableTree(dest) == true then
            if IsDestructableDead(dest) != true then
                set D.tree = true
            endif
        endif
        
        set dest = null
    endfunction
            
        
    private function NearTree takes nothing returns nothing
        local data d = GetTimerData(GetExpiredTimer())
        local location l = GetUnitLoc(d.caster)
     
        set D = d
        
        set D.tree = false
        
        call EnumDestructablesInCircleBJ(AOE, l, function TreeCheck) //Picks every destructable in range
        
        if D.tree == true then
            if CAN_MOVE != true then                //If the caster should not move, updates his location.
                set D.newx = GetUnitX(D.caster)
                set D.newy = GetUnitY(D.caster)
                if D.newx != D.oldx or D.newy != D.oldy then
                    set D.oldx = GetUnitX(D.caster)
                    set D.oldy = GetUnitY(D.caster)
                    call UnitRemoveAbility(D.caster, BOOK_ID)        //Remove bonuses if not stationary
                    call UnitRemoveAbility(D.caster, BUFF_ID)        //Remove buff
                else
                    call UnitAddAbility(D.caster, BOOK_ID)
                    call SetUnitAbilityLevel(D.caster, EVADE_ID, D.evasion) //Sets evasion if near a tree and is stationary. 
                    if HEAL_BASE > 0. then                                  //Heals caster if he is near a tree and is stationary.
                        call SetUnitState(D.caster, UNIT_STATE_LIFE, GetUnitState(D.caster, UNIT_STATE_LIFE) + D.heal)
                    endif
                endif
            else
                call UnitAddAbility(D.caster, BOOK_ID)
                call SetUnitAbilityLevel(D.caster, MOVE_ID, D.movebonus)  //Sets the move bonus if near a tree
                call SetUnitAbilityLevel(D.caster, EVADE_ID, D.evasion) //Sets evasion if near a tree    
                if HEAL_BASE > 0. then                                  //Heals caster if near a tree
                    call SetUnitState(D.caster, UNIT_STATE_LIFE, GetUnitState(D.caster, UNIT_STATE_LIFE) + D.heal)
                endif
            endif
        else 
            call UnitRemoveAbility(D.caster, BOOK_ID)     //Remove bonuses if not near a tree
            call UnitRemoveAbility(D.caster, BUFF_ID)     //Remove buff
        endif
        
        set d.time = d.time + INTERVAL
        if d.time >= d.duration then
            set D.tree = false
            call UnitRemoveAbility(d.caster, BOOK_ID)  //Removes evasion at end of duration
            call UnitRemoveAbility(D.caster, BUFF_ID)  //Remove buff
            call d.destroy()
        endif
        
        call RemoveLocation(l)
        set l = null
    endfunction
    
    private function Actions takes nothing returns boolean
        if GetSpellAbilityId() != SPELL_ID then
            return false
        endif
        
        set data = data.create(GetTriggerUnit())
        
        call SetPlayerAbilityAvailable(GetOwningPlayer(GetTriggerUnit()), BOOK_ID, false) //Remove icons from hero UI
        call SetTimerData(data.t, data)
        call TimerStart(data.t, INTERVAL, true, function NearTree)
        
        return false
    endfunction
    
    //======================================================
    
    private function init takes nothing returns nothing
        local unit u
    
        set ForestbornTrigger  = CreateTrigger()
    
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(t, Condition(function Actions))
        
        set u = CreateUnit(Player(15), 'Hpal', 0., 0., 0.)  //Preload ability
        call UnitAddAbility(u, BOOK_ID)
        call SetUnitAbilityLevel(u, MOVE_ID, MOVE_BASE)
        call SetUnitAbilityLevel(u, EVADE_ID, EVADE_BASE+1)
        call UnitRemoveAbility(u, MOVE_ID)
        call UnitRemoveAbility(u, BOOK_ID)
        call RemoveUnit(u)
        
        set u = null
    endfunction
    
    endscope
     
  10. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    >private struct data
    There's your problem. You can't have a struct and instance with the same name. See, you have things called static methods, like create. Create is static because you call the_name_of_the_struct.method instead of instance.method. Since your struct is named data, it doesn't know if you're calling a static method or normal method. Just name your struct Data.
     
  11. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    Oh I see.

    Another error. lol. "D is not a type that allows . syntax"...

    JASS:
    private function TreeCheck takes nothing returns nothing
        local destructable dest = GetEnumDestructable()
        
        if IsDestructableTree(dest) == true then
            if IsDestructableDead(dest) != true then
                set D.tree = true
            endif
        endif
        
        set dest = null
    endfunction


    But I set it here?

    JASS:
    private function NearTree takes nothing returns nothing
        local data d = GetTimerData(GetExpiredTimer())
        local location l = GetUnitLoc(d.caster)
     
        set D = d


    Ugh. Sorry, this is probably really trivial for you. Thanks for patience.
     
  12. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    JASS:
    globals
        trigger ForestbornTrigger 
        private data D
    endglobals


    data :)
     
  13. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    Okay, I changed the global data to Data, and now I'm getting an error here:

    JASS:
        set data = data.create(GetTriggerUnit())


    What should I change the global "private data D" to?

    lol. I'm getting more and more lost.
     
  14. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    Oh, my apologies. I was looking at the wrong code - I had assumed that tommerbob was doing this
    JASS:
    function ... takes ... returns ...
    if (incorrectSpell) then
      return false
    else
      //spell setup
    endfunction

    and that you were suggesting that it was more efficient to initialize within the create method than within the 'actions' function.

    You must declare a variable to assign the instance allocated by the .create method.

    Case sensitivity: data -> Data
     
  15. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    That's what I did, and I get the error above. :confused:
     
  16. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    data.create -> Data.create

    Notice that it's a static method, so you call it by the name of the struct, which here is Data.
     
  17. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    Right. Of course its something simple.

    And back to square one. (or maybe about square four?). Getting the same syntax error again for the timer. Says its not a static member of Data. I'll put the code in spoiler so my post isn't huge.

    Code:
    JASS:
    scope Forestborn initializer init
    //
    //CONFIGERATION SECTION
    //
    globals
        private constant integer SPELL_ID =  'A000'  //rawcode of the Forestborn spell
        private constant integer BOOK_ID =   'A001'  //rawcode of the Evasion spell book
        private constant integer EVADE_ID =  'A002'  //rawcode of the evasion ability
        private constant integer MOVE_ID =   'A003'  //rawcode of the move speed bonus ability 
        private constant integer BUFF_ID =   'B000'  //rawcode of the Forestborn buff
            
        private constant real    INTERVAL =  0.5     //How often it checks for trees around caster.
        private constant real    AOE =       200.    //Max distance from a tree the caster may be.
            
        private constant real    DURATION_BASE = 10. //Base duration of spell.
        private constant real    DURATION_LVL =  5.  //Added duration for each level of spell.
            
        private constant integer EVADE_BASE =  50    //Base chance for evasion.  Set to 0 if you don't want the caster gaining evasion.
        private constant integer EVADE_LVL  =  0     //Amount added to base evasion for each level of spell.
            
        private constant real    HEAL_BASE =  8.     //Base amount that the caster is healed for each second.
        private constant real    HEAL_LVL =   4.     //Adds this amount to the base heal amount for each level of spell.
                                                     //If you don't want the caster healed, set both HEAL_BASE and HEAL_LVL to 0.
                                                     
        private constant integer MOVE_BASE = 25   //This is the move speed percent bonus the caster receives.  Set to 0 if no bonus.
        private constant integer MOVE_LVL =  0    //Amount added to base speed bonus for each level of spell.
        
        private constant boolean CAN_MOVE =  true  //Setting this to false will require the caster to remain stationary.
        
        private constant integer TRANSPARENCY = 25  //Caster becomes this transparent for some extra bling bling.
    endglobals
    //
    //END CONFIGERATION SECTION.  Do not edit past this point unless you know what you are doing.  
    //  
    private function Duration takes integer level returns real
        return DURATION_BASE + DURATION_LVL * (level)
    endfunction
    
    private function Evasion takes integer level returns integer
        return 1 + EVADE_BASE + EVADE_LVL * (level)
    endfunction
    
    private function Heal takes integer level returns real
        return (HEAL_BASE + HEAL_LVL * (level)) * INTERVAL
    endfunction
    
    private function MoveBonus takes integer level returns integer
        return 1 + MOVE_BASE + MOVE_LVL * (level)
    endfunction
    
    private struct Data
        unit caster
        integer level
        integer evasion
        integer movebonus
        boolean tree
        real heal
        real move
        real time
        real duration
        real oldx
        real oldy
        real newx
        real newy
        timer t
        
        static method create takes unit u returns thistype
            local thistype this = thistype.allocate()
        
            set this.caster = u
            set this.level = GetUnitAbilityLevel(this.caster, SPELL_ID)
            set this.time = 0.
            set this.duration = Duration(this.level)
            set this.evasion = Evasion(this.level)
            set this.heal = Heal(this.level)
            set this.move = GetUnitMoveSpeed(this.caster)
            set this.movebonus = MoveBonus(this.level)
            set this.oldx = GetUnitX(this.caster)
            set this.oldy = GetUnitY(this.caster)
            set this.tree = false
            set this.t = NewTimer()
                
            return this
        endmethod
            
        method destroy takes nothing returns nothing
            call ReleaseTimer(.t)
        endmethod
            
    endstruct
    
    globals
        trigger ForestbornTrigger
    endglobals
    
    globals
        private Data D
    endglobals
        
    private function TreeCheck takes nothing returns nothing
        local destructable dest = GetEnumDestructable()
        
        if IsDestructableTree(dest) == true then
            if IsDestructableDead(dest) != true then
                set D.tree = true
            endif
        endif
        
        set dest = null
    endfunction
            
        
    private function NearTree takes nothing returns nothing
        local data d = GetTimerData(GetExpiredTimer())
        local location l = GetUnitLoc(D.caster)
     
        set D = d
        
        set D.tree = false
        
        call EnumDestructablesInCircleBJ(AOE, l, function TreeCheck) //Picks every destructable in range
        
        if D.tree == true then
            if CAN_MOVE != true then                //If the caster should not move, updates his location.
                set D.newx = GetUnitX(D.caster)
                set D.newy = GetUnitY(D.caster)
                if D.newx != D.oldx or D.newy != D.oldy then
                    set D.oldx = GetUnitX(D.caster)
                    set D.oldy = GetUnitY(D.caster)
                    call UnitRemoveAbility(D.caster, BOOK_ID)        //Remove bonuses if not stationary
                    call UnitRemoveAbility(D.caster, BUFF_ID)        //Remove buff
                else
                    call UnitAddAbility(D.caster, BOOK_ID)
                    call SetUnitAbilityLevel(D.caster, EVADE_ID, D.evasion) //Sets evasion if near a tree and is stationary. 
                    if HEAL_BASE > 0. then                                  //Heals caster if he is near a tree and is stationary.
                        call SetUnitState(D.caster, UNIT_STATE_LIFE, GetUnitState(D.caster, UNIT_STATE_LIFE) + D.heal)
                    endif
                endif
            else
                call UnitAddAbility(D.caster, BOOK_ID)
                call SetUnitAbilityLevel(D.caster, MOVE_ID, D.movebonus)  //Sets the move bonus if near a tree
                call SetUnitAbilityLevel(D.caster, EVADE_ID, D.evasion) //Sets evasion if near a tree    
                if HEAL_BASE > 0. then                                  //Heals caster if near a tree
                    call SetUnitState(D.caster, UNIT_STATE_LIFE, GetUnitState(D.caster, UNIT_STATE_LIFE) + D.heal)
                endif
            endif
        else 
            call UnitRemoveAbility(D.caster, BOOK_ID)     //Remove bonuses if not near a tree
            call UnitRemoveAbility(D.caster, BUFF_ID)     //Remove buff
        endif
        
        set D.time = D.time + INTERVAL
        if D.time >= D.duration then
            set D.tree = false
            call UnitRemoveAbility(D.caster, BOOK_ID)  //Removes evasion at end of duration
            call UnitRemoveAbility(D.caster, BUFF_ID)  //Remove buff
            call D.destroy()
        endif
        
        call RemoveLocation(l)
        set l = null
    endfunction
    
    private function Actions takes nothing returns boolean
        local Data data
        
        if GetSpellAbilityId() != SPELL_ID then
            return false
        endif
        
        set data = Data.create(GetTriggerUnit())
        
        call SetPlayerAbilityAvailable(GetOwningPlayer(GetTriggerUnit()), BOOK_ID, false) //Remove icons from hero UI
        call SetTimerData(Data.t, data)
        call TimerStart(Data.t, INTERVAL, true, function NearTree)
        
        return false
    endfunction
    
    //======================================================
    
    private function init takes nothing returns nothing
        set ForestbornTrigger  = CreateTrigger()
        local unit u
        
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(t, Condition(function Actions))
        
        set u = CreateUnit(Player(15), 'Hpal', 0., 0., 0.)  //Preload ability
        call UnitAddAbility(u, BOOK_ID)
        call SetUnitAbilityLevel(u, MOVE_ID, MOVE_BASE)
        call SetUnitAbilityLevel(u, EVADE_ID, EVADE_BASE+1)
        call UnitRemoveAbility(u, MOVE_ID)
        call UnitRemoveAbility(u, BOOK_ID)
        call RemoveUnit(u)
        
        set u = null
    endfunction
    
    endscope

    So before the problem was that I had both the struct and an instance named the same thing right? So it didn't know which to recognize for the timer. But I don't see how that is the same problem here? Does have have to do with my "private Data D" global? I have a feeling its something very simple. :eek:
     
  18. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    It helps to tell us in which function and which line in there you're getting the error.
     
  19. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
  20. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    Because Data is the struct, data is the instance of the struct. If you wanted one timer for every struct, then it'd be called a static timer, so you'd declare it as static timer t. But it is not, it's an instance variable, so you reference it by the name of the instance, which is data.
     

Share This Page