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

Spectrum Shower v1.2

Description
Summons balls of energy that fires laser in a circular motion relative to the ball itself. The balls constantly move around. Laser deals 20 damage per second. Lasts 6 seconds.

Level 1 - 4 energy balls.
Level 2 - 8 energy balls.
Level 3 - 12 energy balls.
Level 4 - 16 energy balls.

Cast Range: Self
Target Type: Instant
Cooldown: 16 seconds



Implementation
JASS:
//==============================================================================
//                         SPECTRUM SHOWER v1.2                   
//                              BY Ayanami                             
//==============================================================================

//==============================================================================
//                            REQUIREMENTS                                      
//==============================================================================
// - JNGP
// - Alloc     
// - SpellEffectEvent
//   * RegisterPlayerUnitEvent
//   * Table                                                   
// - Timer32                                                                
//==============================================================================

//==============================================================================
//                           IMPLEMENTATION                                     
//==============================================================================
// 1) Copy the whole "Required Systems" Trigger folder 
// 2) Copy the trigger "Spectrum Shower"
// 3) Copy the ability "Spectrum Shower" 
// 4) Copy the unit "Spectrum Shower Dummy"
// 5) Go through the configuration section in "Spectrum Shower" trigger                            
//==============================================================================


Spell Code

JASS:
library SpectrumShower uses Alloc, SpellEffectEvent, T32

//===========================================================================
//                           CONFIGURABLES                        
//===========================================================================

globals
    private constant integer ABIL_ID = 'ABSS' // raw code of base ability
    private constant integer DUMMY_ID = 'dSPE' // raw code of unit "Spectrum Shower Dummy"
    
    private constant integer BALL_RED = 255 // red hex value for ball
    private constant integer BALL_GREEN = 255 // green hex value for ball
    private constant integer BALL_BLUE = 255 // blue hex value for ball
    private constant integer BALL_TRANS = 255 // transparency for ball
    private constant real BALL_AREA = 400. // maximum distance which ball hovers relative to caster
    private constant real BALL_HEIGHT = 200. // ball flying height
    private constant real BALL_SCALE = 1. // scale size of ball
    private constant real BALL_SPEED = 500. // distance travelled by ball per second
    
    private constant string LASER_TYPE = "SPLK" // lightning type for laser
    private constant real LASER_RED = 255. / 255. // red value for laser (where 1.0 == 255 in terms of hex)
    private constant real LASER_GREEN = 255. / 255. // green value for laser (where 1.0 == 255 in terms of hex)
    private constant real LASER_BLUE = 255. / 255. // blue value for laser (where 1.0 == 255 in terms of hex)
    private constant real LASER_TRANS = 255. / 255. // red value for laser (where 1.0 == 255 in terms of hex)
    private constant real LASER_AREA = 100. // laser circulating distance relative to ball
    private constant real LASER_COLLISION = 50. // collision radius of laser (for damage)
    private constant real LASER_SPEED = 1. // time taken for laser to do one circular motion relative to ball
    private constant real LASER_ADJUST_HEIGHT = 50. // adjustment height for the laser
    
    private constant real ENUM_RADIUS = 176. // max collision size of a unit in your map.
    
    private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type
    private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type
endglobals

// number of energy balls
private constant function GetBallCount takes integer level returns integer
    return 4 * level
endfunction

// damage dealt per second by each laser
private constant function GetDamage takes integer level returns real
    return 20.
endfunction

// duration of spell
private constant function GetDuration takes integer level returns real
    return 6.
endfunction

// filter for allowed targets
private constant function GetFilter takes unit u, unit caster returns boolean
    return /*
    */ not IsUnitType(u, UNIT_TYPE_DEAD) /* // target is alive
    */ and IsUnitEnemy(u, GetOwningPlayer(caster)) /* // target is an enemy of caster
    */ and not IsUnitType(u, UNIT_TYPE_STRUCTURE) // target is not a structure
endfunction

//===========================================================================
//                          END CONFIGURABLES                        
//===========================================================================

globals
    private constant player NEUTRAL_PASSIVE = Player(PLAYER_NEUTRAL_PASSIVE)
    private constant real TRUE_BALL_SPEED = BALL_SPEED * T32_PERIOD
    private constant real RAD = 2 * bj_PI
    private constant real ANGLE_RATE = RAD / LASER_SPEED * T32_PERIOD
    private constant real TRUE_HEIGHT = BALL_HEIGHT + LASER_ADJUST_HEIGHT
    
    private group G = bj_lastCreatedGroup
endglobals

private struct Data extends array
    implement Alloc
    
    private unit u
    private unit dummy
    private lightning l
    private real dmg
    private real dur
    private real cos
    private real sin
    private real dist
    private real offsetCos
    private real offsetSin
    private real offset
    private real distCount
    private real a
    
    private method destroy takes nothing returns nothing
        call RemoveUnit(this.dummy)
        call DestroyLightning(this.l)
        call this.stopPeriodic()
        call this.deallocate()
    endmethod
    
    private method setOffset takes nothing returns nothing
        local real x = GetUnitX(this.dummy)
        local real y = GetUnitY(this.dummy)
        local real dx = x - GetUnitX(this.u)
        local real dy = y - GetUnitY(this.u)
        local real a
    
        set this.offset = SquareRoot(dx * dx + dy * dy)
        set a = Atan2(dy, dx)
        set this.offsetCos = Cos(a)
        set this.offsetSin = Sin(a)
        set dx = (GetUnitX(this.u) + GetRandomReal(-BALL_AREA, BALL_AREA)) - x
        set dy = (GetUnitY(this.u) + GetRandomReal(-BALL_AREA, BALL_AREA)) - y
        set this.dist = SquareRoot(dx * dx + dy * dy)
        set a = Atan2(dy, dx)
        set this.cos = Cos(a)
        set this.sin = Sin(a)
        set this.distCount = 0
    endmethod
    
    private method periodic takes nothing returns nothing
        local unit u
        local real x
        local real y
        local real a
    
        if this.dur <= 0 then
            call this.destroy()
        else
            set this.dur = this.dur - T32_PERIOD
            set this.dist = this.dist - TRUE_BALL_SPEED
            set this.distCount = this.distCount + TRUE_BALL_SPEED
            set this.a = this.a + ANGLE_RATE
            set x = (GetUnitX(this.u) + this.offset * this.offsetCos) + this.distCount * this.cos
            set y = (GetUnitY(this.u) + this.offset * this.offsetSin) + this.distCount * this.sin
            
            call SetUnitX(this.dummy, x)
            call SetUnitY(this.dummy, y)
            call MoveLightningEx(this.l, true, x, y, TRUE_HEIGHT, x + LASER_AREA * Cos(this.a), y + LASER_AREA * Sin(this.a), -LASER_ADJUST_HEIGHT)
            call GroupEnumUnitsInRange(G, x, y, LASER_COLLISION + ENUM_RADIUS, null)
            
            loop
                set u = FirstOfGroup(G)
                exitwhen u == null
                call GroupRemoveUnit(G, u)
                
                if GetFilter(u, this.u) then
                    call UnitDamageTarget(this.u, u, this.dmg, true, false, ATK, DMG, null)
                endif
            endloop
            
            if this.dist <= 0 then
                call this.setOffset()
            endif
        endif
    endmethod
    implement T32x
    
    private static method onCast takes nothing returns boolean
        local thistype this
        local unit u = GetTriggerUnit()
        local integer level = GetUnitAbilityLevel(u, ABIL_ID)
        local integer i = GetBallCount(level)
        local real dmg = GetDamage(level)
        local real dur = GetDuration(level)
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real dx
        local real dy
        
        loop
            exitwhen i == 0
            
            set this = thistype.allocate()
            set this.u = u
            set dx = x + GetRandomReal(-BALL_AREA, BALL_AREA)
            set dy = y + GetRandomReal(-BALL_AREA, BALL_AREA)
            set this.dummy = CreateUnit(NEUTRAL_PASSIVE, DUMMY_ID, dx, dy, 0)
            set this.l = AddLightningEx(LASER_TYPE, true, dx, dy, TRUE_HEIGHT, dx + GetRandomReal(-LASER_AREA, LASER_AREA), dy + GetRandomReal(-LASER_AREA, LASER_AREA), -LASER_ADJUST_HEIGHT)
            set this.dmg = GetDamage(level) * T32_PERIOD
            set this.dur = GetDuration(level)
            set this.a = GetRandomReal(-1, 1)
            
            call SetUnitVertexColor(this.dummy, BALL_RED, BALL_GREEN, BALL_BLUE, BALL_TRANS)
            call SetUnitScale(this.dummy, BALL_SCALE, BALL_SCALE, BALL_SCALE)
            call SetUnitFlyHeight(this.dummy, BALL_HEIGHT, 0)
            call SetLightningColor(this.l, LASER_RED, LASER_GREEN, LASER_BLUE, LASER_TRANS)
            call this.setOffset()
            call this.startPeriodic()
            
            set i = i - 1
        endloop
    
        set u = null
        return false
    endmethod

    private static method onInit takes nothing returns nothing
        call RegisterSpellEffectEvent(ABIL_ID, function thistype.onCast)
    endmethod
endstruct

endlibrary


Credits
Sevion - Alloc
Bribe - SpellEffectEvent
Jesus4Lyf - Timer32


Changelogs

- Initial relase


- Removed Damage and GTrigger
- Replaced GTrigger with SpellEffectEvent
- Removed unecessary locals


- Removed GroupUtils
- Optimized code
- Fixed a problem where the damage was way over the stated amount (forgot to multiply by T32_PERIOD)


Feedback will be appreciated.

Keywords:
spectrum, shower, laser, arcane, light, magic
Contents

Spectrum Shower v1.2 (Map)

Reviews
12 Nov 2011 Bribe: Everything looks good to me - thanks for making the changes!

Moderator

M

Moderator

12 Nov 2011
Bribe: Everything looks good to me - thanks for making the changes!
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
JASS:
private static method filterFunc takes nothing returns boolean
    local unit u = GetFilterUnit()
        
    if GetFilter(u, staticData.u) then
        call UnitDamageTargetEx(staticData.u, u, staticData.dmg, true, false, ATK, DMG, null)
    endif
        
    set u = null
    return false
endmethod

should be:
JASS:
private static method filterFunc takes nothing returns boolean
    if GetFilter(GetFilterUnit(), staticData.u) then
        call UnitDamageTargetEx(staticData.u, GetFilterUnit(), staticData.dmg, true, false, ATK, DMG, null)
    endif
    return false
endmethod

and

JASS:
private static method onInit takes nothing returns nothing
    local trigger t = CreateTrigger()
    call GT_RegisterStartsEffectEvent(t, ABIL_ID)
    call TriggerAddCondition(t, Condition(function thistype.onCast))
    set t = null
endmethod

should be:
JASS:
private static method onInit takes nothing returns nothing
    call TriggerAddCondition(GT_RegisterStartsEffectEvent(CreateTrigger(), ABIL_ID), Condition(function thistype.onCast))
endmethod

Those changes'd increase the speed slightly.
 
Level 4
Joined
Jun 22, 2008
Messages
49
JASS:
private static method filterFunc takes nothing returns boolean
    local unit u = GetFilterUnit()
        
    if GetFilter(u, staticData.u) then
        call UnitDamageTargetEx(staticData.u, u, staticData.dmg, true, false, ATK, DMG, null)
    endif
        
    set u = null
    return false
endmethod
should be:
JASS:
private static method filterFunc takes nothing returns boolean
    if GetFilter(GetFilterUnit(), staticData.u) then
        call UnitDamageTargetEx(staticData.u, GetFilterUnit(), staticData.dmg, true, false, ATK, DMG, null)
    endif
    return false
endmethod
Isn't it normally faster to use a local variable instead of calling a function multiple times?
 
Top