Spell Forestborn

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

  1. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    Forestborn
    version 2.1
    By Tommerbob





    [​IMG]


    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
    
    Any comments are welcome! If you use in your map, please give credit. Enjoy!
     

    Attached Files:

    • Like Like x 2
  2. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    This would be beautiful if it was made in JASS.

    You should add a configurable globals section at the top.
     
    • Like Like x 1
  3. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    Updated the code, added configurable globals as you suggested. Thanks for the tip.
     
  4. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    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 call RemoveLocation (udg_PickedUnitLoc) - 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)]
     
  5. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    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.
     
  6. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    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
    • call RemoveLocation (location) 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
    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 :)
     
  7. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    Ahhh yeah ok. I gotcha. Fixed. (I think...?)
     
  8. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    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.
     
  9. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    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.
     
  10. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    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.
     
  11. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    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?
     
  12. Laiev

    Laiev Hey Listen!!

    Ratings:
    +187 / 0 / -0
    it should be allowed to work with all tree, instead of only Ashenvale
     
  13. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    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.
     
  14. Laiev

    Laiev Hey Listen!!

    Ratings:
    +187 / 0 / -0
    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)
     
  15. Weep

    Weep Godspeed to the sound of the pounding

    Ratings:
    +400 / 0 / -0
    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.
     
  16. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    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?
     
  17. Laiev

    Laiev Hey Listen!!

    Ratings:
    +187 / 0 / -0
    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 |:
     
  18. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    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.
     
  19. tommerbob

    tommerbob Minecraft. :D

    Ratings:
    +110 / 0 / -0
    *Major update.

    Check it out. :)
     
  20. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    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.​
     

Share This Page