1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Find your way through the deepest dungeon in the 18th Mini Mapping Contest Poll.
    Dismiss Notice
  3. A brave new world lies beyond the seven seas. Join the 34th Modeling Contest today!
    Dismiss Notice
  4. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

BouncingGlaives v1.0.5

Submitted by Geshishouhu
This bundle is marked as approved. It works and satisfies the submission rules.
Well, after reading posts on THW for almost two years, here comes my first spell! It is a simple but fun spell. I hope you can enjoy it. :) Since this is my first spell, it sure needs improvement, so comments are welcomed.

Description: The caster hurls several glaives from his position. When the glaive hits an obstacle it will bounce to other direction. By default, each glaive can bounce five times if it keeps hitting obstacles. However, if an enemey is hit by the glaive, the glaive will damage the enemy before its vanishment.

Credits

Credits:
To Anitarf and Maker for makeing the IsTerrainWalkable library
To Malhorne for making the vJass spell tutorial
To PurgeandFire for answering all my questions
To Vexorian for making vJass
To many others how have helped me, such as Bannar, Chobibo, DIMF, muzzel and the list can go on and on.


Implementation

1. Copy the "Library" and "Spell" folders to your map;
2. Copy the dummy ('h000') to your map, or you can create your own if you like.
3. Import the glaive model if you like it, or you can use any model you want.
Done!


Code (vJASS):

scope BouncingGlaives initializer init
/*************************************************************************************
*   BouncingGlaves v1.0.5
*   Discription: the caster hurls several glaives from his position. When the glaive hits an obstacle
*   it will bounce to other direction. By default, each glaive can bounce five times if it
*   keeps hitting obstacles. However, if an enemey is hit by the glaive, the glaive will
*   damage the enemy before its vanishment.
*
*************************************************************************************
*
*   Requires IsTerrainWalkable
*
*************************************************************************************
*
*   Credits
*
*       To Anitarf and Maker for makeing the IsTerrainWalkable library
*       To Malhorne for making the vJass spell tutorial
*       To PurgeandFire for answering all my questions
*       To Vexorian for making vJass
*
************************************************************************************/

   
    globals
        //Configurations===============================================================
        //The dummy ID, please do match this ID to your dummy ID.
        private constant integer DUM_ID = 'h000'
        //The ability ID, please match this ID to your ability ID.
        private constant integer ABID = 'A000'
        //The maximum number of glaives that can be hurled by the Hero. Do not set this more than 50.
        private constant integer MAX_NUM_OF_GLAIVE = 30
        //The number of glaive that will be hurled by the Hero at level one.
        private constant integer NUM_OF_GLAIVE = 3
        //How many more glaives when you level up (for each level).
        private constant integer LEVEL_UP = 3
        //The bouncing times of each glaive.
        private constant integer BOUNCE = 5
        //Whether the bouncing angle is random or not. If false, do remember to set the ANGLE bellow.
        private constant boolean RANDOM_ANGLE = true    
        //The bouncing angle. Only works when the above RANDOM_ANGLE is false.
        private constant real ANGLE = 180
        //How fast the glaive moves.
        private constant real SPEED = 25
        //How far away the glaives will appear from the Hero.
        private constant real DISTANCE = 300
        //The attack type of the damage done to the enemy.
        private constant attacktype AT = ATTACK_TYPE_CHAOS
        //The damage type of the damage done to the enemy.
        private constant damagetype DT = DAMAGE_TYPE_DEMOLITION
        //The detection AOE of the glaive. If the distance between the enemy and the glaive exceeds this range, the glaive will not damage it.
        private constant real RANGE = 128
        //The effect when the glaive hits an enemy.
        private constant string FX = "Abilities\\Spells\\Undead\\FrostArmor\\FrostArmorDamage.mdl"
        //The attach point of the effect
        private constant string ATTACH = "origin"
    endglobals
   
    //The damage amount upon hiting an enemy.
    private constant function GetDamage takes integer level returns real
        return 35. + (10*level)
    endfunction
   
    //How many glaives for each level
    private constant function GetNumberofGlaives takes integer level returns integer
        return NUM_OF_GLAIVE + (level -1 ) * LEVEL_UP
    endfunction
   
    //Filter out which units should be damaged.
    private function FilterUnits takes unit u, player p returns boolean
        return UnitAlive(u) and IsUnitEnemy(u, p)
    endfunction
   
    //Configuration Ends====================================================================
   
    globals
        private integer deindex = -1
        private timer t = CreateTimer()
        private group g = CreateGroup()
    endglobals
   
    native UnitAlive takes unit id returns boolean
   
    //Uses this struct to store data.
    private struct Data
       
        integer array bounce[50]   //bouncing times of a glaive
        unit array dum[50]         //glaive dmummy
        real array angle[50]       //bouncing angle
        real dmg                    //damage when hits an enemy
        real X                   //x coordinate of the spell casting unit
        real Y                   //y coordinate of the spell casting unit
        integer num                 //the number of glaives
        player p
       
        method destroy takes nothing returns nothing
            if deindex == -1 then
                call PauseTimer(t)
            endif            
            set this.p = null
            call this.deallocate()
        endmethod
    endstruct
   
    globals
        private Data array data
    endglobals

    private function Loop takes nothing returns nothing
        local integer i = 0
        local integer ii = 0
        local integer iii = 0
        local Data this
        local real x
        local real y
        local boolean bb = false
        local unit u
        //Loop through all the instances
        loop
            exitwhen i > deindex
            set this = data[i]
           
            loop
                exitwhen ii > this.num
               
                //Calculate the new coordinates for the glaive
                set x = GetUnitX(this.dum[ii]) + SPEED * Cos(this.angle[ii] * bj_DEGTORAD)
                set y = GetUnitY(this.dum[ii]) + SPEED * Sin(this.angle[ii] * bj_DEGTORAD)
               
                //Moves the glaive if it hasn't reach it maximum bouncing limite and the terrain is walkable
                if this.bounce[ii] > 0 and IsTerrainWalkable(x,y) then
                    call SetUnitX(this.dum[ii],x)
                    call SetUnitY(this.dum[ii],y)
                   
                    //Check if there are enemys near the glaive
                    call GroupEnumUnitsInRange(g,x,y,RANGE,null)
                    loop
                        set u = FirstOfGroup(g)
                        exitwhen u == null
                        call GroupRemoveUnit(g,u)
                        //If the glaive hits an enemy, does damage and remove the glaive
                        if FilterUnits(u,this.p) then
                            call UnitDamageTarget(this.dum[ii],u,this.dmg,true,false,AT,DT,null)
                            call DestroyEffect(AddSpecialEffectTarget(FX,u,ATTACH))
                            set this.bounce[ii] = 0
                            set u = null
                            //Only needs the loop to operate once, so ends it here.
                            exitwhen true
                        endif
                    endloop
                   
                else
                    //If the glaive hits an obstacle, it bounces.
                    static if RANDOM_ANGLE then
                        set this.angle[ii] = this.angle[ii] - GetRandomReal(90,180)
                    else
                        set this.angle[ii] = this.angle[ii] - ANGLE
                    endif
                   
                    //Setting new locations for the bouncing glaive
                    set x = GetUnitX(this.dum[ii]) + SPEED * Cos(this.angle[ii] * bj_DEGTORAD)
                    set y = GetUnitY(this.dum[ii]) + SPEED * Sin(this.angle[ii] * bj_DEGTORAD)
                    call SetUnitX(this.dum[ii],x)
                    call SetUnitY(this.dum[ii],y)
                   
                    //Reduce its remaining bouncing times
                    set this.bounce[ii] = this.bounce[ii] - 1
                   
                    //If the glaive has reached its maximum bouncing times, it will be removed.
                    if this.bounce[ii] <= 0 then
                        call RemoveUnit(this.dum[ii])
                        set this.dum[ii] = null
                       
                        //Checks if all glaives are removed
                        loop
                            exitwhen iii > this.num
                            if UnitAlive(this.dum[iii]) then
                                set bb = true
                            endif
                            set iii = iii + 1
                        endloop
                       
                        set iii = 0
                        //If all glaives are removed then deallocates the instance
                        if not bb then
                            set data[i] = data[deindex]
                            set i = i - 1
                            set deindex = deindex - 1
                            //Ends the loop
                            set ii = this.num + 10
                            call this.destroy()
                            set bb = false
                        endif
                    endif
                endif
                set ii = ii + 1
            endloop
            set ii = 0
            set i = i + 1
        endloop
    endfunction
   
   
    private function onCast takes nothing returns boolean
        local unit u = GetTriggerUnit()
        local integer i = 0
        local real angle = 0
        local real newX
        local real newY
        local integer n
        local integer lvl = GetUnitAbilityLevel(u,ABID)
        local Data this = Data.create()
        //Stores data in the struct
        set this.p = GetTriggerPlayer()
        set this.X = GetUnitX(u)
        set this.Y = GetUnitY(u)
        set this.num = GetNumberofGlaives(lvl) - 1
        set this.dmg = GetDamage(lvl)
        set n = GetNumberofGlaives(lvl)
        if this.num >= MAX_NUM_OF_GLAIVE then
            set this.num = MAX_NUM_OF_GLAIVE - 1
            set n = MAX_NUM_OF_GLAIVE
        endif
       
        //Creates glaives=======================================
        loop
            exitwhen i > this.num
            set newX = this.X + DISTANCE * Cos(angle * bj_DEGTORAD)
            set newY = this.Y + DISTANCE * Sin(angle * bj_DEGTORAD)
            set this.dum[i] = CreateUnit(this.p,DUM_ID,newX,newY,angle)
            set this.bounce[i] = BOUNCE + 1
            set this.angle[i] = angle
            set angle = angle + 360/n
            set i = i + 1
        endloop
        //=======================================================
        set deindex = deindex + 1
        set data[deindex] = this
        if deindex == 0 then
            call TimerStart(t,0.0312500,true,function Loop)
        endif
        set u = null
        return false
    endfunction
   
    private function run takes nothing returns boolean
        if GetSpellAbilityId() == ABID then
            call onCast()
        endif
        return false
    endfunction

    //===========================================================================
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(t, Condition(function run))
        set t = null  //I don't think it needs to be nulled here, but nulling it won't hurt.
    endfunction
endscope

 


change log

v.1.0.5: Final version, minor bug fix.
v.1.0.4: now the number of glaives increases when the ability levels up, also fixs a minor bug.
v.1.0.3: fixes a possible unit leak.
v.1.0.2: fixes a bug and uses one global group variable for better efficiency.
v.1.0.1: makes some improvement.
v.1.0: initial release.



Keywords:
glaive, bounce, ninja, chi, hunter, bouncing
Contents

只是另外一张魔兽争霸III的地图 (Map)

Reviews
Moderator
18th Apr 2016 Your resource has been reviewed by BPower. In case of any questions or for reconfirming the moderator's rating, please make use of the Quick Reply function of this thread. Review: Totally fun to test. This could be useful for...
  1. 18th Apr 2016

    General Info

    Your resource has been reviewed by BPower.
    In case of any questions or for reconfirming the moderator's rating,
    please make use of the Quick Reply function of this thread.

    Review:

    Totally fun to test. This could be useful for a mini-contest/map.

    The configuration options could be a bit more level orientated.
    I would say that vector calculation should be done in radians and not degrees.
    The code is ok. Yet there is room for optimization.

    Troubleshooting:

    • The timer timeout could be stored into a descriptive constant.
      ---
    • You don't update the dummy facing on bounce.
      ---
    • Dummy units could be paused to spare computation time.
      ---
    • static method onCast could return null instead of a boolean.
      ---
    • local integer n could be directly initialized to GetNumberofGlaives(lvl) by moving n under integer lvl.
      this.num would then be n - 1.
      ---
    • velocity in XY could be cached to get rid of extra
      Cos/Sin
      calculations.
      ---
    • Code (vJASS):
      //Ends the loop
      set ii = this.num + 10
      ^exitwhen true?

    Review changelog:
    1. -
     
  2. nhocklanhox6

    nhocklanhox6

    Joined:
    Feb 12, 2012
    Messages:
    335
    Resources:
    28
    Models:
    6
    Spells:
    21
    Tutorials:
    1
    Resources:
    28
    SPEED * Cos(this.angle[ii] * bj_DEGTORAD

    SPEED * Sin(this.angle[ii] * bj_DEGTORAD
    should be used as a global variables.

    You've leaked group g in Loop function, just create a global
    group
    and use it for entrie spell.
    call GroupEnumUnitsInRange(g,x,y,128,null)


    Area of effects should be in configurable part.

    DISTANCE * Cos(angle * bj_DEGTORAD)

    DISTANCE * Sin(angle * bj_DEGTORAD)


    Should be outside the loop.

    Since you've used struct to create spell (Yup!, it's vjass). Your spell can't become a jass because it's contain vjass func, so I suggest you to create spell using struct (vjass). If you want your spell become jass. Just use indexing instead of struct Data. Or if you really want it become a real vjass, put your code inside struct Data and recode it ;)..

    exitwhen ii > this.num - 1
    ,
    this.num - 1
    in Loop func calculating every .03125s, you have to fix it.

    call DestroyEffect(AddSpecialEffectTarget(FX,u,"origin"))
    ,
    "origin"
    should be in configurable part.

    Fun spell anyway :).
     
    Last edited: Jul 30, 2014
  3. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,099
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    Code (vJASS):
            integer array bounce[50]      //bouncing times of a glaive
            unit array dum[50]              //glaive dummy
            real array angle[50]
    Drop the size, it does nothing.
    Code (vJASS):
        globals
            private Data array data
        endglobals
    You aren't coding this in GUI, you would rather make use of linked list instead of iterating an array.

    Code (vJASS):
    local group g

    loop
        set g = CreateGroup()
        // Enumerate (...)
    endloop
    should be just
    local group g = CreateGroup()
    . Each GroupEnum call clears the group, thus using just one is fine. You could even argue to use global group instead. The Create/Destroy adds unnecessary overhead.

    set this.p = GetOwningPlayer(tri)
    ->
    set this.p = GetTriggerPlayer()

    Code (vJASS):
    if this.bounce[ii] > 0 and IsTerrainWalkable(x,y) then
        // stuff
    else
        if this.bounce[ii] <= 0 or not IsTerrainWalkable(x,y) then
            // stuff
    You already know after the first "if" that either bounces are <= 0 or terrain isn't walkable thus the second if statement is pointless, drop it.

    Code (vJASS):
    //Ends the loop.
    set u = null
    Nulling "u" is unnecessary, your FoG loop will do this for you:
    exitwhen u == null
    .

    Improve the readability, it's bad right now. Insert breaks between locals/code and separate if statements as well as loops so we can finally see whats goin on instead of looking at code bloat.

    You have forgotten to null the trigger in
    init()
    function.
     
  4. Mythic

    Mythic

    Media Manager

    Joined:
    Apr 24, 2012
    Messages:
    8,274
    Resources:
    144
    Models:
    122
    Icons:
    6
    Skins:
    1
    Maps:
    5
    Spells:
    6
    Reforged HD Icons:
    1
    Tutorials:
    3
    Resources:
    144
    Glaives have shadows. ;_;

    Idea and effects are nice though, I like the glaive model.
     
  5. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,549
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    @Bannar, no need to null the local trigger unless you don't destroy it.

    Also players don't need to be nulled, but it's not really something bad to do.^^

    @nhocklanhox6, where is the sense you moved
    call GroupRemoveUnit(g,u)
    to the end of loop in your example?

    Can't find this part. ^^
    "Your spell can't become jass, because it uses some vjass" What are you referring to? :eekani:

    @Geshishouhu, an expiration timer would be cool, that defines the max life duration for a glaive.
     
  6. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    native UnitAlive takes unit u returns boolean
    -->
    native UnitAlive takes unit id returns boolean
    it maybe works with "u" aswell, but I'm not sure about this.
     
  7. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,099
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    For me it works just fine. However, it might be more convenient to use exact declaration from .j script
     
  8. Geshishouhu

    Geshishouhu

    Joined:
    Oct 11, 2012
    Messages:
    709
    Resources:
    1
    Spells:
    1
    Resources:
    1
    First of all, thanks for the comment.
    I think I have destroyed the group right after the end of the loop.
    I will fix these.

    JasssHelper throws me errors if I do not specify an array size. :(
    Thanks for pointing these out.
    I set u to null because I want to stop the loop immediately.

    I will fix this for more readibility.

    I didn't do that because I want the glaive to keep bouncing until it reaches the maximum bouncing limit. But maybe I will make your opinion as a configurable option for players. Thanks.

    I think it should be fine, but I will change it. Thanks.
     
  9. nhocklanhox6

    nhocklanhox6

    Joined:
    Feb 12, 2012
    Messages:
    335
    Resources:
    28
    Models:
    6
    Spells:
    21
    Tutorials:
    1
    Resources:
    28
    @IceManBo: Here ^^!
    Code (vJASS):
     loop
                exitwhen i > this.num - 1
                set newX = this.triX + DISTANCE * Cos(angle * bj_DEGTORAD)
                set newY = this.triY + DISTANCE * Sin(angle * bj_DEGTORAD)
                set this.dum[i] = CreateUnit(this.p,DUM_ID,newX,newY,angle)
                set this.bounce[i] = BOUNCE + 1
                set this.angle[i] = angle
                set angle = angle + 360/this.num
                set i = i + 1
            endloop


    and about
    GroupRemoveUnit
    , I am wrong, my bad >.<..., This is none sense ==!..Just my habit >.<...

    My wrong imagination: u=FirstOfGroup(g)
    GroupRemoveUnit(g,u) <=> u=null


    I don't know 0.0..

    Yes. But..

    For more information: http://world-editor-tutorials.thehelper.net/cat_usersubmit.php?view=138374

    Native
    GroupEnumUnitsInRange
    doesn't requires
    DestroyGroup
    like
    ForGroup
    or
    ForGroupBJ
    because it used
    GroupRemoveUnit
    , which remove unit from the group, after removed all the unit, that group will be empty!. And yes, that group still have
    CreateGroup()
    until
    DestroyGroup
    is called. But what the point ?, because that group is empty, just simply to re-use it instead of
    local group g
    and
    CreateGroup()
    and
    DestroyGroup
    and
    g=null
    every .0312500s.
     
    Last edited: Jul 29, 2014
  10. Geshishouhu

    Geshishouhu

    Joined:
    Oct 11, 2012
    Messages:
    709
    Resources:
    1
    Spells:
    1
    Resources:
    1
    So
    GroupEnumUnitsInRange
    leaks anyway unless I use a global group variable? :(
     
  11. nhocklanhox6

    nhocklanhox6

    Joined:
    Feb 12, 2012
    Messages:
    335
    Resources:
    28
    Models:
    6
    Spells:
    21
    Tutorials:
    1
    Resources:
    28
    Yes and no, if that native is used right, it will be fine, but for more safer, you have to use global group variable!.

    For global group variable:
    When using group in period func to operate some stuff every <s>, try to avoid
    DestroyGroup
    , just use
    GroupRemoveUnit
    instead!

    Read more:

     
  12. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,099
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    Code (vJASS):
                        loop
                            set u = FirstOfGroup(g)
                            exitwhen u == null
                            call GroupRemoveUnit(g,u)

                            set u = null
                        endloop
    What do you want to stop? When you set unnecessarily u to null, loop starts new iteration anyways and now it sets u to returned value from FirstOfGroup() function. When group if empty (and it will be sooner or later) the FirstOfGroup() will automatically return null, thus your last operation wasn't needed at all.

    GroupEnum doesn't leak if handled properly, but it clears the group prior to actual enumeration.

    Btw, if you just drop the size, then yes it will throw you some errors. Code has to be reorganized (approach a bit modified) to compile properly.
     
  13. Geshishouhu

    Geshishouhu

    Joined:
    Oct 11, 2012
    Messages:
    709
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Oh, I got your point. I set u to null because I only want the loop to operate once. But you are right, setting u to null causes the FoG function to return a value to u again. I will fix that.

    So do I correctly handle the GroupEnum issue? I have read the tutorial and it recommends using a global group variable, maybe I should use GroupUtils. However, I want my spell less dependent on other resources, so if someone want to use it, it will be easier for them to import.

    I am not sure what do you mean by reorganizing the code to avoid the JassHelper error. Sorry.. :(

    Thanks and I have read the tutorial. It recommends a global group...
     
  14. Geshishouhu

    Geshishouhu

    Joined:
    Oct 11, 2012
    Messages:
    709
    Resources:
    1
    Spells:
    1
    Resources:
    1
    v.1.0.1: makes some improvement according to the posts above.
     
  15. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,099
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    If you want to use list instead of iteration method (through an array) and get rid of sized arrays then yes, your code has to be reorganized.
    However, you will feel better with your code once you do this, the spell will be more efficient.

    You don't need to do anything tho :)
     
  16. Geshishouhu

    Geshishouhu

    Joined:
    Oct 11, 2012
    Messages:
    709
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Oh, got it. I will use a linked list if that is required for approval. :)
    +Rep to you and nhocklanhox6
     
  17. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,099
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    It won't be. You will never need to code your stuff like someelse does.

    Personaly, before I finally reached the point where I have "static framework" for my spells I've been using plenty of approaches, at the end I'm convenient that I've went through most of available methods regardless if it's GUI or jass extension. After such process, you got enough knowledge of what to do and when to do it :)
     
  18. Geshishouhu

    Geshishouhu

    Joined:
    Oct 11, 2012
    Messages:
    709
    Resources:
    1
    Spells:
    1
    Resources:
    1
    I think you are right, it is good to know the pros and cons for each approach. You sound like a Zen master BTW. :D
     
  19. Mythic

    Mythic

    Media Manager

    Joined:
    Apr 24, 2012
    Messages:
    8,274
    Resources:
    144
    Models:
    122
    Icons:
    6
    Skins:
    1
    Maps:
    5
    Spells:
    6
    Reforged HD Icons:
    1
    Tutorials:
    3
    Resources:
    144
    I take my statement back, shadows look cool on missiles but only if it is set to flying type