Spell Forestborn

tommerbob

Minecraft. :D
Reaction score
110
Your entire Actions should be inside the Condition function, because Conditions are quicker.

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


You can just override destroy nowadays.

Done.

You don't have to null instance variables (variables in structs)

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

Oh, my bad, I thought this was supposed to add invisibility.

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?

The "Learn" tooltip displays Level 26.

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

tooltiperror

Super Moderator
Reaction score
231
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 [LJASS]thistype[/LJASS] rather than [LJASS]data[/LJASS] in your struct. That's because if you rename your struct later to Pizzacakeuobengtbewjngw or anything, it won't matter, because you used [LJASS]thistype[/LJASS]. Also, rename it to Data. Capitalized struct names are all the rage (convention). Use [LJASS]this[/LJASS] too.

Now, change your [LJASS]create[/LJASS] 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 [LJASS]data.x[/LJASS].

>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!
 

Flare

Stops copies me!
Reaction score
662
tommerbob said:
Like this?
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.


tooltiperror said:
More efficient. Then you can just reference the locals that you need in that function as data.x.
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
You can gather the information you provide as parameters with one parameter, the triggering unit
when you're talking about being more efficient


tooltiperror said:
Using Player 14 as well as the rawcode of the dummy should be configurable, too.
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
 

tooltiperror

Super Moderator
Reaction score
231
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
Firing off five unneccesary natives each time any spell is cast sounds inefficient to me.

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.
Reasonable.

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
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.
 

Flare

Stops copies me!
Reaction score
662
Firing off five unneccesary natives each time any spell is cast sounds inefficient to me.
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 [ljass]function Actions ...[/ljass] or from within the create method), right?

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

Could be avoided by changing the constant to another hero.
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
 

tooltiperror

Super Moderator
Reaction score
231
>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


=>

  • A unit cast a spell (not Forestborn):
    • call [LJASS]GetTriggerUnit[/LJASS]
    • call [LJASS]GetUnitX[/LJASS], [LJASS]GetUnitY[/LJASS]
    • call [LJASS]GetUnitMoveSpeed[/LJASS]
    • call [LJASS]GetOwningPlayer[/LJASS]
    • call [LJASS]GetSpellAbilityId[/LJASS] and compare it to the constant
    • return [LJASS]false[/LJASS]
  • A unit casts Forestborn:
    • call [LJASS]GetTriggerUnit[/LJASS]
    • call [LJASS]GetUnitX[/LJASS], [LJASS]GetUnitY[/LJASS]
    • call [LJASS]GetUnitMoveSpeed[/LJASS]
    • call [LJASS]GetOwningPlayer[/LJASS]
    • call [LJASS]GetSpellAbilityId[/LJASS] and compare it to the constant
    • [LJASS]// spell actions and create data[/LJASS]

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.
 

tommerbob

Minecraft. :D
Reaction score
110
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 [ljass]function Actions ...[/ljass] or from within the create method), right?

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:
 

tommerbob

Minecraft. :D
Reaction score
110
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
 

tooltiperror

Super Moderator
Reaction score
231
[LJASS]>private struct data[/LJASS]
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.
 

tommerbob

Minecraft. :D
Reaction score
110
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.
 

tommerbob

Minecraft. :D
Reaction score
110
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.
 

Flare

Stops copies me!
Reaction score
662
Well, look at this code he has like this.
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.

set data = data.create(GetTriggerUnit())
You must declare a variable to assign the instance allocated by the .create method.

Okay, I changed the global data to Data, and now I'm getting an error here:
What should I change the global "private data D" to?
Case sensitivity: data -> Data
 

tooltiperror

Super Moderator
Reaction score
231
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.
 

tommerbob

Minecraft. :D
Reaction score
110
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:
 

tooltiperror

Super Moderator
Reaction score
231
It helps to tell us in which function and which line in there you're getting the error.
 

tommerbob

Minecraft. :D
Reaction score
110

tooltiperror

Super Moderator
Reaction score
231
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 [ljass]static timer t[/ljass]. But it is not, it's an instance variable, so you reference it by the name of the instance, which is data.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top