• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Panda Roll v1.01

originally created this spell for pacman on my map project - Grand Battle :)

Panda Roll

Ability Type: Active
Target Type: None
Effect: Bounce Damage
Cooldown: 20 seconds
Mana Cost: 100

Description:
Panda turns into a ball rolling in any direction around the field, attack enemies within 1000 range, until Panda Roll is disabled, or runs out of mana or there is no target. Drains mana per second.

Level 1 - 10 damage per hit, drains 20 mana/s.
Level 2 - 20 damage per hit, drains 15 mana/s.
Level 3 - 30 damage per hit, drains 10 mana/s.


JASS:
library PandaRoll requires CTL, OrderEvent
//*-------------------------------------------------------------------------------------------
//* __________________________________________________________________________________________
//*                     PANDA ROLL - v1.01
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* Requirements
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* - CTL          by   Nestharus  
//* - OrderEvent   by   Bribe      
//*
//* Importing
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* 1. Copy the ability, buff, dummy unit, and import dummy.mdx model
//* 2. Implement the required libraries
//* 3. Copy this trigger
//* 4. Configure the spell, and make sure the buff is correct
//*-------------------------------------------------------------------------------------------
//*                             CALIBRATION SECTION
//*-------------------------------------------------------------------------------------------
    globals
        // the rawcode of the ability
        private constant integer    SPELL_ID            = 'A000' 
    
        // the rawcode of the buff
        private constant integer    BUFF_ID             = 'B000'
        
        // the rawcode of the dummy unit
        private constant integer    DUMMY_ID            = 'e000'
        
        // order string turn on
        private constant string     ORDER_ON_ID         = "immolation"
        
        // order string turn off
        private constant string     ORDER_OFF_ID        = "unimmolation"
        
        // effect on cast
        private constant string     CAST_FX             = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
        
        // effect on hit
        private constant string     HIT_FX              = "Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl"
        
        // dummy effect
        private constant string     DUMMY_FX            = "Units\\Creeps\\FirePandarenBrewmaster\\FirePandarenBrewmaster_Missile.mdl"
        
        // dummy scaling
        private constant real       DUMMY_SCALE         =   1.7
        
        // dummy fly height
        private constant real       DUMMY_Z             =   100.
        
        // roll speed
        private constant real       SPEED               =   1000. 
        
        // distance increment
        private constant real       DISTANCE_INC        =   SPEED * .031250000 
        
        // attack type
        private constant attacktype AT                  =   ATTACK_TYPE_NORMAL
        
        // damage type
        private constant damagetype DT                  =   DAMAGE_TYPE_MAGIC
        
        // weapon type
        private constant weapontype WT                  =   WEAPON_TYPE_WHOKNOWS
        
        // caster vertex color
        private constant integer    RED_C_CASTER        =   255
        
        private constant integer    BLUE_C_CASTER       =   255
        
        private constant integer    GREEN_C_CASTER      =   255
        
        private constant integer    ALPHA_C_CASTER      =   255
        
        // max distance
        private constant real       MAX_DISTANCE_VALUE  =   2147483647.
                                                            
        // preload fx
        private constant boolean    PRELOAD             =   true
    endglobals
    
    // area of effect to find the closest target
    private constant function GetAoE takes integer lvl returns real
        return 700. + lvl * 100.
    endfunction
    
    // damage per level
    private constant function GetDamage takes integer lvl returns real
        return 10. * lvl
    endfunction
    
//*-----------------------------------------------------------------------------------
//*                             END OF CALIBRATION SECTION
//*-----------------------------------------------------------------------------------
    private struct Spell extends array
        
        static unit array caster //caster unit
        static unit array target //target unit
        static unit array dummy  //dummy unit
        static effect array fx   //dummy effect
        static integer array lvl //level of ability when it cast
        
        implement CTL
            local real xCaster 
            local real yCaster 
            local real xTarget 
            local real yTarget 
            local real tempX
            local real tempY
            local real angle 
            local real dx 
            local real dy 
            local unit t 
            local real nearestDist
            local real dist
            
        implement CTLExpire
        
            set xCaster = GetUnitX(caster[this])
            set yCaster = GetUnitY(caster[this])
            set xTarget = GetUnitX(target[this])
            set yTarget = GetUnitY(target[this])
            set angle = Atan2(yTarget-yCaster,xTarget-xCaster)
            set dx = DISTANCE_INC * Cos(angle)
            set dy = DISTANCE_INC * Sin(angle)
            set t = null
            set nearestDist = MAX_DISTANCE_VALUE
            
            if GetUnitAbilityLevel(caster[this], BUFF_ID) == 0 or target[this] == null or IsUnitType(target[this],UNIT_TYPE_DEAD) then
                call SetUnitPathing(caster[this], true)
                call SetUnitVertexColor(caster[this], RED_C_CASTER, BLUE_C_CASTER, GREEN_C_CASTER, ALPHA_C_CASTER)
                call IssueImmediateOrder(caster[this], ORDER_OFF_ID)
                call DestroyEffect(AddSpecialEffect(CAST_FX, GetUnitX(caster[this]), GetUnitY(caster[this])))
                call DestroyEffect(fx[this])
                call RemoveUnit(dummy[this])
                set caster[this] = null
                set target[this] = null
                set dummy[this] = null
                set fx[this] = null
                call destroy()
            else
                call SetUnitPosition(caster[this], xCaster + dx, yCaster + dy)
                call SetUnitX(dummy[this], GetUnitX(caster[this]))
                call SetUnitY(dummy[this], GetUnitY(caster[this]))
                
                if (xTarget-xCaster)*(xTarget-xCaster)+(yTarget-yCaster)*(yTarget-yCaster)<2500 then
                    
                    call DestroyEffect(AddSpecialEffect(HIT_FX, GetUnitX(target[this]), GetUnitY(target[this])))
                    call UnitDamageTarget(caster[this], target[this], GetDamage(lvl[this]), true, false, AT, DT, WT)
                    
                    call GroupEnumUnitsInRange(bj_lastCreatedGroup, xCaster, yCaster, GetAoE(lvl[this]), null)
                    
                    loop         
                        set t = FirstOfGroup(bj_lastCreatedGroup)
                        exitwhen t == null 
                        set tempX = xCaster - GetUnitX(t)
                        set tempY = yCaster - GetUnitY(t)
                        set dist = tempX * tempX + tempY * tempY
                        if dist < nearestDist and (t != target[this] and IsUnitEnemy(t, GetOwningPlayer(caster[this])) and not IsUnitType(t, UNIT_TYPE_STRUCTURE) and not IsUnitType(t,UNIT_TYPE_DEAD)) then
                            set nearestDist = dist
                            set target[this] = t
                            exitwhen true
                        endif
                        call GroupRemoveUnit(bj_lastCreatedGroup, t)
                    endloop
                    
                endif
                
            endif
            
        implement CTLNull
            set t = null
        implement CTLEnd
    
        private static method onStart takes unit u, integer level returns nothing
            local thistype this = create()
            local unit t = null
            local real nearestDist = MAX_DISTANCE_VALUE
            local real dist
            local real x
            local real y
            local real tempX
            local real tempY
        
            set target[this] = null
            set caster[this] = u
            set lvl[this] = level
            set x = GetUnitX(u)
            set y = GetUnitY(u)
            
            call DestroyEffect(AddSpecialEffect(CAST_FX, x, y))
            set dummy[this] = CreateUnit(GetOwningPlayer(u), DUMMY_ID, GetUnitX(u), GetUnitY(u), GetUnitFacing(u))
            set fx[this] = AddSpecialEffectTarget(DUMMY_FX, dummy[this], "origin")
            
            call SetUnitScale(dummy[this], DUMMY_SCALE, DUMMY_SCALE, DUMMY_SCALE)
            call SetUnitFlyHeight(dummy[this], DUMMY_Z, 0)
            
            call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, GetAoE(lvl[this]), null)
            
            loop         
                set t = FirstOfGroup(bj_lastCreatedGroup)
                exitwhen t == null 
                set tempX = x - GetUnitX(t)
                set tempY = y - GetUnitY(t)
                set dist = tempX * tempX + tempY * tempY
                if dist < nearestDist and (t != target[this] and IsUnitEnemy(t, GetOwningPlayer(caster[this])) and not IsUnitType(t, UNIT_TYPE_STRUCTURE) and not IsUnitType(t,UNIT_TYPE_DEAD)) then
                    set nearestDist = dist
                    set target[this] = t
                    exitwhen true
                endif
                call GroupRemoveUnit(bj_lastCreatedGroup, t)
            endloop
            
            call SetUnitPathing(u, false)
            call SetUnitVertexColor(caster[this], RED_C_CASTER, BLUE_C_CASTER, GREEN_C_CASTER, 0)
            
            set t = null
        endmethod
        
        private static method onRun takes nothing returns nothing
            if GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID) > 0 then
                call onStart(GetTriggerUnit(), GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID))
            endif
        endmethod
        
        private static method onInit takes nothing returns nothing
            call RegisterOrderEvent(OrderId(ORDER_ON_ID), function thistype.onRun)
            
            static if PRELOAD then
                call Preload(CAST_FX)
                call Preload(HIT_FX)
                call Preload(DUMMY_FX)
            endif
            
        endmethod
    endstruct
    
endlibrary


History
v1.00
-----------------------
- Initial release

v.101
-----------------------
- Added OrderEvent to the requirement.
- Added vertex color to constant.
- Make 'Amrf' and 'Aloc' default in the object editor.

Keywords:
roll, panda, rolling, fire, ninja, epic, chain, sword
Contents

Panda Roll v1.01 (Map)

Reviews
9th Dec 2011 Bribe: Approved 4/5 (Recommended). Setting the caster's Vertex color values back to 255 could be unsafe if the caster is supposed to have different values. You should have a "static if VERTEX_COLOR" so that it is optional for the user...

Moderator

M

Moderator

9th Dec 2011
Bribe: Approved 4/5 (Recommended).

Setting the caster's Vertex color values back to 255 could be unsafe if the caster is supposed to have different values. You should have a "static if VERTEX_COLOR" so that it is optional for the user ot have it or not.

SetUnitScale can put 0 as the second 2 parameters, they do nothing.

Your trigger fires and creates the local variables and checks if the unit has the ability before even checking if the right order was cast. There are two proposals for making this better:

JASS:
if GetIssuedOrderId() == OrderId(ORDER_ON_ID) then
    //call a function to handle the actions
endif

or use OrderEvent (the best way because then the trigger doesn't even evaluate unless it's the right order):

JASS:
call RegisterOrderEvent(OrderId(ORDER_ID_ON), function thistype.run)
 
library PandaRoll requires CTL

Do you know how much I love you now? This much:
JASS:
loop
    exitwhen NEVER
endloop

private constant real MAX_DISTANCE_VALUE = 10000000000.

HURRRR
You can change this to 2147483647. instead because that number causes an overflow ;o (Reals can go beyond 2^31-1 in Warcraft, but not when their initial values are greater :eek: (According to my tests a month ago))

Good Job on this spell, I love how you made your structs extend arrays and how you used static arrays :3

set dist = Pow(xCaster - GetUnitX(t), 2.) + Pow(yCaster - GetUnitY(t), 2.)
Really? ;o
You can do this instead:
JASS:
set tempX = xCaster - GetUnitX(t)
set tempY = yCaster - GetUnitY(t)
set dist = tempX*tempX + tempY*tempY

Oh and you NEVER have to null t because when you exit that FirstOfGroup loop, it's going to be null anyways ;)

Just remove that line that nulls t in the middle of CTLExpire and CTLNull and that line inside CTLNull.

JASS:
                call UnitAddAbility(dummy[this], 'Amrf')
                call UnitAddAbility(dummy[this], 'Aloc')

Instead of adding these abilities, you can make them default in the object editor.

Overall, nice job. 5/5 for the quality of the code (Meets my high standards)
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
For
JASS:
if (GetUnitAbilityLevel(u, SPELL_ID) > 0 and GetIssuedOrderId() == OrderId(ORDER_ON_ID)) then
I'd just suggest switching the order around to
JASS:
if GetIssuedOrderId() == OrderId(ORDER_ON_ID)) and (GetUnitAbilityLevel(u, SPELL_ID) > 0 then
since and short circuits.
I think you should also have configurables for the caster's vertex color.
 
UPDATED

Do you know how much I love you now? This much:
JASS:
loop
    exitwhen NEVER
endloop

private constant real MAX_DISTANCE_VALUE = 10000000000.

HURRRR
You can change this to 2147483647. instead because that number causes an overflow ;o (Reals can go beyond 2^31-1 in Warcraft, but not when their initial values are greater :eek: (According to my tests a month ago))

Good Job on this spell, I love how you made your structs extend arrays and how you used static arrays :3

set dist = Pow(xCaster - GetUnitX(t), 2.) + Pow(yCaster - GetUnitY(t), 2.)
Really? ;o
You can do this instead:
JASS:
set tempX = xCaster - GetUnitX(t)
set tempY = yCaster - GetUnitY(t)
set dist = tempX*tempX + tempY*tempY

Oh and you NEVER have to null t because when you exit that FirstOfGroup loop, it's going to be null anyways ;)

Just remove that line that nulls t in the middle of CTLExpire and CTLNull and that line inside CTLNull.

JASS:
                call UnitAddAbility(dummy[this], 'Amrf')
                call UnitAddAbility(dummy[this], 'Aloc')

Instead of adding these abilities, you can make them default in the object editor.

Overall, nice job. 5/5 for the quality of the code (Meets my high standards)

Or sqrt((480*128)^2*2) = 87000 :D

- i prefer 2147483647, because 87000 or 92083 doesn't work don't know why :ogre_hurrhurr:

- i need to null t because t not nulled when meet exitwhen true :ogre_hurrhurr:

JASS:
if dist < nearestDist and (t != target[this] and IsUnitEnemy(t, GetOwningPlayer(caster[this])) and not IsUnitType(t, UNIT_TYPE_STRUCTURE) and not IsUnitType(t,UNIT_TYPE_DEAD)) then
                            set nearestDist = dist
                            set target[this] = t
                            exitwhen true
                        endif

Rating: 4.5/5
The spell is nice
I have no problems with it
Good use of model and nice idea

thanks :ogre_hurrhurr:

For
JASS:
if (GetUnitAbilityLevel(u, SPELL_ID) > 0 and GetIssuedOrderId() == OrderId(ORDER_ON_ID)) then
I'd just suggest switching the order around to
JASS:
if GetIssuedOrderId() == OrderId(ORDER_ON_ID)) and (GetUnitAbilityLevel(u, SPELL_ID) > 0 then
since and short circuits.
I think you should also have configurables for the caster's vertex color.

added vertex color to constant :ogre_haosis:
 
i need to null t because t not nulled when meet exitwhen true

True that :3 (Didn't notice it)

One other thing:

JASS:
            local thistype this = create()
            local unit t = null
            local real nearestDist = MAX_DISTANCE_VALUE
            local real dist
            local real x
            local real y
            local real tempX
            local real tempY
        
            set target[this] = null
            set caster[this] = u
            set lvl[this] = level
            set x = GetUnitX(u)
            set y = GetUnitY(u)
            
            call DestroyEffect(AddSpecialEffect(CAST_FX, x, y))
            set dummy[this] = CreateUnit(GetOwningPlayer(u), DUMMY_ID, GetUnitX(u), GetUnitY(u), GetUnitFacing(u))
            set fx[this] = AddSpecialEffectTarget(DUMMY_FX, dummy[this], "origin")

Inefficient :eek:

->

JASS:
            local thistype this = create()
            local unit t
            local real nearestDist = MAX_DISTANCE_VALUE
            local real dist
            local real x = GetUnitX(u)
            local real y = GetUnitY(u)
            local real tempX
            local real tempY
        
            set target[this] = null
            set caster[this] = u
            set lvl[this] = level
            
            call DestroyEffect(AddSpecialEffect(CAST_FX, x, y))
            set dummy[this] = CreateUnit(GetOwningPlayer(u), DUMMY_ID, x, y, GetUnitFacing(u))
            set fx[this] = AddSpecialEffectTarget(DUMMY_FX, dummy[this], "origin")

That's better :3

edit

Also, over here:

JASS:
            set xCaster = GetUnitX(caster[this])
            set yCaster = GetUnitY(caster[this])
            set xTarget = GetUnitX(target[this])
            set yTarget = GetUnitY(target[this])
            set angle = Atan2(yTarget-yCaster,xTarget-xCaster)
            set dx = DISTANCE_INC * Cos(angle)
            set dy = DISTANCE_INC * Sin(angle)
            set t = null
            set nearestDist = MAX_DISTANCE_VALUE

There's this really awkward set t = null line ._.
 
Top