• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Omega Wave v1.04

Omega Wave v1.04 by Maker


IconDescription
OW_scr3.jpg
OW_scr2.jpg
A spell request from Aeroblyctos to be used as a boss ability in his great campaign, The Chosen Ones.

The cool thing with this ability is that you can turn it into a game of trying to evade the green parts and go through the blue parts so that you take no damage. There's a mini-game for that in the test map.


The code uses recycling indexing in hashtables, so it only loops through active cicles.

JASS:
/*~~~~~~~~~~~~~~~~~ OMEGA WAVE v1.04 by Maker ~~~~~~~~~~~~~~~~~~~~~*/  
/*                                                                 |
| Creates rotating circles around the caster. Part of the circle   |
| damages and part destroys mana.                                  |
|                                                                 */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/          


scope OmegaWave initializer Omega_Wave

    globals
        private constant integer    ABILCODE            = 'A000'
        private constant integer    DUMMY_TYPE          = 'h000'
        private constant integer    ORDERID             = 852600
        // Of how many lightnings the circle consist
        private constant integer    LIGHTNINGS          = 16
        // How many of the lightnings do not cause damage
        // They drain mana instead
        private constant integer    MANA_LIGHTNINGS     = 6
        // How many circle waves there are
        private constant integer    WAVES               = 3
        // How many more waves per ability level. Does not apply at lvl 1
        private constant integer    WAVES_BONUS         = 1
        // How often a new circle is spawned
        // Match this with Data - Art duration
        // in object editor
        private constant real       INTERVAL            = 2.0
        // Height offset from ground
        private constant real       HEIGHT              = 60.
        // How fast the circle expands
        private constant real       SPEED               = 3.
        // Initial radius of the circle
        private constant real       INIT_DIST           = 32.
        // Circle position update interval
        private constant real       TIMEOUT             = 0.03
        // How far the circle expands
        private constant real       MAX_OFFSET          = 400.
        // Base damage per second
        private constant real       DMG_BASE            = 100.  * TIMEOUT
        // Bonus damage per ability level per second.
        // Does not apply at level 1
        private constant real       DMG_BONUS           = 30.   * TIMEOUT
        // How much mana is destroyed per second
        private constant real       MANA_BASE           = 40.   * TIMEOUT
        // BOnus mana destroyed per ability level per second.
        // Does not apply at level 1
        private constant real       MANA_BONUS          = 20.   * TIMEOUT
        // How wide the lightning is. Used for detecting
        // collision with units
        private constant real       LIGHTNING_WIDTH     = 28.
        // Does the circle rotate
        private constant boolean    ROTATES             = TRUE
        // Min and max speed for the rotation
        private constant real       MIN_ROT_SPD         = 15    * TIMEOUT * bj_DEGTORAD
        private constant real       MAX_ROT_SPD         = 30    * TIMEOUT * bj_DEGTORAD
        // Lightning types
        private constant string   TYPESAFE            = "DRAM"
        private constant string   TYPEDANGER          = "DRAL"
        
        // RGB values of the mana lightnings, adjusts colour
        // Adjust the first 255 between 0 and 255
        private constant real       MRED                = 255.  / 255.
        private constant real       MGREEN              = 255.  / 255.
        private constant real       MBLUE               = 255.  / 255.
        
        // RGB values of the health lightnings, adjusts colour
        private constant real       HRED                = 255.  / 255.
        private constant real       HGREEN              = 255.  / 255.
        private constant real       HBLUE               = 255.  / 255.
        
        // Transparency of the lightnings
        private constant real       MALPHA              = 1.
        private constant real       HALPHA              = 1.
        // Effect when damaging lightning hits
        private constant string     DMG_EFFECT          = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl"
        // Effect when mana destroying lightning hits
        private constant string     MANA_EFFECT         = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
        
        // Attack- and damage types
        private constant attacktype ATKTYPE             = ATTACK_TYPE_NORMAL
        private constant damagetype DMGTYPE             = DAMAGE_TYPE_NORMAL
        
        /*~~~~~~~~~~~~~ Global variables, don't change these ~~~~~~~~~~~~~*/
        private real r1
        private real r2
        private real r3
        private real r4
        private real r5
        private real r6
        private real r7
        
        private player plr
        private unit un
        private integer casts = 0
        
        private location l1 = Location(0,0)
        private location l2 = Location(0,0)
        
        private group grp1 = CreateGroup()
        private group grp2 = CreateGroup()
        private group casters = CreateGroup()
        
        private timer tmr = CreateTimer()
        
        private hashtable hash = InitHashtable()
        /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    endglobals
    
    
    /*~The hash functions return the child hash integer for hashtable~*/
    private constant function HashCircles takes nothing returns integer
        return 1
    endfunction
    
    private constant function HashActiveCircles takes nothing returns integer
        return 2
    endfunction
    
    private constant function HashTime takes nothing returns integer
        return 3
    endfunction
    
    private constant function HashInterval takes nothing returns integer
        return 4
    endfunction
    
    private constant function HashDamage takes nothing returns integer
        return 5
    endfunction
    
    private constant function HashSafeWidth takes nothing returns integer
        return 6
    endfunction
    
    private constant function HashWaves takes nothing returns integer
        return 7
    endfunction
    
    private constant function HashFirstFree takes nothing returns integer
        return 8
    endfunction
    
    private constant function HashFirst takes nothing returns integer
        return 9
    endfunction
    
    private constant function HashLast takes nothing returns integer
        return 10
    endfunction
    
    private constant function HashMana takes nothing returns integer
        return 11
    endfunction
    
    private function HashAngle takes integer value returns integer
        return 10 * LIGHTNINGS + value
    endfunction
    
    private function HashDist takes integer value returns integer
        return 20 * LIGHTNINGS + value
    endfunction

    private function HashDestroyed takes integer value returns integer
        return 30 * LIGHTNINGS + value
    endfunction
    
    private function HashSafeAngle takes integer value returns integer
        return 40 * LIGHTNINGS + value
    endfunction
    
    private function HashRotSpeed takes integer value returns integer
        return 50 * LIGHTNINGS + value
    endfunction
    
    private function HashRotDir takes integer value returns integer
        return 60 * LIGHTNINGS + value
    endfunction
    
    private function HashDummy takes integer value1, integer value2 returns integer
        return 1000 * LIGHTNINGS + value1 * 100 + value2
    endfunction
    
    private function HashLightning takes integer value1, integer value2 returns integer
        return 5000 * LIGHTNINGS + value1 * 100 + value2
    endfunction
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    
    
    /*~~~~~~~~~~~~~~~~~~~~~~Deals damage to units~~~~~~~~~~~~~~~~~~~~~*/
    private function Pick_Filter_2 takes nothing returns boolean
        local unit u = GetFilterUnit()
        if not(IsUnitInGroup(u, grp1))                                      and /*
        */ IsUnitEnemy(u, plr)                                              and /*
        */ GetWidgetLife(u) > 0.405                                         and /*
        */ GetUnitFlyHeight(u) < HEIGHT                                     then
            if Cos(r1 - Atan2(GetUnitY(u) - r4, GetUnitX(u) - r3)) < r7 then
                call UnitDamageTarget(un, u, r5, false, true, ATKTYPE, DMGTYPE, null)
                call DestroyEffect(AddSpecialEffectTarget(DMG_EFFECT, u, "chest"))
            elseif GetUnitState(u, UNIT_STATE_MANA) > 0 then
                call DestroyEffect(AddSpecialEffectTarget(MANA_EFFECT, u, "chest"))
                call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - r6)
            endif
        endif
        set u = null
        return FALSE
    endfunction

    private function Pick_Filter_1 takes nothing returns boolean
        return IsUnitEnemy(GetFilterUnit(), plr)
    endfunction
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    
    /*~~~~~~~~~~~~~Adds an expanding circle for the caster~~~~~~~~~~~~*/
    private function Add_Circle takes unit u, integer ID returns nothing
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real angle = GetRandomReal(-bj_PI, bj_PI)
        local real x1
        local real y1
        local real x2
        local real y2
        local integer i = 0
        local integer first = LoadInteger(hash, ID, HashFirst())
        local integer last = LoadInteger(hash, ID, HashLast())
        local integer free = LoadInteger(hash, ID, HashFirstFree())
        local integer circles = LoadInteger(hash, ID, HashCircles())
        local integer waves = LoadInteger(hash, ID, HashWaves())
        local lightning l
        
        // Saves the angle where the first lightning starts from
        call SaveReal(hash, ID, HashAngle(free), angle)
        // Saves the angle at the center of the safe lightnings
        call SaveReal(hash, ID, HashSafeAngle(free), angle + (I2R(MANA_LIGHTNINGS) / I2R(LIGHTNINGS))* bj_PI)
         
        // Randomizes the rotation angle
        if ROTATES then
            if GetRandomInt(1,2) == 1 then
                call SaveReal(hash, ID, HashRotDir(free), 1)
            else
                call SaveReal(hash, ID, HashRotDir(free), -1)
            endif
        endif
        
        // The loop creates a circle of lightnings and
        // dummies around the caster
        loop
            set x1 = x + INIT_DIST * Cos(angle)
            set y1 = y + INIT_DIST * Sin(angle)
            set angle = angle + 2 * bj_PI / LIGHTNINGS
            set x2 = x + INIT_DIST * Cos(angle)
            set y2 = y + INIT_DIST * Sin(angle)
                
            if i < MANA_LIGHTNINGS then
                set l = AddLightningEx(TYPESAFE , true , x1 , y1 , HEIGHT , x2 , y2 , HEIGHT)
                call SetLightningColor(l, MRED, MGREEN, MBLUE, MALPHA)
            else
                set l = AddLightningEx(TYPEDANGER , true , x1 , y1 , HEIGHT , x2 , y2 , HEIGHT)
                call SetLightningColor(l, HRED, HGREEN, HBLUE, HALPHA)
            endif
            
            set bj_lastCreatedUnit = CreateUnit(Player(15), DUMMY_TYPE, x1, y1, angle*bj_RADTODEG)
            call SetUnitFlyHeight(bj_lastCreatedUnit, HEIGHT, 0)
            // Remove Move ability from dummy so it won't try to return
            // to the pointwhere it was created at
            call UnitRemoveAbility(bj_lastCreatedUnit, 'Amov')
                
            call SaveUnitHandle(hash, ID, HashDummy(free,i), bj_lastCreatedUnit)
            call SaveLightningHandle(hash, ID, HashLightning(free,i), l)
                
            set i = i + 1
            exitwhen i >= LIGHTNINGS
        endloop
        
        // Saves the distance of the circle from the caster
        call SaveReal(hash, ID, HashDist(free), INIT_DIST)
        call SaveBoolean(hash, ID, HashDestroyed(free), false)
        call SaveInteger(hash, ID, HashCircles(), circles + 1)
        call SaveInteger(hash, ID, HashFirstFree(), free + 1)
        call SaveInteger(hash, ID, HashActiveCircles(), LoadInteger(hash, ID, HashActiveCircles()) + 1)
        call SaveReal(hash, ID, HashRotSpeed(free), GetRandomReal(MIN_ROT_SPD, MAX_ROT_SPD))
        
        // Update the smallest index to loop through
        if free < first then
            call SaveInteger(hash, ID, HashFirst(), free + 1)
        endif
        
        // Update the largest index to loop through
        if free > last then
            call SaveInteger(hash, ID, HashLast(), free)
        endif
            
        set l = null
    endfunction
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/


    private function Loop takes nothing returns nothing
        local unit u = GetEnumUnit()
        local integer ID = GetHandleId(u)
        local integer i = 0
        local integer j = LoadInteger(hash, ID, HashFirst())
        local integer k = 1
        local integer oid = GetUnitCurrentOrder(u)
        local integer free = LoadInteger(hash, ID, HashFirstFree())
        local integer circles = LoadInteger(hash, ID, HashCircles())
        local integer activeCircles = LoadInteger(hash, ID, HashActiveCircles())
        local integer maxloop = LoadInteger(hash, ID, HashLast())
        local real time = LoadReal(hash, ID, HashTime())+ TIMEOUT
        local real interv = LoadReal(hash, ID, HashInterval()) + TIMEOUT
        local real rotdir
        local real offset
        local real angle
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real x1
        local real x2
        local real y1
        local real y2
        local unit dummy
        local lightning l
        
        
        // Caster must be alive, waves left to cast and at least one active circle
        if not(IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0) and (circles < LoadInteger(hash, ID, HashWaves()) or activeCircles != 0) and oid == ORDERID then
            loop
                set rotdir = LoadReal(hash, ID, HashRotDir(j))
                set angle = LoadReal(hash, ID, HashAngle(j))
                set offset = LoadReal(hash, ID, HashDist(j)) + SPEED
                // Has the circle not reached max dist
                if offset < MAX_OFFSET then
                    set i = 0
                    loop
                        set x1 = x + offset * Cos(angle)
                        set y1 = y + offset * Sin(angle)
                        set angle = angle + 2 * bj_PI / LIGHTNINGS
                        set x2 = x + offset * Cos(angle)
                        set y2 = y + offset * Sin(angle)
                        
                        call MoveLocation(l1, x1, y1)
                        call MoveLocation(l2, x2, y2)
                        
                        set l = LoadLightningHandle(hash, ID, HashLightning(j,i))
                        
                        // Makes the lightnings to "flow" the right direction
                        if rotdir == 1 then
                            call MoveLightningEx(l, false, x2, y2, HEIGHT + GetLocationZ(l2), x1, y1, HEIGHT + GetLocationZ(l1))
                        else
                            call MoveLightningEx(l, false, x1, y1, HEIGHT + GetLocationZ(l1), x2, y2, HEIGHT + GetLocationZ(l2))
                        endif
                        
                        set dummy = LoadUnitHandle(hash, ID, HashDummy(j,i))
                        call SetUnitX(dummy, x1)
                        call SetUnitY(dummy, y1)
                        
                        set i = i + 1
                        exitwhen i > LIGHTNINGS
                    endloop
                    
                    set l = null
                    set dummy = null
                    
                    set un = u
                    set plr = GetOwningPlayer(u)
                    set r1 = LoadReal(hash, ID, HashSafeAngle(j))
                    set r2 = LoadReal(hash, ID, HashSafeWidth())
                    set r7 = Cos(r2)
                    set r3 = x
                    set r4 = y
                    set r5 = LoadReal(hash, ID, HashDamage())
                    set r6 = LoadReal(hash, ID, HashMana())
                    
                    call GroupEnumUnitsInRange(grp1, x, y, offset - LIGHTNING_WIDTH, function Pick_Filter_1)
                    call GroupEnumUnitsInRange(grp2, x, y, offset + LIGHTNING_WIDTH, function Pick_Filter_2)
                    
                    call SaveReal(hash, ID, HashDist(j), offset)
                    
                    // Updates the rotation angle and the safe angle
                    static if ROTATES then
                        call SaveReal(hash, ID, HashAngle(j), LoadReal(hash, ID, HashAngle(j)) + LoadReal(hash, ID, HashRotSpeed(j)) * LoadReal(hash, ID, HashRotDir(j)))
                        call SaveReal(hash, ID, HashSafeAngle(j), LoadReal(hash, ID, HashAngle(j)) + r2)
                    endif
                else
                    // Checks whether the circle has been destroyed already or not
                    if LoadBoolean(hash, ID, HashDestroyed(j)) != true then
                        // The loop destroys all dummies and lightnings of the circle
                        set k = 0
                        loop
                            // Checks that the lightning exists, can produce fatal error without this check
                            if HaveSavedHandle(hash, ID, HashLightning(j,k)) then
                                call DestroyLightning(LoadLightningHandle(hash, ID, HashLightning(j,k)))
                                call UnitApplyTimedLife(LoadUnitHandle(hash, ID, HashDummy(j,k)), 1 , 0.01)
                                call RemoveSavedHandle(hash, ID, HashLightning(j,k))
                            endif
                            set k = k + 1
                            exitwhen k > LIGHTNINGS
                        endloop
                        // Marks the circle as been destroyed
                        call SaveBoolean(hash, ID, HashDestroyed(j), true)
                        // Reduces the number of active circles for the caster
                        call SaveInteger(hash, ID, HashActiveCircles(), activeCircles - 1)
                        // Updates the first free index
                        if j < free then
                            call SaveInteger(hash, ID, HashFirstFree(), j)
                        endif
                    endif
                endif
                set j = j + 1
                exitwhen j > maxloop
            endloop
            // Updates the interval time used for creating a new circle
            call SaveReal(hash, ID, HashTime(), interv)
            if circles < LoadInteger(hash, ID, HashWaves()) then
                if interv >= INTERVAL then
                    call Add_Circle(u, ID)
                    call SaveReal(hash, ID, HashInterval(), 0)
                else
                    call SaveReal(hash, ID, HashInterval(), interv)
                endif
            endif
        else
            loop
                set k = 0
                loop
                    if HaveSavedHandle(hash, ID, HashLightning(j,k)) then
                        call DestroyLightning(LoadLightningHandle(hash, ID, HashLightning(j,k)))
                        call UnitApplyTimedLife(LoadUnitHandle(hash, ID, HashDummy(j,k)), 1 , 0.01)
                    endif
                    set k = k + 1
                    exitwhen k > LIGHTNINGS
                endloop
                set j = j + 1
                exitwhen j > maxloop
            endloop
            call GroupRemoveUnit(casters, u)
            if oid == ORDERID then
                call IssueImmediateOrder(u, "stop")
            endif
            call FlushChildHashtable(hash, ID)
            
            set casts = casts - 1
            if casts == 0 then
                call PauseTimer(tmr)
            endif
        endif
        
        set u = null
    endfunction


    private function Timer_Expire takes nothing returns nothing
        call ForGroup(casters, function Loop)
    endfunction


    private function Actions takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local integer ID = GetHandleId(u)
        local integer level = GetUnitAbilityLevel(u, ABILCODE) - 1
        
        /*~~~ 0 value doesn't need to be saved but I do it to remember ~~~*/
        /*~~~~~~~~~~~~~~~ the things that need to be saved ~~~~~~~~~~~~~~~*/
        
        // call SaveReal(hash, ID, HashTime(), 0)
        // call SaveReal(hash, ID, HashInterval(), 0)
        // call SaveInteger(hash, ID, HashLast(), 0)
        // call SaveInteger(hash, ID, HashFirst(), 0)
        // call SaveInteger(hash, ID, HashCircles(), 0)
        // call SaveInteger(hash, ID, HashFirstFree(), 0)
        // call SaveInteger(hash, ID, HashActiveCircles(), 0)
        
        call SaveInteger(hash, ID, HashWaves(), WAVES + WAVES_BONUS * level)
        call SaveReal(hash, ID, HashDamage(), DMG_BASE + DMG_BONUS * level)
        call SaveReal(hash, ID, HashMana(), MANA_BASE + MANA_BONUS * level)
        call SaveReal(hash, ID, HashSafeWidth(), I2R(MANA_LIGHTNINGS) / I2R(LIGHTNINGS)* bj_PI)
        
        /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

        call Add_Circle(u, ID)
        
        call GroupAddUnit(casters, u)
        if casts == 0 then
            call TimerStart(tmr , TIMEOUT , true , function Timer_Expire)
        endif
        set casts = casts + 1
        
        set u = null
    endfunction

    
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABILCODE
    endfunction


    private function Omega_Wave takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(t, function Conditions )
        call TriggerAddAction(t, function Actions )
    endfunction

endscope



v1.00 Uploaded 20.04.2011
v1.01 Uploaded 21.04.2011
Changed the name of one variable
Made some hash functions constant
Small code improvements
v1.02 Uploaded 21.04.2011
Code improved
v1.03 Uploaded 22.04.2011
Code improved
v1.04
Added configurable lightning colour




Aeroblyctos for spell idea


Keywords:
omega, wave, lightning, laser, maker, aeroblyctos, circle
Contents

Omega Wave v1.04 (Map)

Reviews
Bribe: This is a very inspiring spell for me. The effects are very creative. Technically speaking, the biggest improvement I see you don't have to init hashtable values to 0 - you can remove those lines as hashtables will return 0 if they aren't...

Moderator

M

Moderator

Bribe:

This is a very inspiring spell for me. The effects are very creative.

Technically speaking, the biggest improvement I see you don't have to init hashtable values to 0 - you can remove those lines as hashtables will return 0 if they aren't set - it's not like using an un-initialized global. Arrays work the same as this (auto-init to 0). This won't affect the speed too tremendously but would decrease the map size a bit.

At the people worried that the constant functions don't inline - JassHelper does inline them. This ends up being very efficient.
 
VERY ORIGINAL!
Excellent work mate :)

Just a few things:
JASS:
    private function HashCircles takes nothing returns integer
        return 1
    endfunction
    
    private function HashActiveCircles takes nothing returns integer
        return 2
    endfunction
    
    private function HashTime takes nothing returns integer
        return 3
    endfunction
    
    private function HashInterval takes nothing returns integer
        return 4
    endfunction
    
    private function HashDamage takes nothing returns integer
        return 5
    endfunction
    
    private function HashSafeWidth takes nothing returns integer
        return 6
    endfunction
    
    private function HashWaves takes nothing returns integer
        return 7
    endfunction
    
    private function HashFirstFree takes nothing returns integer
        return 8
    endfunction
    
    private function HashFirst takes nothing returns integer
        return 9
    endfunction
    
    private function HashLast takes nothing returns integer
        return 10
    endfunction
    
    private function HashMana takes nothing returns integer
        return 11
    endfunction

I know these functions help readability, but I think it's best (for efficiency) if you just use
the values in your code instead of calling these functions.

If you don't like my suggestion, then I guess the least you could do is call them "constant"
functions. This way, they'd be faster.
 
nicely done maker. however, i dont know much vjass so i cant comment on the code >.<. i give 5/5

*Whisper*: He's on to us.. Get him! ;p (I don't get it too xD)

*looks for any reason to make a post so it doesn't count as spam* ... ...

FOUND ONE:

JASS:
local unit u = GetFilterUnit()
        if not(IsUnitInGroup(u, grp1))                                      and /*
        */ IsUnitEnemy(GetFilterUnit(), plr)                                and /*

Instead of GetFilterUnit(), use the local u

Another thing, I don't know if this will make any difference to the efficiency, but over here: (IsUnitType(u, UNIT_TYPE_DEAD), you could use GetWidgetLife(u) <= 0.405
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Thanks for the comments!

I know these functions help readability, but I think it's best (for efficiency) if you just use
the values in your code instead of calling these functions.

If you don't like my suggestion, then I guess the least you could do is call them "constant"
functions. This way, they'd be faster.

They will remain functions :) But I will make the functions constant.

JASS:
local unit u = GetFilterUnit()
        if not(IsUnitInGroup(u, grp1))                                      and /*
        */ IsUnitEnemy(GetFilterUnit(), plr)                                and /*

Instead of GetFilterUnit(), use the local u

Yeah.

Another thing, I don't know if this will make any difference to the efficiency, but over here: (IsUnitType(u, UNIT_TYPE_DEAD), you could use GetWidgetLife(u) <= 0.405

Apparently GetWidgetLife, while being faster, can bug in some rare cases.
 
rare cases

SOLD! ;P

One last thing to make this as efficient as possible:
Store the level of the ability in an integer instead of repeating the function call here:

JASS:
local unit u = GetTriggerUnit()
        local integer ID = GetHandleId(u)
        
        /*~~~ 0 value doesn't need to be saved but I do it to remember ~~~*/
        /*~~~~~~~~~~~~~~~ the things that need to be saved ~~~~~~~~~~~~~~~*/
        call SaveInteger(hash, ID, HashLast(), 0)
        call SaveInteger(hash, ID, HashFirst(), 0)
        call SaveInteger(hash, ID, HashCircles(), 0)
        call SaveInteger(hash, ID, HashFirstFree(), 0)
        call SaveInteger(hash, ID, HashActiveCircles(), 0)
        call SaveInteger(hash, ID, HashWaves(), WAVES + WAVES_BONUS * (GetUnitAbilityLevel(u, ABILCODE)) - 1)
        call SaveReal(hash, ID, HashTime(), 0)
        call SaveReal(hash, ID, HashInterval(), 0)
        call SaveReal(hash, ID, HashSafeWidth(), I2R(SAFE_LIGHTNINGS) / I2R(LIGHTNINGS)* bj_PI)
        call SaveReal(hash, ID, HashDamage(), DMG_BASE + DMG_BONUS * (GetUnitAbilityLevel(u, ABILCODE)) - 1)
        call SaveReal(hash, ID, HashMana(), MANA_BASE + MANA_BONUS * (GetUnitAbilityLevel(u, ABILCODE)) - 1)

That should do it :)
The spell is now as efficient as possible .. at least i hope so :p
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
I spesifically didn't declare a variable for that since I'm calling the function only twice. Not worth it IMO :)

Thanks for the suggestion though.

There's still room for improving efficiency. Like storing the Cos(r2) into hashtable. But I will take a look at things when I have more time.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Alright haven't reviewed a spell in a while so don't blame me.

-private functions doesn't need to be prefixed, they're private?

-merge the conditions and the actions

return GetSpellAbilityId() == ABILCODE

will be an if instead and below that you put all your actions

and remember to always return false afterwards

-I don't get why you aren't using structs, your life could have been much more easier but as you are using hashtables, use Bribe's New Table library no buts

-these calls are unnecessary as GroupEnumUnitsInRange clears a group before it enums
call GroupClear(grp1)
call GroupClear(grp2)

-you should be ashamed (no offense :p), using globals and yet arent using global locations
JASS:
        local location l1 = null
        local location l2 = null

create two global locations and call the native "MoveLocation" to your newx and newy and then you can play with GetLocationZ and don't have to remove them

-I don't get this, in the filter function, you are only using GetWidgetLife as death check yet in the loop you use IsUnitType and GetUnitTypeId. Combine atleast GWL and IUT at both places.

This is a great spell though no question about it.
 
My feedback:

  • JASS:
    /*~~~ 0 value doesn't need to be saved but I do it to remember ~~~*/
            /*~~~~~~~~~~~~~~~ the things that need to be saved ~~~~~~~~~~~~~~~*/
    Now that you are done remembering and saving, you can feel free to remove them, unless I'm mistaken. :p
  • In the loop function, not all of the variables need to be initialized. (eg: x1, x2, y1, y2... etc)
  • For this:
    JASS:
                            if rotdir == 1 then
                                call MoveLightningEx(l, false, x2, y2, HEIGHT + GetLocationZ(l2), x1, y1, HEIGHT + GetLocationZ(l1))
                            else
                                call MoveLightningEx(l, false, x1, y1, HEIGHT + GetLocationZ(l1), x2, y2, HEIGHT + GetLocationZ(l2))
                            endif
    You can just move one location (and only use one). It will either be moved to (x2,y2) or (x1,y1), so you don't need two separate locations unless you are doing it for both. So it can be:
    JASS:
                            if rotdir == 1 then
                                call MoveLocation(l1,x2,y2)
                                call MoveLightningEx(l, false, x2, y2, HEIGHT + GetLocationZ(l1), x1, y1, HEIGHT + GetLocationZ(l1))
                            else
                                call MoveLocation(l1,x1,y1)
                                call MoveLightningEx(l, false, x1, y1, HEIGHT + GetLocationZ(l1), x2, y2, HEIGHT + GetLocationZ(l1))
                            endif
  • "l" and "dummy" don't need to be nulled per loop, just nulled when they are done being used in general. (at the bottom of the function or after the loop..)
  • Instead of using r1, r2, r3... You should make it an array.
  • Instead of using grp1, you might be able to just use not IsUnitInRangeXY.
  • Possibly merge the conditions and actions.

Otherwise, really cool spell. :)

If you don't like my suggestion, then I guess the least you could do is call them "constant"
functions. This way, they'd be faster.

Afaik, it is the same speed. Someone did benchmarks a long time ago, and they ended up being the same in terms of speed. However, I still recommend keeping the constant there so that the people will know that they can modify those values. =)
 
Level 5
Joined
Jul 16, 2009
Messages
88
Instead of having a semi-circle of green and a semi-circle of blue, why not alternate them between green and blue lightning?

In other words: green lightning, then blue lightning, then green again, followed by blue, etc.

It makes dodging more challenging when the dodger has to "slip" through a small gap.
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
JASS:
                        if rotdir == 1 then
                            call MoveLightningEx(l, false, x2, y2, HEIGHT + GetLocationZ(l2), x1, y1, HEIGHT + GetLocationZ(l1))
                        else
                            call MoveLightningEx(l, false, x1, y1, HEIGHT + GetLocationZ(l1), x2, y2, HEIGHT + GetLocationZ(l2))
                        endif
You can just move one location (and only use one). It will either be moved to (x2,y2) or (x1,y1), so you don't need two separate locations unless you are doing it for both. So it can be:
JASS:
                        if rotdir == 1 then
                            call MoveLocation(l1,x2,y2)
                            call MoveLightningEx(l, false, x2, y2, HEIGHT + GetLocationZ(l1), x1, y1, HEIGHT + GetLocationZ(l1))
                        else
                            call MoveLocation(l1,x1,y1)
                            call MoveLightningEx(l, false, x1, y1, HEIGHT + GetLocationZ(l1), x2, y2, HEIGHT + GetLocationZ(l1))
                        endif

But I need the z at the starting- and end points of the lightning. Can't use the same value.

Thanks for the comments, some changes made to the code.

Instead of having a semi-circle of green and a semi-circle of blue, why not alternate them between green and blue lightning?

I might make it configurable to make it be like that.
 
Level 5
Joined
Jul 17, 2010
Messages
140
Well this is a spell maked, by the maker..xD
So there is almost, no doubt that its a good coded spell.!:D
Rate 5/5.
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Damn cool, this is!
Is it possible to edit the colors of the wave?

It is now! Updated the spell, now you can configure the lightning colours.

I also added a list of lightning effects.


  • LIGHTNING EFFECTS
    • Events
    • Conditions
    • Actions
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Chain Lightning - Primary lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: CLPB
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Chain Lightning - Secondary lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: CLSB
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Drain lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: DRAB
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Drain Life lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: DRAL
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Drain Mana lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: DRAM
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Finger of Death lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: AFOD
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Forked Lightning lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: FORK
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Healing Wave - Primary lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: HWPB
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Healing Wave - Secondary lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: HWSB
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Lightning Attack lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: CHIM
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Magic Leash lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: LEAS
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Mana Burn lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: MBUR
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Mana Flare lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: MFPB
      • -------- ------------------------------------------------------------ --------
      • Lightning - Create a Spirit Link lightning effect from source (Position of (Triggering unit)) to target (Center of (Playable map area))
      • Custom script: SPLK
      • -------- ------------------------------------------------------------ --------


As always this is worth being a 5/5 for me.

Thanks!
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Updated the spell, added the static if. Yeah, I'm using 0.A.2.B

I had the boolean set to TRUE. When you start writing something then it offers to autocomplete the word. I had done that so the true was in capital letters. I then changed it to non capital letters an it worked. Quite a bug.
 
Level 7
Joined
Oct 16, 2010
Messages
193
Hey does anyone else get an error when they import this spell to the map. They give there 328 compile errors.


EDIT: Nevermind i figured it out. I have to use NewJassGen then it works.
 
Last edited:
Top