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

Amumu "The Sad Mummy" v1.1

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Amumu of LoL
Use: Vexorian - TimerUtils
In stock
JASS:
scope AmumuQ initializer Init

    globals
        private constant real   SPEED            = 60    // Dummy and caster speed
        private constant real   BASIC_DAMAGE     = 30    // Standard damage, how it's work? Watch to GetDamage function
        private constant real   DAMAGE_PER_LVL   = 50    // Damage pre lvl
        private constant real   DISTANCE         = 800   // Max distance
        private constant real   RANGE            = 100   // Unit pick radius
        private constant real   MAGIC_NUMBER     = 57.29 // Прост))00
        private constant real   PERIOD           = .04   // Timeout timer
        
        private constant string LIGHTNING_MODEL  = "DRAL"    // Lightning type
        
        private constant integer ABILITY_ID      = 'A000'    // Spell ID
        private constant integer DUMMY_CASTER_ID = 'h002'    // Dummy caster
        private constant integer STUN_ID         = 'A005'    // Stun spell id
        private constant integer DUMMY_HEAD_ID   = 'h001'    // Head chain dummy ID
        
        private constant boolean STUN            = true  // boolean for end spell, if stun = true then target is stunned
        private constant boolean PRELOAD         = true  // give preload for stun ability?
        
        private constant attacktype ATTACK_TYPE  = ATTACK_TYPE_HERO 
        private constant damagetype DAMAGE_TYPE  = DAMAGE_TYPE_MAGIC
        
        /**/
        private group GROUP = CreateGroup()
    endglobals
    
    //Target check
    private function Q_Boolean takes unit u,unit e returns boolean
        return /*
        */ IsUnitEnemy(e,GetOwningPlayer(u)) and /* // target enemy? true
        */ not IsUnitType(e,UNIT_TYPE_DEAD) and /* //Alive target?
        */ GetUnitTypeId(e) != 0 and /* // Alive target?
        */ not IsUnitType(e, UNIT_TYPE_STRUCTURE) and /* // target don't structure
        */ not IsUnitType(e, UNIT_TYPE_MECHANICAL) and /* // target don't mechanical
        */ not IsUnitType(e, UNIT_TYPE_MAGIC_IMMUNE) // target don't magic immune
    endfunction
    
    //Damage easy function
    private function GetDamage takes unit u returns real
        return BASIC_DAMAGE + (DAMAGE_PER_LVL * GetUnitAbilityLevel(u,ABILITY_ID))
    endfunction
    
    //This function checks the distance
    function DistanceBetweenCoords takes real x1, real y1, real x2, real y2 returns real 
        return SquareRoot((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2))    
    endfunction
    
//Don't touch struct//////
/* */ private struct Q  //
/* */   unit caster     //
/* */   unit target     //
/* */   unit dummy      //
/* */   real damage     //
/* */   real x          //
/* */   real y          //
/* */   real dis        //
/* */   lightning l     //
/* */endstruct          //
//////////////////////////
    
    private function SecondLoop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local Q data = GetTimerData(t)
        local real x = GetUnitX(data.caster)
        local real y = GetUnitY(data.caster)
        local real X = x - SPEED * Cos(Atan2(GetUnitY(data.caster) - GetUnitY(data.target),GetUnitX(data.caster) - GetUnitX(data.target)))
        local real Y = y - SPEED * Sin(Atan2(GetUnitY(data.caster) - GetUnitY(data.target),GetUnitX(data.caster) - GetUnitX(data.target)))
            if DistanceBetweenCoords(x,y,GetUnitX(data.target),GetUnitY(data.target)) < 100 then
                call RemoveUnit(data.dummy)
                call DestroyLightning(data.l)
                call data.destroy()
                call ReleaseTimer(t)
                set data.caster = null
                set data.target = null
                set data.dummy = null
                set data.l = null
            else
                call SetUnitX(data.caster,X)
                call SetUnitY(data.caster,Y)
                call MoveLightning(data.l,true,x,y,GetUnitX(data.dummy),GetUnitY(data.dummy))
            endif
        set t = null
    endfunction

    private function FirstLoop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local Q data = GetTimerData(t)
        local real x = GetUnitX(data.dummy)
        local real y = GetUnitY(data.dummy)
        local real X = x + SPEED * Cos(Atan2(data.y - GetUnitY(data.caster),data.x - GetUnitX(data.caster)))
        local real Y = y + SPEED * Sin(Atan2(data.y - GetUnitY(data.caster),data.x - GetUnitX(data.caster)))
        local unit e
            if data.dis >= DISTANCE then
                call RemoveUnit(data.dummy)
                call DestroyLightning(data.l)
                call data.destroy()
                call ReleaseTimer(t)
                set data.caster = null
                set data.target = null
                set data.dummy = null
                set data.l = null
            else
                call SetUnitX(data.dummy,X)
                call SetUnitY(data.dummy,Y)
                call MoveLightning(data.l,true,GetUnitX(data.caster),GetUnitY(data.caster),x,y)
                call GroupEnumUnitsInRange(GROUP,X,Y,RANGE, null)
                loop 
                set e = FirstOfGroup(GROUP)
                exitwhen(e == null)
                call GroupRemoveUnit(GROUP,e)
                    if Q_Boolean(data.caster,e) then
                        call UnitDamageTarget(data.caster,e,data.damage,false,false,ATTACK_TYPE,DAMAGE_TYPE,null)
                        if STUN then
                            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(data.caster),DUMMY_CASTER_ID,x,y,GetUnitFacing(data.target))
                            call UnitAddAbility(bj_lastCreatedUnit,STUN_ID)
                            call IssueTargetOrder(bj_lastCreatedUnit,"thunderbolt",e)
                        endif
                        call PauseTimer(t)
                        call TimerStart(t,.04,true,function SecondLoop)
                        set data.target = e
                    endif
                endloop
            endif
            call GroupClear(GROUP)
            set data.dis = data.dis + SPEED
            set t = null
            set e = null
    endfunction
    
    private function QAct takes nothing returns boolean
        local Q data
        if GetSpellAbilityId() == ABILITY_ID then
            set data = Q.create()
            set data.caster = GetTriggerUnit()
            set data.dummy = CreateUnit(GetTriggerPlayer(),DUMMY_HEAD_ID,GetUnitX(data.caster),GetUnitY(data.caster),Atan2(GetSpellTargetY() - GetUnitY(data.caster),GetSpellTargetX() - GetUnitX(data.caster)) * MAGIC_NUMBER)
            set data.damage = GetDamage(data.caster)
            set data.l = AddLightning(LIGHTNING_MODEL,true,GetUnitX(data.caster),GetUnitY(data.caster),GetUnitX(data.dummy),GetUnitY(data.dummy))
            set data.dis = 0.
            set data.x = GetSpellTargetX()
            set data.y = GetSpellTargetY()
            call TimerStart(NewTimerEx(data),PERIOD,true,function FirstLoop)
        endif
        return false
    endfunction

    private function Init takes nothing returns nothing
        local trigger trg = CreateTrigger()
        static if PRELOAD then
            set bj_lastCreatedUnit = CreateUnit(Player(0),DUMMY_CASTER_ID,0,0,0)
            call UnitAddAbility(bj_lastCreatedUnit,STUN_ID)
        endif
        call TriggerRegisterAnyUnitEventBJ(trg,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(trg,Condition(function QAct))
        set trg = null
    endfunction

endscope
JASS:
scope AmumuW initializer Init

    globals
        private constant real BASIC_DAMAGE       = 4     // Standard damage, how it's work? Watch to GetDamage function
        private constant real DAMAGE_PER_LVL     = 4     // Per lvl damage
        private constant real HP_BASIC_DAMAGE    = 1.2   // Percentage of the damage on the health of
        private constant real HP_DAMAGE_PER_LVL  = .3    // Per lvl hp damage
        private constant real RANGE              = 300   // Radius
        private constant real REDIOD             = 1     // Evre x seconde
        
        private constant integer ABILITY_ID      = 'A001'  // Spell id
        private constant integer BUFF_ID         = 'B000'  // Buff id
        
        private constant attacktype ATTACK_TYPE  = ATTACK_TYPE_HERO 
        private constant damagetype DAMAGE_TYPE  = DAMAGE_TYPE_MAGIC
        
        /**/
        private group GROUP = CreateGroup()
    endglobals
    
    //Target check
    private function W_Boolean takes unit u,unit e returns boolean
        return /*
        */ IsUnitEnemy(e,GetOwningPlayer(u)) and /* // target enemy? true
        */ not IsUnitType(e,UNIT_TYPE_DEAD) and /* //Alive target?
        */ GetUnitTypeId(e) != 0 and /* // Alive target?
        */ not IsUnitType(e, UNIT_TYPE_STRUCTURE) and /* // target don't structure
        */ not IsUnitType(e, UNIT_TYPE_MECHANICAL) and /* // target don't mechanical
        */ not IsUnitType(e, UNIT_TYPE_MAGIC_IMMUNE) // target don't magic immune
    endfunction
    
    private function GetDamage takes unit u,unit e returns real
        return (BASIC_DAMAGE + (DAMAGE_PER_LVL*GetUnitAbilityLevel(u,ABILITY_ID))) + (((HP_BASIC_DAMAGE+HP_DAMAGE_PER_LVL*GetUnitAbilityLevel(u,ABILITY_ID))*GetWidgetLife(e))*.01)
    endfunction
    
//Don't touch struct//////
/* */private struct W   //
/* */   unit caster     //
/* */endstruct          //
//////////////////////////

    private function FirstLoop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local W data = GetTimerData(t)
        local real x = GetUnitX(data.caster)
        local real y = GetUnitY(data.caster)
        local unit e
        if GetUnitAbilityLevel(data.caster,BUFF_ID) > 0 and (not IsUnitType(data.caster, UNIT_TYPE_DEAD)) and GetUnitTypeId(data.caster) != 0 then
            call GroupEnumUnitsInRange(GROUP,x,y,RANGE, null)
            loop 
            set e = FirstOfGroup(GROUP)
            exitwhen(e == null)
            call GroupRemoveUnit(GROUP,e)
                if W_Boolean(data.caster,e) then
                    call UnitDamageTarget(data.caster,e,GetDamage(data.caster,e),false,false,ATTACK_TYPE,DAMAGE_TYPE,null)
                endif
            endloop
        else
            call data.destroy()
            call ReleaseTimer(t)
            set data.caster = null
        endif
        call GroupClear(GROUP)
        set e = null
        set t = null
    endfunction
    
    private function WAct takes nothing returns boolean
        local W data
        if GetSpellAbilityId() == ABILITY_ID then
            set data = W.create()
            set data.caster = GetTriggerUnit()
            call TimerStart(NewTimerEx(data),REDIOD,true,function FirstLoop)
        endif
        return false
    endfunction

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

endscope
JASS:
scope AmumuE initializer Init

    globals
        private constant real BASIC_DAMAGE       = 50     // Standard damage
        private constant real DAMAGE_PER_LVL     = 50     // Per lvl damage
        private constant real RANGE              = 350    // Radius
        
        private constant integer LOOP            = 20     // Effect count (360\LOOP,effects are created in a circle)
        private constant integer ABILITY_ID      = 'A002' // Spell id
        
        private constant string EFFECT           = "Abilities\\Spells\\Undead\\RaiseSkeletonWarrior\\RaiseSkeleton.mdl" // no comments
        
        private constant boolean PRELOAD         = true // Preload effect?
        
        private constant attacktype ATTACK_TYPE  = ATTACK_TYPE_HERO 
        private constant damagetype DAMAGE_TYPE  = DAMAGE_TYPE_MAGIC
        
        /**/
        private group GROUP = CreateGroup()
    endglobals
    
    private function GetDamage takes unit u returns real
        return BASIC_DAMAGE + (DAMAGE_PER_LVL*GetUnitAbilityLevel(u,ABILITY_ID))
    endfunction
    
    //Target check
    private function E_Boolean takes unit u,unit e returns boolean
        return /*
        */ IsUnitEnemy(e,GetOwningPlayer(u)) and /* // target enemy? true
        */ not IsUnitType(e,UNIT_TYPE_DEAD) and /* //Alive target?
        */ GetUnitTypeId(e) != 0 and /* // Alive target?
        */ not IsUnitType(e, UNIT_TYPE_STRUCTURE) and /* // target don't structure
        */ not IsUnitType(e, UNIT_TYPE_MECHANICAL) and /* // target don't mechanical
        */ not IsUnitType(e, UNIT_TYPE_MAGIC_IMMUNE) // target don't magic immune
    endfunction

    private function EAct takes nothing returns boolean
    local unit u
    local unit e
    local real x
    local real y
    local integer i
        if GetSpellAbilityId() == ABILITY_ID then
            set u = GetTriggerUnit()
            set x = GetUnitX(u)
            set y = GetUnitY(u)
            set i = 0
            call GroupEnumUnitsInRange(GROUP,x,y,RANGE, null)
            loop 
            set e = FirstOfGroup(GROUP)
            exitwhen(e == null)
            call GroupRemoveUnit(GROUP,e)
                if E_Boolean(u,e) then
                    call UnitDamageTarget(u,e,GetDamage(u),false,false,ATTACK_TYPE,DAMAGE_TYPE,null)
                endif
            endloop
            loop
            exitwhen i == LOOP
                call DestroyEffect(AddSpecialEffect(EFFECT,GetUnitX(u) + (RANGE-35) * Cos(i),GetUnitY(u) + (RANGE-35) * Sin(i)))
            set i = i + 1
            endloop
            call GroupClear(GROUP)
        endif
    set e = null
    set u = null
    return false
    endfunction

    private function Init takes nothing returns nothing
        local trigger trg = CreateTrigger()
        static if PRELOAD then
            call Preload(EFFECT)
        endif
        call TriggerRegisterAnyUnitEventBJ(trg,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(trg,Condition(function EAct))
        set trg = null
    endfunction

endscope
JASS:
scope AmumuR initializer Init

    globals
        private constant real BASIC_DAMAGE       = 50 // Standard damage
        private constant real DAMAGE_PER_LVL     = 100 // Per lvl damage
        private constant real RANGE              = 600 // Radius
        
        private constant integer LOOP            = 36 // Effect count (360\LOOP,effects are created in a circle)
        private constant integer ABILITY_ID      = 'A003' // Spell id
        private constant integer ROOT_ID         = 'A004' // Root id
        private constant integer DUMMY_MODEL_ID  = 'h003' // Duumy effect model
        private constant integer DUMMY_CASTER_ID = 'h002' // Dummy spell caster
        
        private constant string EFFECT           = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" // no comments
        
        private constant boolean PRELOAD         = true // preload effect and root?
        
        private constant attacktype ATTACK_TYPE  = ATTACK_TYPE_HERO 
        private constant damagetype DAMAGE_TYPE  = DAMAGE_TYPE_MAGIC
        
        /**/
        private group GROUP = CreateGroup()
    endglobals
    
    private function GetDamage takes unit u returns real
        return BASIC_DAMAGE + (DAMAGE_PER_LVL*GetUnitAbilityLevel(u,ABILITY_ID))
    endfunction
    
    //Target check
    private function R_Boolean takes unit u,unit e returns boolean
        return /*
        */ IsUnitEnemy(e,GetOwningPlayer(u)) and /* // target enemy? true
        */ not IsUnitType(e,UNIT_TYPE_DEAD) and /* //Alive target?
        */ GetUnitTypeId(e) != 0 and /* // Alive target?
        */ not IsUnitType(e, UNIT_TYPE_STRUCTURE) and /* // target don't structure
        */ not IsUnitType(e, UNIT_TYPE_MECHANICAL) and /* // target don't mechanical
        */ not IsUnitType(e, UNIT_TYPE_MAGIC_IMMUNE) // target don't magic immune
    endfunction

    private function RAct takes nothing returns boolean
    local unit u
    local unit e
    local unit dummy
    local real x
    local real y
    local integer i
        if GetSpellAbilityId() == ABILITY_ID then
            set u = GetTriggerUnit()
            set x = GetUnitX(u)
            set y = GetUnitY(u)
            set dummy = CreateUnit(GetOwningPlayer(u),DUMMY_MODEL_ID,x,y,0)
            set i = 0
            call SetUnitScale(dummy,RANGE/100,RANGE/100,RANGE/100)
            call GroupEnumUnitsInRange(GROUP,x,y,RANGE, null)
            loop 
            set e = FirstOfGroup(GROUP)
            exitwhen(e == null)
            call GroupRemoveUnit(GROUP,e)
                if R_Boolean(u,e) then
                    set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(u),DUMMY_CASTER_ID,x,y,GetUnitFacing(e))
                    call UnitAddAbility(bj_lastCreatedUnit,ROOT_ID)
                    call IssueTargetOrder(bj_lastCreatedUnit,"entanglingroots",e)
                    call UnitDamageTarget(u,e,GetDamage(u),false,false,ATTACK_TYPE,DAMAGE_TYPE,null)
                endif
            endloop
            loop
            exitwhen i == LOOP
                call DestroyEffect(AddSpecialEffect(EFFECT,GetUnitX(u) + (RANGE-100) * Cos(i),GetUnitY(u) + (RANGE-100) * Sin(i)))
            set i = i + 1
            endloop
            call GroupClear(GROUP)
        endif
    set u = null
    set dummy = null
    set e = null
    return false
    endfunction

    private function Init takes nothing returns nothing
        local trigger trg = CreateTrigger()
        static if PRELOAD then
            set bj_lastCreatedUnit = CreateUnit(Player(0),DUMMY_CASTER_ID,0,0,0)
            call UnitAddAbility(bj_lastCreatedUnit,ROOT_ID)
            call Preload(EFFECT)
        endif
        call TriggerRegisterAnyUnitEventBJ(trg,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(trg,Condition(function RAct))
        set trg = null
    endfunction

endscope
changelog:
v1.0 - nothing
v1.1 - fixed to "Need fix"
In the next version I give passive ability and reduction for "Tantrum"
P.S:Excuse me for my english, I'm Russian

Keywords:
Amumu,lol,the,sad,mummy,Despair,Tantrum,Curse of the Sad Mummy,Bandage Toss
Contents

Еще одна карта (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 10:24, 13th Jun 2014 BPower: There are still a lot of things need to be done. http://www.hiveworkshop.com/forums/spells-569/amumu-sad-mummy-v1-1-a-251723/index2.html#post2540792 08:30...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

10:24, 13th Jun 2014
BPower:
There are still a lot of things need to be done.
http://www.hiveworkshop.com/forums/spells-569/amumu-sad-mummy-v1-1-a-251723/index2.html#post2540792

08:30, 7th May 2014
BPower:
Refer to my last post. For now set to Need Fix
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
Did you try to compile this ? As it is it will not compile. You can't create locals anywhere except the top of the function.

This does not check if unit is alive.
GetWidgetLife(e) > .405
You need to do this to check if a unit is alive.
GetWidgetLife( e) > .405 and not IsUnitType( e, UNIT_TYPE_DEAD) and UnitTypeId( u) != 0

You leak timers because you never destroy them.
You have to null groups after destroying them.

This is not needed and is bad to have.
call DestroyTrigger(gg_trg_Q)

This would be better if everything was in the struct. Check out my spell to see how to do that. http://www.hiveworkshop.com/forums/spells-569/doom-heal-v-1-0-5-a-235217/

Indentation is bad here.
JASS:
    function WAct takes nothing returns boolean
        local W data
            if GetSpellAbilityId() == ID then
                set data = W.create()
                set data.caster = GetTriggerUnit()
                call TimerStart(NewTimerEx(data),REDIOD,true,function FirstLoop)
            endif
        return false
    endfunction
Should be this.
JASS:
    function WAct takes nothing returns boolean
        local W data
        if GetSpellAbilityId() == ID then
            set data = W.create()
            set data.caster = GetTriggerUnit()
            call TimerStart(NewTimerEx(data),REDIOD,true,function FirstLoop)
        endif
        return false
    endfunction
The above function should also be private along with all functions that are not meant to be called outside the library / scope.

You should make a global group. Constantly creating groups is inefficient. Create the global group once and never destroy it.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Struct Q and W should be private, otherwise you'll have to choose a longer and unique name for the struct.

Please write out all configurable variables in full, for instance BDMG should be BASE_DAMAGE. You also have some typos in your very short documentation.

AmumuQ:
JASS:
        set bj_lastCreatedUnit = CreateUnit(Player(0),'h002',0,0,0)
        call UnitAddAbility(bj_lastCreatedUnit,STUNID)
What is this about? Preloading can be done by the mapper himself.
Also this unit will remain forever, furthermore 'h002' may not exist in the object editor.

function QAct should be private.
You don't have to call GetTriggerUnit() again, since you already stored it in data.caster
You could also store GetTriggerPlayer() into a struct member.

* 57.29 magic numbers should be part of the configuration.

Damage calculation should also be configurable. I recommend you write a constant function GetDamage(...) and add it to the user configuration part.

The timer interval should also be configurable.

Make sure you have all over a proper identation.

Create one global group for the FoG enumeration instead of multiple local ones.

Pausing units is always a very bad choice, if possible try to avoid it.

In all three functions you should cache GetUnitX/Y into a local variable. Variable lookups are much faster than function calls.

In second loop local unit e and group g is not required at all.

In order to check if a unit is alive or not you can either do:
IsUnitType(unit, UNIT_TYPE_DEAD) and GetUnitTypeId(unit) != 0 or you declare native UnitAlive takes unit id returns boolean and use it.

Function QBool should be found above the struct and have a more distinct name.

In general all struct members which could leak an handle should be nulled properly, once you call method destroy().

AmumuW:
Same issue like above concerning the user configuration.

In FirstLoop you could cache GetUnitX/Y, furthermore use one global group for the group enumeration.

The damage calculation should be part of the user config. Design a private function called GetDamage()

Struct members should be nulled properly at the end.

AmumuE and AmumuR:
Won't compile at all. Same issues like the two above.
Preloading should be within static ifs
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
Where is this function. call ReleaseTimer(t)

Also all the problems I said above will stop this from working. You do have leaks.
You leak local variables. You leak timers unless that function above destroys timers in which case you should simply call DestroyTimer rather than making a function that does the same thing.

And as said above by me and Bpower. Your GetWidgetLife(e) > .405 does not check if unit is actually dead.
 
Level 4
Joined
Jan 8, 2013
Messages
16
Struct Q and W should be private, otherwise you'll have to choose a longer and unique name for the struct.

Please write out all configurable variables in full, for instance BDMG should be BASE_DAMAGE. You also have some typos in your very short documentation.

AmumuQ:
JASS:
        set bj_lastCreatedUnit = CreateUnit(Player(0),'h002',0,0,0)
        call UnitAddAbility(bj_lastCreatedUnit,STUNID)
What is this about? Preloading can be done by the mapper himself.
Also this unit will remain forever, furthermore 'h002' may not exist in the object editor.

function QAct should be private.
You don't have to call GetTriggerUnit() again, since you already stored it in data.caster
You could also store GetTriggerPlayer() into a struct member.

* 57.29 magic numbers should be part of the configuration.

Damage calculation should also be configurable. I recommend you write a constant function GetDamage(...) and add it to the user configuration part.

The timer interval should also be configurable.

Make sure you have all over a proper identation.

Create one global group for the FoG enumeration instead of multiple local ones.

Pausing units is always a very bad choice, if possible try to avoid it.

In all three functions you should cache GetUnitX/Y into a local variable. Variable lookups are much faster than function calls.

In second loop local unit e and group g is not required at all.

In order to check if a unit is alive or not you can either do:
IsUnitType(unit, UNIT_TYPE_DEAD) and GetUnitTypeId(unit) != 0 or you declare native UnitAlive takes unit id returns boolean and use it.

Function QBool should be found above the struct and have a more distinct name.

In general all struct members which could leak an handle should be nulled properly, once you call method destroy().

AmumuW:
Same issue like above concerning the user configuration.

In FirstLoop you could cache GetUnitX/Y, furthermore use one global group for the group enumeration.

The damage calculation should be part of the user config. Design a private function called GetDamage()

Struct members should be nulled properly at the end.

AmumuE and AmumuR:
Won't compile at all. Same issues like the two above.
Preloading should be within static ifs

I corrected, then that could translate.
Check
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
In your unit alive check you need to check if GetWidgetLife( unit) > 0.405

You really should use a local variable instead of this. bj_lastCreatedUnit

Never use PauseUnit() in spells or systems.
Instead get unit prop window. then set the units prop window to 0. At end of spell set unit prop window back to the original.

Also this is vJass only so it should not have jass category tag in spell description.
 

Cokemonkey11

Spell Reviewer
Level 29
Joined
May 9, 2006
Messages
3,534
In your unit alive check you need to check if GetWidgetLife( unit) > 0.405

No.

Never use PauseUnit() in spells or systems.
Instead get unit prop window. then set the units prop window to 0. At end of spell set unit prop window back to the original. Or, use a system that handles CC.

Fixed.
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
I didn't read the previous comment I'm here on a fast look :

On the first spell :
I highly suggest to use native UnitAlive takes unit id return bolean
Make the function private for the coordinates or just inline it everywhere (I prefer inline).
I HIGHLY recommend not to use PauseUnit to stun a unit but to use the stun system here in the JASS section.
if STUN == true then->if STUN then
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
JASS:
        static if PRELOAD then
            set bj_lastCreatedUnit = CreateUnit(Player(0),DUMMY_CASTER_ID,0,0,0)
            call UnitAddAbility(bj_lastCreatedUnit,STUN_ID)
        endif
You should remove this unit, otherwise you have it on the map for the whole game season.

JASS:
        local real x = GetUnitX(data.dummy)
        local real y = GetUnitY(data.dummy)
        local real X = x + SPEED * Cos(Atan2(data.y - GetUnitY(data.caster),data.x - GetUnitX(data.caster)))
        local real Y = y + SPEED * Sin(Atan2(data.y - GetUnitY(data.caster),data.x - GetUnitX(data.caster)))
Use the information you stored into the local reals.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Q Spell:

Variables within the configuration part should be written out in full. This makes the code more comprehensible on the first look.
private function Q_Boolean takes unit u,unit e returns boolean --> private function QBoolean takes unit caster,unit target returns boolean
private function GetDamage takes unit u returns real --> private function GetDamage takes unit caster returns real

You also have to remove the unit used for preloading. Otherwise it would remain for the rest of the game season.
JASS:
        static if PRELOAD then
            set bj_lastCreatedUnit = CreateUnit(Player(0),DUMMY_CASTER_ID,0,0,0)
            call UnitAddAbility(bj_lastCreatedUnit,STUN_ID)
        endif
In QAct you could store GetUnitX/Y into a local real variable, because they are used 4 times. However the speed gain is not noticeable, but looks better and is a good practise for yourself.

In "FirstLoop" relabel local real X/Y into x1/y1 instead of capitalizing local variable names.

Proper lightning movement also has to take the z-axis into account and use MoveLightningEx(...). In order to retrieve the correct z value you have to use a location and the native function GetLocationZ(loc).

JASS:
                        if STUN then
                            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(data.caster),DUMMY_CASTER_ID,x,y,GetUnitFacing(data.target))
                            call UnitAddAbility(bj_lastCreatedUnit,STUN_ID)
                            call IssueTargetOrder(bj_lastCreatedUnit,"thunderbolt",e)
                        endif
This dummy caster is never removed. In general I recommend to use only one dummy caster for the whole spell. As an alternative you could also use a dummy caster resource from the JASS section as requirement.

call GroupClear(GROUP) Because of the FoG loop this group is always empty by default.

WSpell:

W_Boolean --> WBoolean, u and e --> caster and target
JASS:
        call GroupClear(GROUP)
        set e = null
These two lines are not needed, as e is null after the loop and the group will be empty

ESpell: pretty much the same as above.

RSpell: pretty much the same as QSpell.

The despair turn off tooltip is a joke.

I never played the game this spellpack is from, so I can't really judge if it is fitting or not.
 
Top