Spell Forestborn

tommerbob

Minecraft. :D
Reaction score
110
Forestborn
version 2.1
By Tommerbob





Forestborn.png


Description:
Born in the wild woodlands of the Elves, Sylvanus is at home among the trees. She gains increased life regeneration, move speed and evasion while close to trees.

Level 1: 12 hitpoints/second, 25% move speed, 50% evasion, lasts 15 seconds
Level 2: 16 hitpoints/second, 25% move speed, 50% evasion, lasts 20 seconds
Level 3: 20 hitpoints/second, 25% move speed, 50% evasion, lasts 25 seconds


This is my first (and last) spell in GUI, and my first submitted spell in vJass. Hopefully with many more to come. :) I decided to keep the GUI version included with a few updates, you can find it below.

Requires:
(vJass version only)
- Jass NewGen
- TimerUtils (included)
- DestructabeLib(included)

Pros:
- Highly customizable
- vJass or GUI, whatever you like
- MUI
- Lagless and Leakless
- Unique and original
- My first vJass spell :D

Cons:
- Requires several Object Editor data
- Requires NewGen and 2 systems (all included)
- It doesn't pay your bills


================================================================
vJass Version 2.1

Every aspect of the spell can be altered. Don't want evasion? Just say the word. Need the caster to remain stationary? Done. Want 500 hit points regenerated? You got it.

Code:
JASS:
//=============================//
//  F O R E S T B O R N        //
//       by Tommerbob v2.1     //
//=============================//
//
//Required: Jass NewGen, DestructableLib, TimerUtils  (All included)
//
//Implementation:
//  1. Copy the "Spell" Category from the demo map.  It includes everything you need.
//  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!
//
//Note: If you want to use the leaves SFX, you need to import it into your map.
//
//Credits: Vexorian for TimerUtils, PitzerMike for DestructableLib, Leaves.mdx from DotA
//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 string  SFX1 = "Leaves.mdx" //Bling bling
    private constant string  SFX2 = "Abilities\\Spells\\NightElf\\TargetArtLumber\\TargetArtLumber.mdl"  //More bling bling!
    private constant string  SFX1_ATTACH = "chest"  //Where the first SFX is attached to the caster
    private constant string  SFX2_ATTACH = "origin" //Where the second SFX is attached to the caster
    private constant integer TRANSPARENCY = 30  //Caster becomes this transparent (even more 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
    boolean hassfx
    effect sfx1
    effect sfx2
    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.hassfx = false
        set this.t = NewTimer()
            
        return this
    endmethod
        
    method onDestroy 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 data = GetTimerData(GetExpiredTimer())
    local location l = GetUnitLoc(data.caster)
 
    set D = data
    
    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
                if D.hassfx == true then
                    call SetUnitVertexColor(D.caster, 255, 255, 255, 255)  //Reset caster transparency
                    call DestroyEffect(D.sfx1)                             //Remove SFX
                    call DestroyEffect(D.sfx2)
                    set D.hassfx = false
                endif
            else
                if D.hassfx == false then
                    call SetUnitVertexColor(D.caster, 255, 255, 255, (100 - TRANSPARENCY))  //Set caster transparency
                    set D.sfx1 = AddSpecialEffectTarget(SFX1, D.caster, SFX1_ATTACH)  //Add SFX
                    set D.sfx2 = AddSpecialEffectTarget(SFX2, D.caster, SFX2_ATTACH)
                    set D.hassfx = true
                endif
                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
            if D.hassfx == false then
                call SetUnitVertexColor(D.caster, 255, 255, 255, (100 - TRANSPARENCY))  //Set caster transparency
                set D.sfx1 = AddSpecialEffectTarget(SFX1, D.caster, SFX1_ATTACH)        //Add SFX
                set D.sfx2 = AddSpecialEffectTarget(SFX2, D.caster, SFX2_ATTACH)
                set D.hassfx = true
            endif
            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 
        if D.hassfx == true then
            call SetUnitVertexColor(D.caster, 255, 255, 255, 255)  //Reset unit transparency
            call DestroyEffect(D.sfx1)                             //Remove SFX
            call DestroyEffect(D.sfx2)
            set D.hassfx = false
        endif
        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
        set D.hassfx = false
        call DestroyEffect(D.sfx1)                      //Remove SFX
        call DestroyEffect(D.sfx2)
        call SetUnitVertexColor(D.caster, 255, 255, 255, 255)  //Reset caster transparency
        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
    local unit u
    
    set ForestbornTrigger  = CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(ForestbornTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition(ForestbornTrigger, 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 RemoveUnit(u)
    
    set u = null
endfunction

endscope





================================================================
GUI Version 1.1b

Code:
Trigger:
  • Forestborn cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Forestborn
    • Actions
      • -------- GLOBALS --------
      • Set Forestborn_Caster = (Triggering unit)
      • -------- set these to the appropriate ability/buff for your Forestborn Ability in your map --------
      • Set Forestborn_Ability[0] = (Ability being cast)
      • Set Forestborn_Ability[1] = Forestborn (spellbook)
      • Set Forestborn_Ability[2] = Forestborn (evasion)
      • Set Forestborn_Ability[3] = Forestborn (movement)
      • Set Forestborn_Buff = Forestborn
      • -------- hit points healed per second --------
      • Set Forestborn_HealAmount = (8.00 + (4.00 x (Real((Level of Forestborn_Ability[0] for Forestborn_Caster)))))
      • -------- duration of spell --------
      • Set Forestborn_Duration = (10.00 + (5.00 x (Real((Level of Forestborn_Ability[0] for Forestborn_Caster)))))
      • -------- move speed percent bonus --------
      • Set Forestborn_MoveSpeed = 20
      • -------- evasion chance --------
      • Set Forestborn_EvasionChance = 50
      • -------- interval: how often it checks for trees around the caster --------
      • Set Forestborn_Interval = 0.50
      • -------- max distance caster can be from a tree --------
      • Set Forestborn_AOE = 200.00
      • -------- END GLOBALS --------
      • -------- SAVE INFO --------
      • Set Forestborn_NearTrees = False
      • Hashtable - Save Forestborn_Duration as 0 of (Key (Triggering unit)) in Forestborn_Table
      • Hashtable - Save Forestborn_NearTrees as 2 of (Key (Triggering unit)) in Forestborn_Table
      • Hashtable - Save Forestborn_HealAmount as 3 of (Key (Triggering unit)) in Forestborn_Table
      • Hashtable - Save Forestborn_MoveSpeed as 4 of (Key (Triggering unit)) in Forestborn_Table
      • Hashtable - Save Forestborn_EvasionChance as 5 of (Key (Triggering unit)) in Forestborn_Table
      • Unit Group - Add Forestborn_Caster to Forestborn_Group
      • Set Forestborn_GroupCount = (Forestborn_GroupCount + 1)
      • Countdown Timer - Start Forestborn_Timer as a Repeating timer that will expire in Forestborn_Interval seconds


Trigger:
  • Forestborn timer
    • Events
      • Time - Forestborn_Timer expires
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Forestborn_Group and do (Actions)
        • Loop - Actions
          • Set Forestborn_Caster = (Picked unit)
          • Set Forestborn_NearTrees = (Load 2 of (Key (Picked unit)) from Forestborn_Table)
          • Set Forestborn_Duration = (Load 0 of (Key (Picked unit)) from Forestborn_Table)
          • Set Forestborn_HealAmount = (Load 3 of (Key (Picked unit)) from Forestborn_Table)
          • Set Forestborn_MoveSpeed = (Load 4 of (Key (Picked unit)) from Forestborn_Table)
          • Hashtable - Save (Forestborn_Duration - Forestborn_Interval) as 0 of (Key (Picked unit)) in Forestborn_Table
          • Set Forestborn_Point = (Position of Forestborn_Caster)
          • Destructible - Pick every destructible within Forestborn_AOE of Forestborn_Point and do (Actions)
            • Loop - Actions
              • Unit - Order Forestborn_TreeChopper to Harvest (Picked destructible)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Current order of Forestborn_TreeChopper) Equal to (Order(harvest))
                • Then - Actions
                  • Set Forestborn_NearTrees = True
                • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Forestborn_NearTrees Equal to True
            • Then - Actions
              • Unit - Add Forestborn_Ability[1] to Forestborn_Caster
              • Player - Disable Forestborn_Ability[1] for (Owner of Forestborn_Caster)
              • -------- set move speed bonus --------
              • Unit - Set level of Forestborn_Ability[3] for Forestborn_Caster to Forestborn_MoveSpeed
              • -------- set evasion chance --------
              • Unit - Set level of Forestborn_Ability[2] for Forestborn_Caster to Forestborn_EvasionChance
              • -------- Heal caster --------
              • Unit - Set life of Forestborn_Caster to ((Life of Forestborn_Caster) + (Forestborn_HealAmount x Forestborn_Interval))
            • Else - Actions
              • Unit - Remove Forestborn_Ability[1] from Forestborn_Caster
              • Unit - Remove Forestborn_Buff buff from Forestborn_Caster
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Forestborn_Duration Less than or equal to 0.00
            • Then - Actions
              • Unit Group - Remove Forestborn_Caster from Forestborn_Group
              • Set Forestborn_GroupCount = (Forestborn_GroupCount - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Forestborn_Caster has buff Forestborn_Buff) Equal to True
                • Then - Actions
                  • Unit - Remove Forestborn_Ability[1] from Forestborn_Caster
                  • Unit - Remove Forestborn_Buff buff from Forestborn_Caster
                  • Set Forestborn_NearTrees = (Load 2 of (Key (Picked unit)) from Forestborn_Table)
                • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Forestborn_GroupCount Less than or equal to 0
                • Then - Actions
                  • Countdown Timer - Pause Forestborn_Timer
                • Else - Actions
              • Hashtable - Clear all child hashtables of child (Key (Picked unit)) in Forestborn_Table
            • Else - Actions
          • Custom script: call RemoveLocation(udg_Forestborn_Point)


How to Implement:
Code:
- Copy the Tree Chopper custom unit
- Copy all 4 custom abilities
- Copy the custom Forestborn buff
- Copy the Spell category, it includes everything you need
- You can configure the spell in the first trigger, under GLOBALS

=================================================================

Version Updates:
Code:
[b]Jass[/b]
2.1: Updated the code for better efficiency.  Added new special effects (and made them optional).  
2.0: Initial Jass release
[b]GUI[/b]
1.1b: Fixed minor code error
1.1: Simplified the code, replaced the transparency with a special effect
1.0: Initial GUI release

Credits:
- Vexorian for TimerUtils
- PitzerMike for DestructableLib
-Special thanks to everyone who has (knowingly or unknowingly) encouraged me to learn Jass after so long coding in GUI.
-Thanks to everyone who has helped with coding of the spell

Any comments are welcome! If you use in your map, please give credit. Enjoy!
 

Attachments

  • Forestborn (GUI) v1.1b.w3x
    51.8 KB · Views: 504
  • Forestborn (Jass) v2.1.w3x
    60.9 KB · Views: 465

tooltiperror

Super Moderator
Reaction score
231
This would be beautiful if it was made in JASS.

You should add a configurable globals section at the top.
 

tommerbob

Minecraft. :D
Reaction score
110
Updated the code, added configurable globals as you suggested. Thanks for the tip.
 

Flare

Stops copies me!
Reaction score
662
I would recommend setting Triggering Unit to a variable in Forestborn cast, along with Picked Unit in Forestborn timer - calling Triggering Unit 9 times (by the looks of it) is a tad excessive when you could simply refer to a variable, and the same applies with Picked Unit in the second trigger.

Efficiency may not be something that GUI is known for :)P), but improving it won't do the spell any harm :)

Also, the manner in which you're destroying Forestborn_Point is... peculiar - you're setting it within the unit group loop, but you're destroying it without any conditions, outside of the unit group loop.
Assuming my thinking is correct, this means that the last added instance will lose the reference to the triggering unit's position after one execution of the periodic trigger (since I doubt a hashtable can 'recall' a variable that you destroyed).

Going by the description of the spell (i.e. you gain bonuses while continuously in proximity to trees), you need to re-check the unit's position everytime the periodic trigger executes (incase the unit moves away from a tree) - the way you're doing it now (again, if my thinking is correct), would be that a unit's position is checked at the beginning, and it is assumed that the unit's position remains unchanged (since you are simply checking tree proximity against the location stored at the start)

I don't have The Frozen Throne installed (having lost the install disc and the CD key :p) so I can't verify these, although the way you're handling the leak removal could have an effect.

What I'd recommend (if the spell requires constant tree cover to maintain the effect) is to get rid of storing the unit's location in a hashtable in Forestborn cast, then do something like
Trigger:
  • Pick every unit in WhateverGroup and do actions:
    • Set PickedUnitVar = Picked Unit
    • Set PickedUnitLoc = Position of PickedUnitVar
    • ---Load your hashtable values---
    • Pick every destructible within X range of PickedUnitLoc and do actions:
      • ---Check if there are any elligible trees near PickedUnitLoc, and do whatever is appropriate if there are/aren't tress nearby---

Freehand and incomplete, but hopefully you get the idea

Then, towards the end of the Unit Group loop, destroy the location with [ljass]call RemoveLocation (udg_PickedUnitLoc)[/ljass] - just make sure this line isn't contained within an if block or something like that - this location must be removed for every unit that passes group the Unit Group loop and, as such, there shouldn't be any conditions associated with its removal

Then again, having said all that, it has been a good few months since I've used the World Editor, so I'd wait for verification of what I've said from somebody who would have a clearer memory of GUI-related stuff than I do, or else keep a backup copy of the spell map incase I turn out to be wrong and you're left with a broken spell :p

Also, as a recommendation for the tree check, the option to have multiple possible tree types as an elligible candidate would be nice - simply change Forestborn_DestructibleType to an array, and add another variable (let's call it Forestborn_TypeCount, for example) that will simply be the number of elligible tree-types used by the spell - then, when you are checking the destructible type here
Trigger:
  • Destructible - Pick every destructible within 200.00 of Forestborn_Point and do (Actions)
    • Loop - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Destructible-type of (Picked destructible)) Equal to Forestborn_DestructibleType
        • Then - Actions
          • Set Forestborn_NearTrees = True
        • Else - Actions

Simply make an integer loop from 1 (if you're starting your array indices at 1) to Forestborn_TypeCount and compare the destructible type of the enumerated destructible to Forestborn_DestructibleType[(Integer A)]
 

tommerbob

Minecraft. :D
Reaction score
110
Yeah, I thought about including an array for multiple tree-types, in case people have more than 1 tree type in their map. I was just lazy though. I'll work on that.

Setting (Triggering unit) and (Picked unit) to a variable is a good idea. I'll implement that too.

As far as your comments on location and removing it, it was a bit confusing, but I think I got your point. The spell works exactly as it should though, because of this line:

Trigger:
  • Hashtable - Save Handle Of(Position of (Picked unit)) as 1 of (Key (Picked unit)) in Forestborn_Table
    • Set Forestborn_Point = (Load 1 of (Key (Picked unit)) in Forestborn_Table)


So the location is removed and then updated each time the trigger runs, to prevent a location leak.

Thanks for the input.
 

Flare

Stops copies me!
Reaction score
662
So the location is removed and then updated each time the trigger runs, to prevent a location leak.
Ah, I missed that little bit in Forestborn timer, but you still have a wee issue if multiple instances are active at once (let's say, for example, 5 instances)

  • Forestborn timer is executed
  • Unit Group loop actions are handled - spell mechanics pertaining to instances 1, 2, 3, 4, 5 are dealt with
  • Unit Group loop actions end, actions outside the loop are dealt with
  • [ljass]call RemoveLocation (location)[/ljass] is called, the last location saved within Forestborn_Point is destroyed (i.e. #5, numbers 1 to 4 are not affected)
  • Forestborn timer is executed, again
  • Unit Group loop actions are handled again - a new location is saved within (1, Key of (Picked Unit)) of your hashtable - the only old value to have been destroyed was #5 (since you are only calling RemoveLocation once per trigger execution - collapse the Unit Group actions and you'll see what I mean)
  • Instances 1 to 4 start leaking locations since the points they generate are not removed

You must call RemoveLocation within the Unit Group actions to get rid of the leak - otherwise, you end up defining a location for every unit in the group, but only end up destroying one.

Also, after you pointed that out to me, it makes me wonder if
Hashtable - Save Handle Of(Position of (Triggering unit)) as 1 of (Key (Triggering unit)) in Forestborn_Table
from Forestborn cast no longer serves a purpose (and, in fact, creates a leak :p) - you aren't referencing that particular point at any stage (since you save a new location to (1, Key of that unit) before setting the variable) and you're overwriting the saved hashtable value almost immediately in Forestborn timer

Again, feel free to prove me wrong again - if I am wrong, I will GTFO of this thread and leave you be, to avoid any further mistakes on my part :)
 

Flare

Stops copies me!
Reaction score
662
Ahhh yeah ok. I gotcha. Fixed. (I think...?)

It appears to be fixed (although, again, a second opinion would be great here) - although, the hashtable usage for the location is, to be perfectly honest, pointless (and I probably should've pointed it out in my previous post but it never occured to me).

There is no need to store the point in the hashtable, if you are going to recall and destroy it (as good as) instantaneously - you could simply do
Trigger:
  • Set Forestborn_Point = Position of (Picked Unit)
    • ---Replacing (Picked Unit) with the variable you used to refer to the picked unit, preferably---

and remove this line
Trigger:
  • Hashtable - Save Handle Of(Position of (Picked unit)) as 1 of (Key (Picked unit)) in Forestborn_Table


The effect is, probably, negligible but there isn't any point in keeping (what would appear to be, unless I'm missing something else) redundant code in your triggers.
 

tommerbob

Minecraft. :D
Reaction score
110
Sorry, I don't understand. How is it redundant to store the point in the hashtable? I have to do that, or the spell won't be MUI, because every Picked Unit in the Unit group will reference the last point variable set, instead of the point that is stored for each of them in the hashtable.

Maybe I'm missing something though...I could be wrong.
 

Flare

Stops copies me!
Reaction score
662
Sorry, I don't understand. How is it redundant to store the point in the hashtable? I have to do that, or the spell won't be MUI, because every Picked Unit in the Unit group will reference the last point variable set, instead of the point that is stored for each of them in the hashtable.

Maybe I'm missing something though...I could be wrong.
Assuming you don't do anything stupid (and, of course, that I'm not talking utter sh*t), adding the change that I suggested in my previous post will not affect the 'MUI-ness' of the spell. The point for Unit 1 is created, used and destroyed before the Unit Group loop continues to Unit 2 - there is no risk of 'contamination' that would break the spell.

You only need the point for an instant (i.e. until that trigger execution has finished doing the Unit Group loop actions for Unit 1) and, once the next unit is handled by the Unit Group loop, it will not reference the previous point set (which was destroyed, so it couldn't be referenced anyway :p) because you ensure that a new point is set (i.e. Unit 2's position) before you do anything with that point variable.
 

tommerbob

Minecraft. :D
Reaction score
110
Okay, I finally got it updated. Sorry it took so long, finishing this spell was on the bottom of my to-do list the past several months.

Updated the code: set (triggering unit) and (picked unit) variables to make it easier to read, eliminated an unnecessary line as Flare suggested, and removed the buff immediately to make it smoother. (there was a brief delay for the buff to disappear after the spell ended.)

Also added a "how to implement" and "credits" section, put the screeny and code in spoilers, and cleaned up my first post to make it look nicer.

As far as getting it approved, anything else needs to be done?
 

Laiev

Hey Listen!!
Reaction score
188
it should be allowed to work with all tree, instead of only Ashenvale
 

tommerbob

Minecraft. :D
Reaction score
110
Ah yes, I knew there was something missing.

What's the best way to do that? Set them all to a variable array? There are ~80-100 different tree walls...

Is there a faster way to distinguish tree walls from other destructible types? Is there any function in GUI or Jass that is, say, something like this?...

Code:
Pick every (destructible) in (playable map area) of Type (Tree Wall for all palettes)

that would be the easiest, because then I could just pick all the trees in any map and would not have to set all ~100 of them individually to a variable array. But I don't know of any function like that.
 

Laiev

Hey Listen!!
Reaction score
188
well.. you can check if that is tree by creating a invisible work and order it to get lumber, if order return true, then you have a tree :D

this is used in the system by PitzerMike here.


EDIT: about the pick every dest. that don't exist (i think)
 

Weep

Godspeed to the sound of the pounding
Reaction score
401
Well, you can determine if a destructable is harvestable or not (incl. mushrooms, whatever) with this, or by translating its method into GUI with some custom script lines.

[edit] Lol, beaten.
 

tommerbob

Minecraft. :D
Reaction score
110
Ooooh, that looks like exactly what I need...but...I am a bit confused, I don't know much Jass. :/ I don't really know how to implement that system in my map and use it. I tried doing this:

Trigger:
  • Initialization
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Add Harvest (Ghouls Lumber) to (Last created unit)
      • Custom script: call UnitAddAbilityBJ( 'Aloc', GetLastCreatedUnit() )
      • Destructible - Pick every destructible in (Playable map area) and do (Actions)
        • Loop - Actions
          • Unit - Order (Last created unit) to Harvest (Picked destructible)


Trigger:
  • Untitled Trigger 001
    • Events
      • Unit - A unit Is issued an order targeting an object
    • Conditions
      • (Issued order) Equal to (Order(harvest))
    • Actions
      • Trigger - Turn off (This trigger)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ((Target destructible of issued order) is alive) Equal to True
        • Then - Actions
        • Else - Actions


But now I'm lost on what to do next. Do I have it right so far? How can I get a destructible type to return as a boolean? I guess my real question is how to do I translate what he did with his system into GUI?
 

Laiev

Hey Listen!!
Reaction score
188
well... to implement the system is a bit easy...

Trigger:
  • Destructible - Pick every destructible within 200.00 of Forestborn_Point and do (Actions)
    • Custom Script: set udg_TempBoolean = IsDestructableTree(GetEnumDestructable())
    • Loop - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • TempBoolean == true


then check if the TempBoolean is true in the condition...

i think is impossible to do that in gui (at least in the same way), since the BJ which issue order, cant be passed to a variable |:
 

tommerbob

Minecraft. :D
Reaction score
110
Aaaaaah okay. Thanks for the help. Messed around with some GUI functions and figured it out.

* Updated the code, now works with any tree-type destructible.
 

tooltiperror

Super Moderator
Reaction score
231
Fun, fun, fun, time to review.

JASS:
//
//CONFIGERATION SECTION
//

JASS:
//
//END CONFIGERATION.  Do not edit past this point unless you know what you are doing.  
//

Configeration~

Coding:
1) Your entire Actions should be inside the Condition function, because Conditions are quicker.
JASS:
function Conditions takes nothing returns boolean
return (condition)
endfunction

function Actions takes nothing returns nothing
// spell actions
endfunction

=>

function Actions takes nothing returns boolean
    if not (condition) then
        return false
    endif
    // spell code
    return false
endfunction

2) You can just override destroy nowadays.
JASS:

    method onDestroy takes nothing returns nothing
        set .caster = null
        call ReleaseTimer(.t)
    endmethod

JASS:

=>
method destroy takes nothing returns nothing
        call ReleaseTimer(.t)
endmethod

3) You don't have to null instance variables (variables in structs)
4) [del]The problem right now is you still autoattack enemies. You could make it so that you add a spell based on Shadowmeld and then cast it.[/del] Oh, my bad, I thought this was supposed to add invisibility.
5) The "Learn" tooltip displays Level 26.​
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top