Wc3C.net

Wc3C.net (http://www.wc3c.net/forums.php)
-   vJass Spells (http://www.wc3c.net/forumdisplay.php?f=647)
-   -   Boomerang! (http://www.wc3c.net/showthread.php?t=106276)

Deaod 06-08-2009 07:11 AM

Boomerang!
 
2 Attachment(s)
Throw a deadly boomerang at your enemies.

This uses vJass (JassHelper 0.9.H.0 and up), GroupUtils, xefx, DestructableLib and IsTerrainWalkable.

In-Game-Commands

- "-reset": spawns some footmen
- "-level <level>": Set the heros level to the level you specified. Note that you cannot decrease it that way.
- "-handleid": creates a location, displays its handlid-0x100001, and then destroys it.
- "-credits": displays credits
- "-commands": displays this list
- "-clear": removes all messages ingame



Changelog

06/03/2009 - Version 1.0.0
- initial release

06/04/2009 - Version 1.1.0
- stopped the spell from triggering when using unrelated abilities
- added a minimum range
- added support for trees
- added damage reduction when hitting multiple targets
- added a hit sound
- switched from direct dummy unit usage to xe

06/05/2009 - Version 1.2.0
- fixed two bugs reported by -JonNny
- enabled damaging units more than once

06/08/2009 - Version 1.2.1
- some optimizations and the final touches (i hope this really is the final version)

06/15/2009 - Version 1.3.0
- added an option to launch a boomerang to the left side, either additionally or exclusively
- made the boomerang collide with unwalkable terrain (after checking for trees)

07/12/2009 - Version 1.3.1
- more and better comments
- made configuring valid targets easier
- boomerangs colliding with unwalkable terrain is now optional
- made an additional version where the calibration section uses formulae (for those who cant/dont want to configure arrays)

08/07/2009 - Version 1.3.3
- Compatibility with 1.24
- 1.3.2 was a private build for a request

06/10/2011 - Version 1.3.4
- fixed a few double frees

07/11/2011 - Version 1.4.0
- you can now change the width of the path of the boomerang (BOOMERANG_FOCUS)
- you can now configure the spell to simply ignore trees.
- a few optimizations in the background (mainly using static ifs) and a bit of cleanup

01/28/2012 - Version 1.4.1
- boomerangs now follow unit targets
- each boomerang now has its own group of units it has already hit
- cleaned up the code with more meaningful names

01/07/2015 - Version 1.4.2
- rewrote the entire spell, no functionality was changed
- fixed a bug involving bad logic which excluded all units from being dealt damage



Expand Code (Array version) + Credits:

Expand Code (Formula version) + Credits:

wraithseeker 06-08-2009 09:12 AM

I heard somewhere that doing GroupClear before a ENUM_GROUP is safer, is that true?

The way you name your struct members make it really hard to understand, couldn't you just name them properly?

Like s.d and etc

Deaod 06-08-2009 11:07 AM

calling GroupEnums clears the groups.

And i didnt add comments to the declaration of the members for nothing. Read them. The members are properly named (d for distance, a for angle, ...).

Sunwarrior25 06-08-2009 12:53 PM

Three questions:
 
  • Can this work with items? (thinking Legend of Zelda)
  • Can you move about while a boomerang is in the air?
  • Can we limit the number of boomerangs a given hero/item can have flying in the air at a time?

Deaod 06-08-2009 01:32 PM

  1. yes, i think you can add abilities to items which in turn can trigger off this ability.
  2. yes, you can (blinking will cause some visual glitches, but nothing screws up when blinking).
  3. yes, use unit indexing and assign each index a number of currently active boomerangs. You will have to add code for that yourself though.

Deaod 06-15-2009 05:45 PM

Version 1.3.0

Someone asked for a few additional features.

Rising_Dusk 07-01-2009 03:05 PM

Critiques...
  • Collapse JASS:
        private function SetUpDAMAGE takes nothing returns nothing
            set DAMAGE[1]=200. // initially deals 200 damage
            set DAMAGE[2]=275.
            set DAMAGE[3]=350.
        endfunction
        
        private function Damage takes integer level returns real // PROXY
            return DAMAGE[level]
        endfunction
    I think that should be replaced with the following, which gets rid of the unnecessary call at Init. I like formulas more anyways.
    Collapse JASS:
        private constant function Damage takes integer level returns real
            return 125.+75.*level
        endfunction
  • Similar as above for Damage Absorption.
  • Similar as above for Minimum Range.
  • The rest looks okay, but it's entirely possible I missed something due to it being tough to read and follow in a lot of places. You should comment the bulk of your code more in the future, it would really help in navigating the trickier parts.

akolyt0r 07-01-2009 03:15 PM

if you alread critisize deaods config function(s), you should tell him to use constant functions aswell (+3% speed 0.o)

busterkomo 07-01-2009 04:04 PM

Quote:

Originally Posted by Rising_Dusk
Critiques...
  • Collapse JASS:
        private function SetUpDAMAGE takes nothing returns nothing
            set DAMAGE[1]=200. // initially deals 200 damage
            set DAMAGE[2]=275.
            set DAMAGE[3]=350.
        endfunction
        
        private function Damage takes integer level returns real // PROXY
            return DAMAGE[level]
        endfunction
    I think that should be replaced with the following, which gets rid of the unnecessary call at Init. I like formulas more anyways.
    Collapse JASS:
        private function Damage takes integer level returns real
            return 125.+75.*level
        endfunction
  • Similar as above for Damage Absorption.
  • Similar as above for Minimum Range.
  • The rest looks okay, but it's entirely possible I missed something due to it being tough to read and follow in a lot of places. You should comment the bulk of your code more in the future, it would really help in navigating the trickier parts.

Well, formulas are good because they're a lot cleaner than arrays. On the other hand, you might not want the increments to be linear.

Quote:

if you alread critisize deaods config function(s), you should tell him to use constant functions aswell (+3% speed 0.o)
A 3% speed boost is extremely negligible.

Rising_Dusk 07-01-2009 04:20 PM

Quote:

Originally Posted by busterkomo
Well, formulas are good because they're a lot cleaner than arrays. On the other hand, you might not want the increments to be linear.

You can always generate a function fitting N points in a curve. It's very easy with only 3 levels, and still easy even with 5. After that it gets a bit ugly, yeah, so I can see the value of an array. Also, accessing array values is negligibly faster than doing the arithmetic.

akolyt0r 07-01-2009 04:30 PM

if its no problem to get bonus speed, it should be done no matter how negligible the speed bonus is !

Deaod 07-02-2009 01:20 PM

Dusk, linear is still okay...quadratic is a bit tricky and cubic even more so. But i wont spend half an hour finding a formula fitting my spell values. I will instead use the array method. Easier (for more complex formulae (quadratic and up (nested parentheses ftw))), faster (accessing an array is faster) and still provides you with the possibility to use formulae. Also, IIRC all config functions should get inlined (either by JH or by Vex's Optimizer).

What were the tough parts for you?

Rising_Dusk 07-02-2009 03:49 PM

Quote:

Originally Posted by Deaod
Dusk, linear is still okay...quadratic is a bit tricky and cubic even more so. But i wont spend half an hour finding a formula fitting my spell values. I will instead use the array method. Easier (for more complex formulae (quadratic and up (nested parentheses ftw))), faster (accessing an array is faster) and still provides you with the possibility to use formulae. Also, IIRC all config functions should get inlined (either by JH or by Vex's Optimizer).

Speed as an argument is lame. When you are down to comparing basic arithmetic to array accessing in a spell then you need to shoot yourself. I care more about code readability and user safety. It is much easier for a user to screw up that array thing by changing the index to something unusable or renaming the array thinking that he is allowed to do that than it is for him to screw up a formula that will return a passable value for a given level no matter what.

And if it takes someone half an hour to generate a formula to hit several known points, I dare venture that someone is an idiot.
Quote:

Originally Posted by Deaod
What were the tough parts for you?

Hidden information:
Most of the uncommented functions with numerous nested conditionals that aren't explained, so I can't easily check their veracity. Variable names come into play for those boolean vars here.
Collapse JASS:
        private static method DamageFilterFunc takes nothing returns boolean
        local unit u=GetFilterUnit()
            if IsUnitType(u, UNIT_TYPE_DEAD)==false and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)==false and (not IsUnitInGroup(u, tmps.g)) and IsUnitEnemy(u, GetOwningPlayer(tmps.c)) then
                if tmpd==1 then
                    if UnitDamageTarget(tmps.c, u, tmps.dam1, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
                        call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
                        call GroupAddUnit(tmps.g, u)
                        if DAMAGE_ABSORPTION_RELATIVE then
                            set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
                        else
                            set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
                        endif
                        if tmps.dam1<=DAMAGE_BOUNDARY then
                            call tmps.dum1.destroy()
                            set tmps.d1a=false
                            if not tmps.d2a then
                                call tmps.destroy()
                            endif
                        endif
                    endif
                elseif tmpd==2 then
                    if UnitDamageTarget(tmps.c, u, tmps.dam2, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
                        call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
                        call GroupAddUnit(tmps.g, u)
                        if DAMAGE_ABSORPTION_RELATIVE then
                            set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
                        else
                            set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
                        endif
                        if tmps.dam2<=DAMAGE_BOUNDARY then
                            call tmps.dum2.destroy()
                            set tmps.d2a=false
                            if not tmps.d1a then
                                call tmps.destroy()
                            endif
                        endif
                    endif
                endif
            endif
            set u=null
            return false
        endmethod
        
        private static method TreeFilterFunc takes nothing returns boolean
        local destructable d=GetFilterDestructable()
        local real x
        local real y
        local real bx
        local real by
            if (not IsDestructableDead(d)) and IsDestructableTree(d) then
                set x=GetWidgetX(d)
                set y=GetWidgetY(d)
                if tmpd==1 then
                    set bx=tmps.dum1.x
                    set by=tmps.dum1.y
                    if (((x-bx)*(x-bx))+((y-by)*(y-by)))<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then
                        if KILL_TREES then
                            call KillDestructable(d)
                            if DAMAGE_ABSORPTION_RELATIVE then
                                set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
                            else
                                set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
                            endif
                            if tmps.dam1<=DAMAGE_BOUNDARY then
                                call tmps.dum1.destroy()
                                set tmps.d1a=false
                                if not tmps.d2a then
                                    call tmps.destroy()
                                endif
                            endif
                        else
                            call tmps.dum1.destroy()
                            set tmps.d1a=false
                            if not tmps.d2a then
                                call tmps.destroy()
                            endif
                        endif
                    endif
                elseif tmpd==2 then
                    set bx=tmps.dum2.x
                    set by=tmps.dum2.y
                    if (((x-bx)*(x-bx))+((y-by)*(y-by)))<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then
                        if KILL_TREES then
                            call KillDestructable(d)
                            if DAMAGE_ABSORPTION_RELATIVE then
                                set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
                            else
                                set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
                            endif
                            if tmps.dam2<=DAMAGE_BOUNDARY then
                                call tmps.dum2.destroy()
                                set tmps.d2a=false
                                if not tmps.d1a then
                                    call tmps.destroy()
                                endif
                            endif
                        else
                            call tmps.dum2.destroy()
                            set tmps.d2a=false
                            if not tmps.d1a then
                                call tmps.destroy()
                            endif
                        endif
                    endif
                endif
            endif
            set d=null
            return false
        endmethod

Deaod 07-02-2009 05:00 PM

Lets say you have a spell with 5 or more levels. Lets further assume those values are in no particular easy order (linear, exponential). Maximum complexity would be of 4th degree (ax^4+bx^3+cx^2+dx+e) or of (level-1)th degree. Inserting the values and deducing the formula would be kind of tiresome (not that its impossible, its just tiresome and youre prone to make errors somewhere in that whole mess). Oh, and speed is FAR worse (for any function of a higher degree than 2, but as you mentioned, it doesnt really matter that much for a spell).

Also, arrays saves you the recalculation of the function every time you want to adjust the balance of the spell.

Expand Commented Code:

Maybe we could ask Vex to fix jass tags in hidden tags to not strech the whole thing to infinity.

Rising_Dusk 07-02-2009 06:11 PM

Quote:

Originally Posted by Deaod
Lets say you have a spell with 5 or more levels. Lets further assume those values are in no particular easy order (linear, exponential). Maximum complexity would be of 4th degree (ax^4+bx^3+cx^2+dx+e) or of (level-1)th degree. Inserting the values and deducing the formula would be kind of tiresome (not that its impossible, its just tiresome and youre prone to make errors somewhere in that whole mess)

Dude... You can do up to 10th order polynomial regression by the least squares method online. Generating a 99% accurate formula for 10 levels of any spell is good enough for me - and I found that website in ten seconds of a google search.

Let's also consider the fact that most sensible spell design has linear growth, so this is a non-issue in the first place.
Quote:

Originally Posted by Deaod
Maybe we could ask Vex to fix jass tags in hidden tags to not strech the whole thing to infinity.

Yes, this would be a good idea.

Your commented code looks good now that I know what those booleans mean. Perhaps you should actually comment the spell, instead of just having that excerpt in a separate post?


All times are GMT. The time now is 07:25 AM.

Powered by vBulletin (Copyright ©2000 - 2020, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services