• 🏆 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!

Tidal Payback v1.2

JASS:
*==============================================================================*
*                             Tidal Payback                                    *
*==============================================================================*
*                         by bowser499 aka [DUOS]                              *
*==============================================================================*
*                                                                              *
* Does the charge in the selected direction, leaving a water trail behind and  *
* heavily damaging all the nearby units. After reaching the distance, the hero *
* will jump, leaving a geyser in the place where this jump started. The geyser *
* explodes after some time, dealing damage to the nearby units. After the hero *
* lands, he deals damage to the units in the close area aswell.                *
*                                                                              *
*==============================================================================*
*                               HOW TO IMPORT                                  *
*==============================================================================*
* 1. Copy all Object Editor data:                                              *
*  1.1. Copy 'Tidal Payback' ability.                                          *
*  1.2. Copy 'Tidal Payback Geyser' unit.                                      *
*  1.3. Copy 'Tidal Payback Missile' unit.                                     *
* 2. Copy all Import Manager data. Please do not change any path.              *
* 3. Copy 'Tidal Payback' folder to your map.                                  *
* 4. Configure the constants in the trigger according to your data in map.     *
* 5. Have fun using the spell!                                                 *
*==============================================================================*
*                                  CREDITS                                     *
*==============================================================================*
* BTNBubbles.blp                                                  Golden-Drake *
* TidalErruption.mdx                                            Unknown Author *
*==============================================================================*
*                                   ENDING                                     *
*==============================================================================*
* Note that this spell requires JNGP with JassHelper 0.A.2.B.                  *
* I really appreciate any credits you will place in your maps!                 *
* Be sure to check my pages:                                                   *
*                                                                              *
* www.hiveworkshop.com/forums/members/bowser499/                               *
* simplar.deviantart.com                                                       *
*                                                                              *
* I love comments and reviews!~                                                *
*==============================================================================*
JASS:
library TidalPayback initializer Init requires TimerUtils, SpellEffectEvent, TerrainPathability
    globals
        // CONFIGURABLE CONSTANTS
            // General.
        private constant integer    ABILITY_ID                    = 'A000'   // Tidal Payback Ability Raw Code.
        private constant integer    MISSILE_ID                    = 'n000'   // Tidal Payback Missile Raw Code.
        private constant integer    FLYABIL_ID                    = 'Amrf'   // The ability whose bug can be used to make unit flyable.
        private constant integer    GEYSER_ID                     = 'n001'   // Tidal Payback Geyser Raw Code.
            // Running Phase.
        private constant string     RP_BEFORE_EFFECT              = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"       // The effect before the run starts.
        private constant string     RP_EFFECT                     = "units\\human\\WaterElemental\\WaterElemental.mdl"           // The effect during the run.
        private constant string     RP_DAMAGE_EFFECT              = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl" // The effect on the units damaged during the run.
        private constant integer    RP_WALK_ANIM_INDEX            = 0        // The index of 'walk' animation of the appropriate model.
        private constant real       RP_DISTANCE                   = 1000.    // The distance which the hero must run.
        private constant real       RP_DISTANCE_FACTOR            = 30.      // Run distance increment per level.
        private constant real       RP_ENEMY_CHECK_AOE            = 190.     // The range from the caster which is scanned for enemies.
        private constant real       RP_ENEMY_CHECK_AOE_FACTOR     = 20.      // Range increment per level.
        private constant real       RP_TIMER_PERIOD               = .03125   // The timer of caster's running timeout.
        private constant real       RP_DISTANCE_PER_PERIOD        = 45.      // The distance that caster will pass every RP_TIMER_PERIOD seconds.
        private constant real       RP_WALK_ANIM_SPEED            = 2.2      // The speed of 'walk' animation which is needed to make the effect of running. The value '1' means 100%.
        private constant real       RP_DAMAGE                     = 120.     // The damage that geyser will deal.
        private constant real       RP_DAMAGE_FACTOR              = 42.      // Damage increment per level.
        private constant attacktype RP_ATTACK_TYPE                = ATTACK_TYPE_NORMAL
        private constant damagetype RP_DAMAGE_TYPE                = DAMAGE_TYPE_NORMAL
        private constant weapontype RP_WEAPON_TYPE                = WEAPON_TYPE_WHOKNOWS
            // Geyser after jump.
        private constant string     GZ_BEFORE_EFFECT              = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"       // The effect before the geyser explodes.
        private constant string     GZ_EXPLODE_EFFECT             = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"       // The effect when the geyser explodes.
        private constant string     GZ_EXPLODE_EFFECT_2           = "TidalErruption.mdx"                                         // The effect when the geyser explodes (2).
        private constant string     GZ_DAMAGE_EFFECT              = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl" // The effect on the units damaged by geyser.
        private constant real       GZ_DELAY                      = 1.7      // The time before geyser will explode.
        private constant real       GZ_DELAY_DECREASE_FACTOR      = .2       // How many can delay decrease with every level of the spell.
        private constant real       GZ_ENEMY_CHECK_AOE            = 140.     // The range from geyser in which enemies are considered as caught.
        private constant real       GZ_ENEMY_CHECK_AOE_FACTOR     = 22.      // Range increment per level.
        private constant real       GZ_DAMAGE                     = 340.     // The damage that geyser will deal.
        private constant real       GZ_DAMAGE_FACTOR              = 42.      // Damage increment per level.
        private constant attacktype GZ_ATTACK_TYPE                = ATTACK_TYPE_NORMAL
        private constant damagetype GZ_DAMAGE_TYPE                = DAMAGE_TYPE_NORMAL
        private constant weapontype GZ_WEAPON_TYPE                = WEAPON_TYPE_WHOKNOWS
            // Jumping Phase.
        private constant string     JP_BEFORE_EFFECT              = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"         // The effect before the jump starts.
        private constant string     JP_AFTER_EFFECT               = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"         // The effect after the jump ends.
        private constant string     JP_AFTER_EFFECT_2             = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl" // The effect after the jump ends (2).
        private constant real       JP_DISTANCE                   = 1400.    // The distance which the hero must jump.
        private constant real       JP_DISTANCE_FACTOR            = 10.      // Jump distance increment per level.
        private constant real       JP_TIMER_PERIOD               = .03125   // The timer of caster's jumping timeout.
        private constant real       JP_DISTANCE_PER_PERIOD        = 35.      // The distance that caster will pass every JP_TIMER_PERIOD seconds.
        private constant real       JP_MAX_FLY_HEIGHT             = 400.     // The maximal flying height that will be achieved by the jumper.
        private constant real       JP_ENEMY_CHECK_AOE            = 380.     // The range from geyser in which enemies are considered as caught.
        private constant real       JP_ENEMY_CHECK_AOE_FACTOR     = 62.      // Range increment per level.
        private constant real       JP_DAMAGE                     = 340.     // The damage that geyser will deal.
        private constant real       JP_DAMAGE_FACTOR              = 42.      // Damage increment per level.
        private constant attacktype JP_ATTACK_TYPE                = ATTACK_TYPE_NORMAL
        private constant damagetype JP_DAMAGE_TYPE                = DAMAGE_TYPE_NORMAL
        private constant weapontype JP_WEAPON_TYPE                = WEAPON_TYPE_WHOKNOWS
        // HARDCODED STUFF
        private          group      TempGroup                     = CreateGroup()
    endglobals
    
    private function EnumEnemiesConditions takes unit abilityCaster, unit enumUnit returns boolean
        return IsUnitEnemy(abilityCaster,GetOwningPlayer(enumUnit)) and not IsUnitType(enumUnit,UNIT_TYPE_DEAD) and not IsUnitType(enumUnit,UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(enumUnit,UNIT_TYPE_STRUCTURE)
    endfunction

    private struct SpellActions
        unit    abilityCaster  = null
        integer abilityLevel   = 0
        real    casterX        = 0.
        real    casterY        = 0.
        real    spellTargetX   = 0.
        real    spellTargetY   = 0.
        real    coordAngle     = 0.
        real    distCos        = 0.
        real    distSin        = 0.
        real    distancePassed = 0.
        timer   loopTimer      = null
        timer   geyserTimer    = null
        unit    geyserDummy    = null
        effect  geyserEffect   = null
        unit    flyingMissile  = null
        boolean isFlyingUp     = true
        real    propWindow     = 0.
        integer spellPhase     = 1
        group   damagedUnits   = CreateGroup()
        
        static method geyserCallback takes nothing returns nothing
            local thistype this           = GetTimerData(GetExpiredTimer())
            local real     geyserX        = GetWidgetX(.geyserDummy)
            local real     geyserY        = GetWidgetY(.geyserDummy)
            local unit     enumUnit
            
            call GroupEnumUnitsInRange(TempGroup,geyserX,geyserY,GZ_ENEMY_CHECK_AOE+GZ_ENEMY_CHECK_AOE_FACTOR*abilityLevel,null)
        
            loop
                set enumUnit = FirstOfGroup(TempGroup)
                exitwhen enumUnit == null
                if EnumEnemiesConditions(.abilityCaster,enumUnit) then
                    call DestroyEffect(AddSpecialEffect(GZ_DAMAGE_EFFECT,GetWidgetX(enumUnit),GetWidgetY(enumUnit)))
                    call UnitDamageTarget(.abilityCaster,enumUnit,GZ_DAMAGE+GZ_DAMAGE_FACTOR*abilityLevel,true,false,GZ_ATTACK_TYPE,GZ_DAMAGE_TYPE,GZ_WEAPON_TYPE)
                endif
                call GroupRemoveUnit(TempGroup,enumUnit)
            endloop
        
            call DestroyEffect(AddSpecialEffect(GZ_EXPLODE_EFFECT,geyserX,geyserY))
            call DestroyEffect(AddSpecialEffect(GZ_EXPLODE_EFFECT_2,geyserX,geyserY))
            
            call DestroyEffect(.geyserEffect)
            call RemoveUnit(.geyserDummy)
            call ReleaseTimer(.geyserTimer)
            
            set .geyserEffect = null
            set .geyserDummy  = null
            set .geyserTimer  = null
        endmethod
        
        static method createGeyser takes nothing returns nothing
            local thistype this     = GetTimerData(GetExpiredTimer())
            
            set .geyserTimer  = NewTimerEx(this)
            set .geyserDummy  = CreateUnit(GetOwningPlayer(.abilityCaster),GEYSER_ID,.casterX,.casterY,0.)
            set .geyserEffect = AddSpecialEffect(GZ_BEFORE_EFFECT,.casterX,.casterY)
            
            call TimerStart(.geyserTimer,GZ_DELAY+GZ_DELAY_DECREASE_FACTOR*.abilityLevel,false,function thistype.geyserCallback)
        endmethod
        
        static method jumpCallback takes nothing returns nothing
            local thistype this     = GetTimerData(GetExpiredTimer())
            local real     nextX    = GetWidgetX(.flyingMissile) + .distCos
            local real     nextY    = GetWidgetY(.flyingMissile) + .distSin
            local unit     enumUnit
            
            if .distancePassed < JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel then
                if .distancePassed == 0. and .isFlyingUp then
                    if UnitAddAbility(.flyingMissile,FLYABIL_ID) then
                        call UnitRemoveAbility(.flyingMissile,FLYABIL_ID)
                    endif
                    call SetUnitFlyHeight(.flyingMissile,JP_MAX_FLY_HEIGHT,JP_MAX_FLY_HEIGHT / (.5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) / (JP_DISTANCE_PER_PERIOD / JP_TIMER_PERIOD)))
                endif
            
                if distancePassed >= .5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) and .isFlyingUp then
                    call SetUnitFlyHeight(.flyingMissile,0.,JP_MAX_FLY_HEIGHT / (.5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) / (JP_DISTANCE_PER_PERIOD / JP_TIMER_PERIOD)))
                    set .isFlyingUp = false
                endif
            
                if IsTerrainWalkable(nextX,nextY) then
                    call SetUnitX(.flyingMissile,nextX)
                    call SetUnitY(.flyingMissile,nextY)
                    call SetUnitX(.abilityCaster,nextX)
                    call SetUnitY(.abilityCaster,nextY)
                endif
                
                set .distancePassed = .distancePassed + JP_DISTANCE_PER_PERIOD
            else
                if IsTerrainWalkable(nextX,nextY) then
                    call SetUnitX(.abilityCaster,nextX)
                    call SetUnitY(.abilityCaster,nextY)
                else
                    call SetUnitX(.abilityCaster,GetWidgetX(.flyingMissile))
                    call SetUnitY(.abilityCaster,GetWidgetY(.flyingMissile))
                endif
                
                call SetUnitVertexColor(.abilityCaster,255,255,255,255)
            
                call ShowUnit(.flyingMissile,false)
                call RemoveUnit(.flyingMissile)
                call DestroyEffect(AddSpecialEffect(JP_AFTER_EFFECT,nextX,nextY))
                call DestroyEffect(AddSpecialEffect(JP_AFTER_EFFECT_2,nextX,nextY))
                
                call SetUnitPropWindow(.abilityCaster,.propWindow)
                call SetUnitTurnSpeed(.abilityCaster,GetUnitDefaultTurnSpeed(.abilityCaster))
                call IssueImmediateOrderById(.abilityCaster,851972)
                call SetUnitFacing(.abilityCaster,.coordAngle*57.295827)
                call SetUnitTimeScale(.abilityCaster,1.)
            
                call GroupEnumUnitsInRange(TempGroup,nextX,nextY,JP_ENEMY_CHECK_AOE+JP_ENEMY_CHECK_AOE_FACTOR*.abilityLevel,null)
        
                loop
                    set enumUnit = FirstOfGroup(TempGroup)
                    exitwhen enumUnit == null
                    if EnumEnemiesConditions(.abilityCaster,enumUnit) then
                        call UnitDamageTarget(.abilityCaster,enumUnit,JP_DAMAGE+JP_DAMAGE_FACTOR*.abilityLevel,true,false,JP_ATTACK_TYPE,JP_DAMAGE_TYPE,JP_WEAPON_TYPE)
                    endif
                    call GroupRemoveUnit(TempGroup,enumUnit)
                endloop
                
                call ReleaseTimer(.loopTimer)
                call GroupClear(.damagedUnits)
                call DestroyGroup(.damagedUnits)
                
                set .damagedUnits  = null
                set .flyingMissile = null
                set .abilityCaster = null
                set .loopTimer     = null
                call .destroy()
            endif
        endmethod
        
        static method walkCallback takes nothing returns nothing
            local thistype this     = GetTimerData(GetExpiredTimer())
            local unit     enumUnit
            
            set .casterX       = GetWidgetX(abilityCaster)
            set .casterY       = GetWidgetY(abilityCaster)
            
            if .distancePassed < RP_DISTANCE+RP_DISTANCE_FACTOR*abilityLevel then
                call GroupEnumUnitsInRange(TempGroup,.casterX+.distCos,.casterY+.distSin,RP_ENEMY_CHECK_AOE+RP_ENEMY_CHECK_AOE_FACTOR*.abilityLevel,null)
        
                loop
                    set enumUnit = FirstOfGroup(TempGroup)
                    exitwhen enumUnit == null
                    if EnumEnemiesConditions(.abilityCaster,enumUnit) and not IsUnitInGroup(enumUnit,.damagedUnits) then
                        call GroupAddUnit(.damagedUnits,enumUnit)
                        call DestroyEffect(AddSpecialEffect(RP_DAMAGE_EFFECT,GetWidgetX(enumUnit),GetWidgetY(enumUnit)))
                        call UnitDamageTarget(.abilityCaster,enumUnit,RP_DAMAGE+RP_DAMAGE_FACTOR*.abilityLevel,true,false,RP_ATTACK_TYPE,RP_DAMAGE_TYPE,RP_WEAPON_TYPE)
                    endif
                    call GroupRemoveUnit(TempGroup,enumUnit)
                endloop

                call SetUnitFacing(.abilityCaster,.coordAngle*57.295827)
            
                if IsTerrainWalkable(.casterX+.distCos,.casterY+.distSin) then
                    call SetUnitX(.abilityCaster,.casterX+.distCos)
                    call SetUnitY(.abilityCaster,.casterY+.distSin)
                endif
                call DestroyEffect(AddSpecialEffect(RP_EFFECT,.casterX,.casterY))
                set .distancePassed = .distancePassed + RP_DISTANCE_PER_PERIOD
            else
                set .spellPhase = 2
                
                call SetUnitVertexColor(.abilityCaster,255,255,255,0)
                
                call .createGeyser()
                
                set .distancePassed = 0.
                set .flyingMissile = CreateUnit(GetOwningPlayer(.abilityCaster),MISSILE_ID,.casterX,.casterY,.coordAngle*57.295827)
                
                call PauseTimer(.loopTimer)
                call TimerStart(.loopTimer,JP_TIMER_PERIOD,true,function thistype.callback)
            endif
        endmethod
        
        static method callback takes nothing returns nothing
            local thistype this     = GetTimerData(GetExpiredTimer())
        
            if .spellPhase == 1 then
                call .walkCallback()
            elseif .spellPhase == 2 then
                call .jumpCallback()
            endif
        endmethod
        
        static method create takes unit abilityCaster, real spellTargetX, real spellTargetY returns thistype
            local thistype this = thistype.allocate()
            
            set .abilityCaster = abilityCaster
            set .abilityLevel  = GetUnitAbilityLevel(abilityCaster,ABILITY_ID)
            set .casterX       = GetWidgetX(abilityCaster)
            set .casterY       = GetWidgetY(abilityCaster)
            set .spellTargetX  = spellTargetX
            set .spellTargetY  = spellTargetY
            set .coordAngle    = Atan2(.spellTargetY-.casterY,.spellTargetX-.casterX)
            set .distCos       = RP_DISTANCE_PER_PERIOD * Cos(.coordAngle)
            set .distSin       = RP_DISTANCE_PER_PERIOD * Sin(.coordAngle)
            set .propWindow    = GetUnitPropWindow(abilityCaster)
            set .loopTimer     = NewTimerEx(this)
            
            call SetUnitPropWindow(abilityCaster,0.)
            call SetUnitTurnSpeed(abilityCaster,0.)
            call SetUnitAnimationByIndex(abilityCaster,RP_WALK_ANIM_INDEX)
            
            call TimerStart(.loopTimer,RP_TIMER_PERIOD,true,function thistype.callback)
            
            return this
        endmethod
    endstruct
    
    private function Actions takes nothing returns boolean
        local unit    abilityCaster = GetTriggerUnit()
        local real    casterX       = GetWidgetX(abilityCaster)
        local real    casterY       = GetWidgetY(abilityCaster)
        local real    spellTargetX  = GetSpellTargetX()
        local real    spellTargetY  = GetSpellTargetY()
        
        call SetUnitFacingTimed(abilityCaster,Atan2(spellTargetY-casterY,spellTargetX-casterX)*57.295827,0.)
        call DestroyEffect(AddSpecialEffect(RP_BEFORE_EFFECT,casterX,casterY))
        call SetUnitTimeScale(abilityCaster,RP_WALK_ANIM_SPEED)
        
        call SpellActions.create(abilityCaster,spellTargetX,spellTargetY)

        set abilityCaster = null
        return false
    endfunction
    
    private function Init takes nothing returns nothing
        call RegisterSpellEffectEvent(ABILITY_ID,function Actions)
    endfunction
endlibrary
v1.2
Made more changes requested by Maker

v1.1
Made changes requested by Maker


Keywords:
tidal, panda, pandaren, payback, water, ice, cold, jump, spell, good, effective, ultimate
Contents

Tidal Payback v1.2 (Map)

Reviews
Tidal Payback v1.2 | Reviewed by Maker | 6th Oct 2013 APPROVED The spell is now so much better than what it first was You could make Actions and Init part of the struct Method callback is redundant, remove it CTL...

Moderator

M

Moderator


Tidal Payback v1.2 | Reviewed by Maker | 6th Oct 2013
APPROVED


126248-albums6177-picture66521.png


  • The spell is now so much better than what it first was
126248-albums6177-picture66523.png


  • You could make Actions and Init part of the struct
  • Method callback is redundant, remove it
  • CTL by Nestharus is an awesome library
[tr]



Tidal Payback v1.1b | Reviewed by Maker | 4th Oct 2013
NEEDS FIX


126248-albums6177-picture66522.png


  • Null handle type struct members after you destroy/remove them to avoid leaks
    Units, timers, effects...
  • You need pathing detection because you can get stuck everywhere,
    for example in the middle of buildings
126248-albums6177-picture66523.png


  • timer is not a good variable name as it gets highlighted
  • You could make Actions and Init part of the struct
  • CTL by Nestharus is an awesome library
  • You could add support for limiting units being damaged only once per cast
  • You set facing at 0 rate, so why not use SetUnitFacing
    instead of SetUnitFacingTimed
  • You could try not hiding the caster, set vertex color to 0 instead.
    Move it along with the dummy. This way the caster won't disappear
    from the minimap and you don't need to lose selection
  • Selection status should be saved before hiding the unit
[tr]


Tidal Payback v1.0 | Reviewed by Maker | 28th Sep 2013
NEEDS FIX


126248-albums6177-picture66521.png


  • Well chosen sound effects and icon.
    The Thunderclap and Naga Death effects are a bit overused in spells generally but are ok for this spell
  • The spell is leakless an MUI. Well, creating units leaks but only very little
126248-albums6177-picture66522.png


  • When casting the ability to go out of walkable map bounds, the geysir
    can appear quite far away from where the caster exited the map.
    The caster itself also teleports here and there which looks unprofessional
  • Under no circumstance do you use PauseUnit to disable user commands.
    Do not use it with spells, ever
  • Use the ability level at the time of casting
126248-albums6177-picture66523.png


  • The text in the tooltip is ok, but it could be improved.
    It is not in a standard ability format. I prefer sticking to the standard Warcraft style,
    because if you import 20 spells into your map and have to change every single one,
    it can be a pain in the rear end.You should list the next level to be learned like
    "Ability Name - [Level x]" and it should list the per level stats as every standard
    non ultimate spell does. The text is not completely in Warcraft style. You have some
    excessive "the" articles in there, for example "Does the charge", "all the units nearby".
    The text could use some polishing and could be slightly compressed. The text should not be very long.
  • Always place two or more caster in a test map so it is easy to test if the spell is MUI
  • If the spell alters flying hight, it is a very good idea to not have completely flat terrain
  • The dummy units do not need Invulnerability ability. They have Locust
    which makes them invulnerable
  • You could consider using Spell Effect Event
  • You could consider using Table
  • You could consider using TimerUtils
  • You could have used struct and TimerUtils to attach the struct to the timer
  • You could consider merging actions with conditions, you woul only add a condition to your trigger, not actions
  • 0.04 seconds is a bit slow period for constant movement as it makes things
    look choppy. It updates the position only 25 times per second
    1/32 rate is the most common one used, it offers good performance and smoothness
  • When using low rates like around 0.04, a single timer can and should be used
    to handle all instances.
    Now that I think of it, CTL library by Netharus would have been perfect for this
  • Precalculate RP_DISTANCE_PER_PERIOD * Cos(coordAngle) and the Sin one also
  • In Timer_Walkyou calculate nextX, nextY, abilityLevel even if
    you do not need them.
    Same thing in Timer_Jump, you calculate a lot of stuff you might not need
  • Usually spells based on Channel should not have "Disable other abilities" enabled
[tr]
 
Level 9
Joined
Dec 3, 2010
Messages
162
You could improve the spell's configuration. Currently, your incremental values are limited to static increases. Let's say what if I wanted distance values to be like 1000/1100/1300/1600. You could use a function to improve customization.

CalcAngleBetweenCords is unnecessary. Just use Atan2 directly when needed.

Weapon type can be left as null in UnitDamageTarget.

You do not need to create a group for TempGroup. Simply use bj_lastCreatedGroup

You don't need the Action. Just merge your actions into your Condition.
JASS:
private function Actions takes nothing returns boolean
    if GetSpellAbilityId() == ABILITY_ID then
        // your actions
    endif

    return false
endfunction

private function Init takes nothing returns nothing
    local trigger AbilityCast = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(AbilityCast,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(AbilityCast,Condition(function Actions))
    set AbilityCast = null
endfunction

You could be using a struct for this. Any reason why you're not using it? Or do you just prefer hashtables? You could use a struct-based timer for your movement part if you were to use a struct, which would improve efficiency.
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
Storing Cos and Sin is still more function cost than using them directly. That is why I did so.

At the moment you do:
  • 1 Hashtable read
  • 1 Sin and 1 Cos calls respectively
  • 2 (real) multiplications and 2 additions

With them being precalculated you'd do:
  • 2 Hashtable reads (yup, you don't need to read the angle for other things)
  • 2 additions.

So how is this slower than calling Sin and Cos every single time?

I'd have also considered using arrays and storing only an integer (kinda like how structs are implemented in vJASS), the instance in the hashtable, OR using a unit indexer and attaching it to the unit's index (via an int array). With the latter one you'd only be working with arrays, giving the code a huge speed boost. You could have also used a struct with one of the mentioned storage methods, but I'm pretty sure you have your reasons why you avoided them.
 
Top