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

Zephyr Contest #14 - Unique Summoning

Status
Not open for further replies.
Level 13
Joined
Mar 29, 2012
Messages
530
WIP #1 (Concept) - Code:
  • Ancient of Power Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- --------------------------- --------
      • -------- =========================== --------
      • Set AoP_Ability = Ancient of Power
      • Set AoP_Dummy = Ancient of Power Dummy
      • Set AoP_AbilityOrder = (Order(forceofnature))
      • Set AoP_AbilityLevels = 3
      • -------- =========================== --------
      • Set AoP_SummoningDuration[1] = 10.00
      • Set AoP_SummoningDuration[2] = 10.00
      • Set AoP_SummoningDuration[3] = 10.00
      • Set AoP_SummoningPowerThreshold[1] = 2600.00
      • Set AoP_SummoningPowerThreshold[2] = 3400.00
      • Set AoP_SummoningPowerThreshold[3] = 4000.00
      • Set AoP_SummoningStartSFX[1] = war3mapImported\GreenRing.mdl
      • Set AoP_SummoningStartSFX[2] = war3mapImported\PoisonStream.mdl
      • Set AoP_SummoningStartSFX[3] = war3mapImported\Ragingslam.mdl
      • Set AoP_SummoningSFX = war3mapImported\HarvestLife.mdl
      • Set AoP_sSFXFinalSize[1] = 1.00
      • Set AoP_sSFXFinalSize[2] = 1.15
      • Set AoP_sSFXFinalSize[3] = 1.30
      • Set AoP_sSFXHeight = 0.00
      • Set AoP_sGroundSFX = Abilities\Spells\NightElf\EntanglingRoots\EntanglingRootsTarget.mdl
      • Set AoP_sGroundSFXInterval = 0.05
      • Set AoP_sGroundSFXStart = 100.00
      • Set AoP_sGroundSFXSpeed = 640.00
      • Set AoP_sGroundSFXDensity = 2.00
      • Set AoP_sGroundSFXArea = 300.00
      • -------- =========================== --------
      • Set AoP_SummonedUnit[1] = Tree of Life
      • Set AoP_SummonedUnit[2] = Tree of Ages
      • Set AoP_SummonedUnit[3] = Tree of Eternity
      • Set AoP_suFacingAngle = 270.00
      • Set AoP_suBirthAnimation = Stand
      • -------- =========================== --------
      • Set AoP_AbsorbInterval = 1.00
      • Set AoP_LifeDrain = 30.00
      • Set AoP_ManaDrain = 15.00
      • Set AoP_LifePower = 1.00
      • Set AoP_ManaPower = 2.00
      • -------- =========================== --------
      • Set AoP_ManaOrbModel = war3mapImported\Power_Orb.mdl
      • Set AoP_mOrbSize = 0.50
      • Set AoP_mOrbSpawnHeight = 150.00
      • Set AoP_mOrbSpawnOffset = 200.00
      • Set AoP_mOrbTravelTime = 1.00
      • Set AoP_mOrbArc = 0.50
      • Set AoP_mOrbCancelSFX = war3mapImported\Unleash the power.mdx
      • Set AoP_mOrbCancelSFXSize = 1.00
      • Set AoP_mOrbCancelSFXDeath = 1.00
      • -------- =========================== --------
      • Custom script: call ExecuteFunc("AoP_Initialize")
JASS:
constant function AoP_DummiesOwner takes nothing returns player
    return Player(PLAYER_NEUTRAL_PASSIVE)
endfunction

constant function AoP_FlyAbilityID takes nothing returns integer
    return 'Amrf'
endfunction

constant function AoP_FrameUpdate takes nothing returns real
    return 1 / 32.
endfunction

function AoP_IsUnitAlive takes unit u returns boolean
    return GetUnitTypeId(u) != 0 and not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction

function AoP_IsUnitChanneling takes unit u returns boolean
    return AoP_IsUnitAlive(u) and GetUnitCurrentOrder(u) == udg_AoP_AbilityOrder
endfunction

function AoP_GetMagnitude2D takes real x, real y returns real
    return SquareRoot(x*x+y*y)
endfunction

function AoP_GetMagnitude3D takes real x, real y, real z returns real
    return SquareRoot(x*x+y*y+z*z)
endfunction

function AoP_GetAngle takes real x, real y returns real
    return Atan2(y, x)
endfunction

function AoP_GetAngleZ takes real distance2D, real z returns real
    return Atan2(z, distance2D)
endfunction

function AoP_GetParabolaHeight takes real y0, real y1, real h, real d, real x returns real
    local real A = (2*(y0+y1)-4*h)/(d*d)
    local real B = (y1-y0-A*d*d)/d
    
    return A*x*x + B*x + y0
endfunction

function AoP_GetTerrainZ takes real x, real y returns real
    call MoveLocation(udg_AoP_ZLocator, x, y)
    return GetLocationZ(udg_AoP_ZLocator)
endfunction

function AoP_GetUnitZAlt takes unit u, real x, real y returns real
    return GetUnitFlyHeight(u) + AoP_GetTerrainZ(x, y)
endfunction

function AoP_UnitApplyFly takes unit u returns nothing
    if UnitAddAbility(u, AoP_FlyAbilityID()) and UnitRemoveAbility(u, AoP_FlyAbilityID()) then
    endif
endfunction

function AoP_SetUnitZAlt takes unit u, real z, real x, real y returns nothing
    call SetUnitFlyHeight(u, z - AoP_GetTerrainZ(x, y), 0)
endfunction

function AoP_SetUnitPosition takes unit u, real x, real y returns nothing
    call SetUnitX(u, x)
    call SetUnitY(u, y)
endfunction

function AoP_Destroy takes integer index returns nothing
    set udg_AoP_Recycle[udg_AoP_Recyclable] = index
    set udg_AoP_Recyclable = udg_AoP_Recyclable + 1
    set udg_AoP_Next[udg_AoP_Prev[index]] = udg_AoP_Next[index]
    set udg_AoP_Prev[udg_AoP_Next[index]] = udg_AoP_Prev[index]
    
    if udg_AoP_Next[0] == 0 then
        call PauseTimer(udg_AoP_Timer)
    endif
endfunction

function AoP_Create takes nothing returns integer
    local integer newIndex
    if udg_AoP_Recyclable == 0 then
        set udg_AoP_MaxIndex = udg_AoP_MaxIndex + 1
        set newIndex = udg_AoP_MaxIndex
    else
        set udg_AoP_Recyclable = udg_AoP_Recyclable - 1
        set newIndex = udg_AoP_Recycle[udg_AoP_Recyclable]
    endif
    set udg_AoP_Next[newIndex] = 0
    set udg_AoP_Next[udg_AoP_Prev[0]] = newIndex
    set udg_AoP_Prev[newIndex] = udg_AoP_Prev[0]
    set udg_AoP_Prev[0] = newIndex
    
    return newIndex
endfunction

function AoP_CreateParticle takes string model, real x, real y, real z, real size, real dur returns nothing
    local integer pIndex = AoP_Create()
    
    set udg_AoP_Particle[pIndex] = CreateUnit(AoP_DummiesOwner(), udg_AoP_Dummy, x, y, 0)
    call AoP_SetUnitPosition(udg_AoP_Particle[pIndex], x, y)
    call AoP_UnitApplyFly(udg_AoP_Particle[pIndex])
    call AoP_SetUnitZAlt(udg_AoP_Particle[pIndex], z, x, y)
    call SetUnitScale(udg_AoP_Particle[pIndex], size, 0, 0)
    set udg_AoP_AttachedModel[pIndex] = AddSpecialEffectTarget(model, udg_AoP_Particle[pIndex], "origin")
    
    set udg_AoP_realTimer[pIndex] = dur
    
    set udg_AoP_Stage[pIndex] = 5
endfunction

function AoP_CreateParticleTarget takes string model, unit u, real size, real dur returns nothing
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    call AoP_CreateParticle(model, x, y, AoP_GetUnitZAlt(u, x, y), size, dur)
endfunction

function AoP_StartSummoningEffect takes real x, real y, integer lvl returns nothing
    local integer eIndex = AoP_Create()
    local integer i = 0
    
    loop
        set i = i + 1
        exitwhen udg_AoP_SummoningStartSFX[i] == null
        
        call DestroyEffect(AddSpecialEffect(udg_AoP_SummoningStartSFX[i], x, y))
    endloop
    
    set udg_AoP_TargetX[eIndex] = x
    set udg_AoP_TargetY[eIndex] = y
    set udg_AoP_Level[eIndex] = lvl
    set udg_AoP_Dist[eIndex] = udg_AoP_sGroundSFXStart
    set udg_AoP_realTimer[eIndex] = 0
    
    set udg_AoP_Stage[eIndex] = 4
endfunction

function AoP_Periodic takes nothing returns nothing
    local real x
    local real y
    local real z
    local integer i
    local integer index = 0
    
    loop
        set index = udg_AoP_Next[index]
        exitwhen index == 0
        
        if udg_AoP_Stage[index] == 1 then
            if AoP_IsUnitChanneling(udg_AoP_Caster[index]) then
                if udg_AoP_Dist2[index] < udg_AoP_Dist[index] then
                    set udg_AoP_Dist2[index] = udg_AoP_Dist2[index] + udg_AoP_Speed[index]
                    set udg_AoP_Dist[0] = udg_AoP_Dist2[index] - udg_AoP_Dist[index] // Opposite to angle
                    set x = udg_AoP_TargetX[index] + udg_AoP_Dist[0] * Cos(udg_AoP_Angle[index])
                    set y = udg_AoP_TargetY[index] + udg_AoP_Dist[0] * Sin(udg_AoP_Angle[index])
                    set z = AoP_GetParabolaHeight(udg_AoP_SourceZ[index], udg_AoP_TargetZ[index], udg_AoP_MaxHeight[index], udg_AoP_Dist[index], udg_AoP_Dist2[index])
                    call SetUnitX(udg_AoP_Missile[index], x)
                    call SetUnitY(udg_AoP_Missile[index], y)
                    call AoP_SetUnitZAlt(udg_AoP_Missile[index], z, x, y)
                else
                    call DestroyEffect(udg_AoP_AttachedModel[index])
                    call KillUnit(udg_AoP_Missile[index])
                    
                    call AoP_StartSummoningEffect(udg_AoP_TargetX[index], udg_AoP_TargetY[index], udg_AoP_Level[index])
                    
                    set udg_AoP_SummoningEffect[index] = CreateUnit(udg_AoP_Owner[index], udg_AoP_Dummy, udg_AoP_TargetX[index], udg_AoP_TargetY[index], 0)
                    call AoP_SetUnitPosition(udg_AoP_SummoningEffect[index], udg_AoP_TargetX[index], udg_AoP_TargetY[index])
                    call AoP_UnitApplyFly(udg_AoP_SummoningEffect[index])
                    call SetUnitFlyHeight(udg_AoP_SummoningEffect[index], udg_AoP_sSFXHeight, 0)
                    call SetUnitScale(udg_AoP_SummoningEffect[index], 0, 0, 0)
                    set udg_AoP_AttachedModel[index] = AddSpecialEffectTarget(udg_AoP_SummoningSFX, udg_AoP_SummoningEffect[index], "origin")
                    
                    set udg_AoP_Dist[index] = 0
                    
                    set udg_AoP_Stage[index] = 2
                endif
            else
                // --- When caster stopped channeling
                call DestroyEffect(udg_AoP_AttachedModel[index])
                call KillUnit(udg_AoP_Missile[index])
                call AoP_CreateParticleTarget(udg_AoP_mOrbCancelSFX, udg_AoP_Missile[index], udg_AoP_mOrbCancelSFXSize, udg_AoP_mOrbCancelSFXDeath)
                call AoP_Destroy(index)
            endif
        elseif udg_AoP_Stage[index] == 2 then
            if AoP_IsUnitChanneling(udg_AoP_Caster[index]) then
                if udg_AoP_Dist[index] < udg_AoP_sSFXFinalSize[udg_AoP_Level[index]] then
                    
                else
                    
                endif
            else
                // --- When caster stopped channeling
                call AoP_Destroy(index)
            endif
        elseif udg_AoP_Stage[index] == 4 then
            if udg_AoP_Dist[index] < udg_AoP_sGroundSFXArea then
                set udg_AoP_Dist[index] = udg_AoP_Dist[index] + udg_AoP_sGroundSFXSpeed
                set udg_AoP_realTimer[index] = udg_AoP_realTimer[index] + AoP_FrameUpdate()
                if udg_AoP_realTimer[index] >= udg_AoP_sGroundSFXInterval then
                    set udg_AoP_Angle[0] = GetRandomReal(-bj_PI, bj_PI)
                    set i = R2I(4*udg_AoP_sGroundSFXDensity)
                    set udg_AoP_Dist[0] = 360 / i
                    loop
                        exitwhen i <= 0
                        
                        set udg_AoP_Angle[0] = udg_AoP_Angle[0] + udg_AoP_Dist[0]
                        set x = udg_AoP_TargetX[index] + udg_AoP_Dist[index] * Cos(udg_AoP_Angle[0])
                        set y = udg_AoP_TargetY[index] + udg_AoP_Dist[index] * Sin(udg_AoP_Angle[0])
                        call DestroyEffect(AddSpecialEffect(udg_AoP_sGroundSFX, x, y))
                        
                        set i = i - 1
                    endloop
                    
                    set udg_AoP_realTimer[index] = 0
                endif
            else
                call AoP_Destroy(index)
            endif
        elseif udg_AoP_Stage[index] == 5 then
            set udg_AoP_realTimer[index] = udg_AoP_realTimer[index] - AoP_FrameUpdate()
            if udg_AoP_realTimer[index] <= 0 then
                call DestroyEffect(udg_AoP_AttachedModel[index])
                call KillUnit(udg_AoP_Particle[index])
                call AoP_Destroy(index)
            endif
        endif
    endloop
endfunction

function AoP_onEffect takes nothing returns boolean
    local real sx
    local real sy
    local real stx
    local real sty
    local integer cIndex
    
    if GetSpellAbilityId() == udg_AoP_Ability then
        set cIndex = AoP_Create()
        
        set udg_AoP_Caster[cIndex] = GetTriggerUnit()
        set udg_AoP_Owner[cIndex] = GetTriggerPlayer()
        set udg_AoP_Level[cIndex] = GetUnitAbilityLevel(udg_AoP_Caster[cIndex], udg_AoP_Ability)
        
        set sx = GetUnitX(udg_AoP_Caster[cIndex])
        set sy = GetUnitY(udg_AoP_Caster[cIndex])
        set udg_AoP_SourceZ[cIndex] = AoP_GetTerrainZ(sx, sy) + udg_AoP_mOrbSpawnHeight
        
        set udg_AoP_TargetX[cIndex] = GetSpellTargetX()
        set udg_AoP_TargetY[cIndex] = GetSpellTargetY()
        set udg_AoP_TargetZ[cIndex] = AoP_GetTerrainZ(udg_AoP_TargetX[cIndex], udg_AoP_TargetY[cIndex])
        
        set stx = udg_AoP_TargetX[cIndex] - sx
        set sty = udg_AoP_TargetY[cIndex] - sy
        set udg_AoP_Angle[cIndex] = AoP_GetAngle(stx, sty)
        
        set udg_AoP_TargetX[0] = sx + udg_AoP_mOrbSpawnOffset * Cos(udg_AoP_Angle[cIndex])
        set udg_AoP_TargetY[0] = sy + udg_AoP_mOrbSpawnOffset * Sin(udg_AoP_Angle[cIndex])
        set udg_AoP_Dist[cIndex] = AoP_GetMagnitude2D(stx, sty) - udg_AoP_mOrbSpawnOffset
        set udg_AoP_Dist2[cIndex] = 0
        set udg_AoP_Speed[cIndex] = udg_AoP_Dist[cIndex] / udg_AoP_mOrbTravelTime
        set udg_AoP_MaxHeight[cIndex] = udg_AoP_Dist[cIndex] * udg_AoP_mOrbArc + udg_AoP_TargetZ[cIndex]
        
        set udg_AoP_Missile[cIndex] = CreateUnit(AoP_DummiesOwner(), udg_AoP_Dummy, udg_AoP_TargetX[0], udg_AoP_TargetY[0], udg_AoP_Angle[cIndex]*bj_RADTODEG)
        call AoP_UnitApplyFly(udg_AoP_Missile[cIndex])
        call AoP_SetUnitZAlt(udg_AoP_Missile[cIndex], udg_AoP_SourceZ[cIndex], udg_AoP_TargetX[0], udg_AoP_TargetY[0])
        call SetUnitScale(udg_AoP_Missile[cIndex], udg_AoP_mOrbSize, 0, 0)
        set udg_AoP_AttachedModel[cIndex] = AddSpecialEffectTarget(udg_AoP_ManaOrbModel, udg_AoP_Missile[cIndex], "origin")
        
        set udg_AoP_Stage[cIndex] = 1
        
        if udg_AoP_Prev[cIndex] == 0 then
            call TimerStart(udg_AoP_Timer, AoP_FrameUpdate(), true, function AoP_Periodic)
        endif
    endif
    
    return false
endfunction

function AoP_Initialize takes nothing returns nothing
    local integer i = 0
    
    loop
        set i = i + 1
        exitwhen i > udg_AoP_AbilityLevels
        
        
    endloop
    
    set udg_AoP_mOrbTravelTime = udg_AoP_mOrbTravelTime / AoP_FrameUpdate()
    set udg_AoP_sGroundSFXSpeed = udg_AoP_sGroundSFXSpeed * AoP_FrameUpdate()
    
    if udg_AoP_ZLocator == null then
        set udg_AoP_ZLocator = Location(0, 0)
    endif
endfunction

//===========================================================================
function InitTrig_Ancient_of_Power takes nothing returns nothing
    set gg_trg_Ancient_of_Power = CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Ancient_of_Power, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(gg_trg_Ancient_of_Power, Condition(function AoP_onEffect))
endfunction

Update:
Since there aren't much different with the first WIP, I'll just update this.
Updated.

I'll post visual WIP later.
 
Last edited:
One question though - MUST I summon a unit under any circumstances when casting the ability or is it ok if there are additional conditions to be met?

From contest:
Create a spell that summons one or many units in a unique way.
So, yes, the summoning must have clear relation to the spell.
Though the exact circumstances for summoning and/or the process must be defined by you.

I'd like some visual updates as well!
Yeah, I agree. I personaly also prefer seeing concept/ideas/thoughts or maybe a pic over having pure code as WIP. :)
 
I think I'm going to throw away my previous idea :V

It was just a bunch of complex events in a dull creature.

will be posting a new wip soon

[edit]

Here's my new Spell:

Summon Devourer

Target point. If there are 5/4/3 trees within 256 radius of the target point, a portal that lasts for 5/4/3 seconds will spawn. Once fully developed, it summons a vile creature that feeds on energies, both from life and mana.

Units who dies within 800 radius of the Devourer will drop their souls. The devourer can consume the souls if he goes within 600/700/800 radius from the souls, increasing it's timed life by 1/2/3 seconds.

Units who loses mana within 1000 radius of the Devourer will cause the devourer to consume the mana lost. Once the Devourer consumes 400/350/300 mana, the Devourer releases a malevolent shockwave that deals 256 damage on nearby enemy units.

The Devourer can also release a swarm on his direction , that travels 768 distance and deals 75/150/225 damage, whenever he attacks. Has a cooldown of 10/8/6 seconds.

All details are not final



JASS:
library Devourer
    globals
        private constant real TIMEOUT = 0.03125
        
        private constant integer ABIL_ID = 0
        
        private constant integer REQUIRED_TREES = 6
        private constant integer REQUIRED_TREES_PER_LVL = -1
        
        private constant real TREE_RADIUS_CHECK = 256
        private constant real TREE_RADIUS_CHECK_PER_LVL = 0
        
        private constant real SPAWN_DELAY = 6
        private constant real SPAWN_DELAY_PER_LVL = -1
        
        private constant integer BROODMOTHER_1 = 0
        private constant integer BROODMOTHER_2 = 0
        private constant integer BROODMOTHER_3 = 0
        
        private constant real TIMED_LIFE = 10
        private constant real TIMED_LIFE_PER_LVL = 10
        
        private constant real SOUL_DROP_RADIUS = 800
        private constant real SOUL_DROP_RADIUS_PER_LVL = 0
        
        private constant real SOUL_DROP_DURATION = 10
        private constant real SOUL_DROP_DURATION_PER_LVL = 0
        
        private constant real SOUL_FEED_RADIUS = 500
        private constant real SOUL_FEED_RADIUS_PER_LVL = 100
        
        private constant real TIMED_LIFE_BONUS = 0
        private constant real TIMED_LIFE_BONUS_PER_LVL = 1
        
        private constant real MANA_FEED_RADIUS = 1000
        private constant real MANA_FEED_RADIUS = 0
        
        private constant real MANA_RELEASE_REQ = 450
        private constant real MANA_RELEASE_REQ_PER_LVL = -50
        
        private constant real MANA_RELEASE_DMG = 256
        private constant real MANA_RELEASE_DMG_PER_LVL = 0
        
        private constant attacktype MANA_RELEASE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype MANA_RELEASE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        
        private constant real MANA_RELEASE_CD = 0
        private constant real MANA_RELEASE_CD_PER_LVL = 0
        
        private constant boolean COLLECT_MANA_ON_CD = false
        
        private constant real SWARM_SPAWN_OFFSET = 128
        private constant real SWARM_SPAWN_OFFSET_PER_LVL = 0
        
        private constant real SWARM_TRAVEL_DIST = 768
        private constant real SWARM_TRAVEL_DIST_PER_LVL = 0
        
        private constant real SWARM_RADIUS = 128
        private constant real SWARM_RADIUS_PER_LVL = 0
        
        private constant real SWARM_DAMAGE = 0
        private constant real SWARM_DAMAGE_PER_LVL = 75
        
        private constant attacktype SWARM_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype SWARM_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        
        private constant real SWARM_CD = 12
        private constant real SWARM_CD_PER_LVL = -2
        
        
    endglobals
endlibrary
 
Last edited:

Ardenian

A

Ardenian

It depends on what you would like to re-scale.

Mesh, geosets, you can re-scale in the Model Editor, under Windows->Model Editor.

To re-scale particle emitters, you go to Windows->Node Manager-> select a particle emitter you would like to re-scale, then right-click and select Edit Node.
Now there should be a field for size somewhere. Blue particle emitters have three ones, being considered one after each other after spawning particles.
 
Level 17
Joined
Sep 8, 2007
Messages
994
Still not finished, but I'm making progress. I got most of the basic compontents done, several issues still need to be fixed, though. However, I can put them together already
so you can get the idea of my concept.

Here's another WIP:
xxdingo93xxZephyrChallange.jpg
 
Level 20
Joined
Apr 14, 2012
Messages
2,901
Requiem

NEW CONCEPT:

[BEASTMODE] - Passive - 5 Levels

Increases the Hunter's primal instincts. Critical chance is increased. When the Hunter strikes an enemy unit, that enemy gets wounded and causes the Hunter's attacks to get more powerful over time. When the enemy unit dies under this wound, bonus experience is granted to the Hunter.

Whenever the Hunter strikes a critical hit, the Hunter gains a temporary boost in Str. Whenever the Hunter kills an enemy unit, it heightens the senses of the hunter and temporarily increases movement speed. Whenever the hunter is dealt a percentage of his life, he learns from his weakness and temprarily increases evasion.

If the Hunter would gain bonus experience at the maximum level, instead that experience is stored. When the threshold is reached, the Hunter summons a primordial overlord from the earth, born from nature, avenger of the wild. The overlord lasts 300 seconds. When it dies, it spawns an Elder Hydra.

Level 1 - Critical Chance - 15%
- Wound Bonus Damage - 5 (per stack)
- Bonus Experience - 10%
- Critical Strength Boost - +3 (for 10 seconds)
- Killer Instinct Movement Speed - +50 (for 10 seconds)
- Critical Damage Evasion Boost - 10% (for 10 seconds)

Level 2 - Critical Chance - 25%
- Wound Bonus Damage - 10 (per stack)
- Bonus Experience - 20%
- Critical Strength Boost - +7 (for 10 seconds)
- Killer Instinct Movement Speed - +100 (for 10 seconds)
- Critical Damage Evasion Boost - 20% (for 10 seconds)

Level 3 - Critical Chance - 35%
- Wound Bonus Damage - 15 (per stack)
- Bonus Experience - 30%
- Critical Strength Boost - +15 (for 10 seconds)
- Killer Instinct Movement Speed - +150 (for 10 seconds)
- Critical Damage Evasion Boost - 30% (for 10 seconds)

Level 4 - Critical Chance - 45%
- Wound Bonus Damage - 20 (per stack)
- Bonus Experience - 40%
- Critical Strength Boost - +20 (for 10 seconds)
- Killer Instinct Movement Speed - +200 (for 10 seconds)
- Critical Damage Evasion Boost - 40% (for 10 seconds)

Level 5 - Critical Chance - 55%
- Wound Bonus Damage - 30 (per stack)
- Bonus Experience - 50%
- Critical Strength Boost - +25 (for 10 seconds)
- Killer Instinct Movement Speed - +300 (for 10 seconds)
- Critical Damage Evasion Boost - 50% (for 10 seconds)

[Overlord] - Lots of Hp, lots of Armor

Skill:
[active] Judgment - The overlord invokes the survival of the fittest, judging those who are within range who will live and who will die. Units within 800 of the overlord that have a lower level than the overlord, that are below 50% hp, and have a slow movement speed (<150) are damaged greatly and knocked back.

[passive] Primal Pressure - The overlord's ancient powers render nearby enemy units weak. Slows nearby enemy units.

[active] Dictate of the Ancient One - The overlord's ancient powers break through the barriers of time, summoning one of three ancient units:

Ancient of Stone - Avatar of endurance. Possesses great health points and can taunt enemy units. Lasts 30 seconds. (Summoned 50% of the time)
Ancient Turtle - Avatar of recovery. - Grants immense health and mana regeneration for nearby friendly units. Lasts 30 seconds (Summoned 40% of the time)
Ancient of Time - Avatar of the Infinite. Can cast Defense of the Ancients. (Summoned 10% of the time)
----Defense of the Ancients - Targets Heroes - May target the Hunter - resets the targeted hero's ability to 1 (if already level 1, if the target is not the Hunter, then kill the target) and for each level taken, kill all enemy units that are 100*levels_taken from the ancient of time. After casting, the Ancient disappears. Lasts 30 seconds.

-----------------------------------------------------------
I hope I make it.

WIP's soon to come!
 
Last edited:
@Master Trainer - When you said you're doing a "binary duo thing" some of us assumed you meant Two units that are summoned and relied on each other, not two abilities;

As far as I'm aware you're allowed abilities on your summoned units (given that normal summons in game have abilities) but the theme states to create a spell
IcemanBo said:
It's defined: "Create a spell", which means one spell.
Given that information you can do everything you want of course and try to argue.
Though, the judgement might share or might not share your thoughts of concept.
Judges haven't weighed-in on this matter though, but I think one "Main" ability (ability on your hero) is fair since allowing multiple intrinsically allows you to do much more complicated things
 
Level 20
Joined
Apr 14, 2012
Messages
2,901
...

Judges haven't weighed-in on this matter though, but I think one "Main" ability (ability on your hero) is fair since allowing multiple intrinsically allows you to do much more complicated things

So technically- if I had one "Main" ability on the hero, sub-abilities should be on the summoned units, and not on the hero itself? I mean, couldn't spell one and spell two be treated like, err, lock and key? :\

...


Judges haven't weighed-in on this matter though
 
Mind to elaborate how the result will look?
1) Do you want to have a combination like defend/undefend?
2) Do you to want to trigger multiple spells that the hero will have at same time.

If it's 1), I could see it being argued. For example if the removal of one has an critical effect on your spell concept. Like defend/undefend.
But otherwise: simple switching between spells to define which unit to summon next, would be no good idea if you ask me.

If it's 2), then spontaneously I would not recommend to follow such a concept. It might be risky it won't count as "one spell", which is defined in the contest template.
 
Level 20
Joined
Apr 14, 2012
Messages
2,901
Mind to elaborate how the result will look?
1) Do you want to have a combination like defend/undefend?
2) Do you to want to trigger multiple spells that the hero will have at same time.

If it's 1), I could see it being argued. For example if the removal of one has an critical effect on your spell concept. Like defend/undefend.
But otherwise: simple switching between spells to define which unit to summon next, would be no good idea if you ask me.

If it's 2), then spontaneously I would not recommend to follow such a concept. It might be risky it won't count as "one spell", which is defined in the contest template.

The spells would have a critical combination with each other, so I guess like defend/undefend. But now that light's really been shed, I'll tweak the concept down to a single, passive, spell.

With the deadline in just a few weeks, you could say I'll have to beastmode this spell. Though I fear in this rush I'll just come up with a beast of a result, messy but it'd probably work.

EDIT:
Regarding this rule:
You can use any enhancing system (xe, IsDestructableTree, Damage detection systems, etc.), as long as the judge(s) and/or host give(s) consent to its usage.

Now when you guys say 'system'... is it meant in the literal sense that there can only be one enhancing system in the map or...? Also, when does a judge/host give consent to the usage of a system?
 
Level 20
Joined
Apr 14, 2012
Messages
2,901
it doesn't mean that it is stricted to system, it states that you can use any enchancing system, as long as the judges/host permitted you to do so.

Oh thank christ.

The vast majority of the contest time is still left (been 1 week out of 4 thus far) - you'd hardly have to rush - especially since you can see a few of us have finished within the first week and we weren't rushing XD

^^ well the few of you that did finish within the first week are archmagi when it comes to spell making. Life's different for a simple adept such as mwah.
 
Level 11
Joined
Dec 19, 2012
Messages
411
WIP #3

224529-albums8862-picture108240.gif


After a few days, spell finally made into such situation.

Length of code is currently 1083 lines. Code :
JASS:
scope ElementalChaos initializer Init
//================================================================================
//============================Configurable Part===================================
//================================================================================
	globals
		/*
			GENERAL Configuration
		*/
		
		//The spell id of the spell
		private	constant integer				SPELL_ID 								= 'A000'
		
		//Dummy unit id
		private constant integer				DUMMY_UNIT_ID							= 'h000'
		
		//Interval time between energy packs to release the next energy pack
		private constant real					SPHERE_PACK_RELEASE_INTERVAL			= 0.5
		
		//Timer updating time constant
		private constant real		 			TIMER_TIMEOUT							= 0.03125
		
		//How long does the dummy unit get removed so that the attached effect could be fully played
		private constant real					DUMMY_DEATH_TIME						= 2.
		
		//The attachement path of the effect on the dummy units
		private	constant string					ATTACHMENT_PATH							= "origin"
		
		//Attack type of the damage
		private constant attacktype				ATTACK_TYPE								= ATTACK_TYPE_NORMAL
		
		//Damage type of the damage
		private constant damagetype				DAMAGE_TYPE								= DAMAGE_TYPE_NORMAL
		
		//Weapon type of the damage
		private constant weapontype				WEAPON_TYPE								= WEAPON_TYPE_WHOKNOWS
		
		//Gravitational force determines how strong the verticle force that pulling the missile to the ground
		private constant real					GRAVITATIONAL_FORCE						= 150.
		/*
			SPHERE Configuration (the generating sphere at start)
		*/
		
		//Sphere effect model path
		private constant string					SPHERE_EFFECT_PATH						= "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
		
		//Color adjustment - red color component of sphere
		private	constant integer				SPHERE_COLOR_RED						= 70
		
		//Color adjustment - green color component of sphere
		private	constant integer				SPHERE_COLOR_GREEN						= 200
		
		//Color adjustment - blue color component of sphere
		private constant integer				SPHERE_COLOR_BLUE						= 255
		
		//Transparency adjustment - transparency of sphere
		private	constant integer				SPHERE_TRANSPARENCY						= 255
		
		//Initial scale of the sphere before generating start
		private	constant real					SPHERE_INITIAL_SCALE					= 0.
		
		/*
				SPHERE_PARTICLE Configuration (those little particles moving toward the sphere)
		*/
		
		//Sphere particle effect stop spawn timing before sphere fully generated
		private constant real					SPHERE_PARTICLE_STOP_TIMING				= 1.
		
		//Sphere particle maximum scale
		private constant real					SPHERE_PARTICLE_MAX_SCALE				= 2.5
		
		//Sphere particle minimum scale
		private constant real					SPHERE_PARTICLE_MIN_SCALE				= 1.0
		
		//Sphere particle maximum distance from sphere
		private constant real					SPHERE_PARTICLE_MAX_DIST				= 300.
		
		//Sphere particle minimum distance from sphere
		private constant real					SPHERE_PARTICLE_MIN_DIST				= 200.
		
		//Sphere particle maximum height distance from shpere
		private constant real					SPHERE_PARTICLE_MAX_HEIGHT				= 200.
		
		//Sphere particle minimum height distance from sphere
		private constant real					SPHERE_PARTICLE_MIN_HEIGHT				= 100.
		
		//Sphere particle maximum speed moving toward sphere (horizontal)
		//(verticle) speed is dependent on the (horizontal) speed and height (distance) between particle and sphere
		private constant real					SPHERE_PARTICLE_MAX_SPEED				= 32.
		
		//Sphere particle minimum speed moving toward sphere (horizontal)
		private constant real					SPHERE_PARTICLE_MIN_SPEED				= 16.
		
		//Sphere particle dissapear (remove) distance between sphere and sphere particle
		private constant real 					SPHERE_PARTICLE_END_DIST				= 25.

		//Color adjustment - red color component of sphere particle
		private constant integer				SPHERE_PARTICLE_COLOR_RED				= 255
		
		//Color adjustment - green color component of sphere particle
		private constant integer				SPHERE_PARTICLE_COLOR_GREEN				= 255
		
		//Color adjustment - blue color component of sphere particle
		private constant integer				SPHERE_PARTICLE_COLOR_BLUE				= 255
		
		//Transparency adjustment - transparency of sphere particle
		private constant integer				SPHERE_PARTICLE_TRANSPARENCY			= 255
		
		//Sphere particle effect model path
		private constant string					SPHERE_PARTICLE_EFFECT_PATH				= "war3mapImported\\Stars_lightning_.MDX"
		
		/*
				SPHERE_ENERGY_PACK Configuraration (The sphere/orb effect moves away from the sphere)
		*/
		
		//Minimum distance to traval from sphere
		private constant real 					SPHERE_ENERGY_PACK_MIN_DIST				= 150.
		
		//Maximum distance to traval from sphere
		private constant real					SPHERE_ENERGY_PACK_MAX_DIST				= 256.

		//Minimum height to travel from sphere
		private constant real					SPHERE_ENERGY_PACK_MIN_HEIGHT			= 100.
		
		//Maximum height to travel from sphere
		private constant real					SPHERE_ENERGY_PACK_MAX_HEIGHT			= 200.
		
		//Minimum speed when travelling
		private constant real					SPHERE_ENERGY_PACK_MIN_SPEED			= 2.
		
		//Maximum speed when travelling
		private constant real					SPHERE_ENERGY_PACK_MAX_SPEED			= 4.
		
		//Minimum scale for the effect (unit)
		private constant real					SPHERE_ENERGY_PACK_MIN_SCALE			= .5
		
		//Maximum scale for the effect (unit)
		private constant real					SPHERE_ENERGY_PACK_MAX_SCALE			= 1.
		
		//Color adjustment - red color component of sphere energy pack
		private constant integer				SPHERE_ENERGY_PACK_COLOR_RED			= 70
		
		//Color adjustment - green color component of sphere energy pack
		private constant integer				SPHERE_ENERGY_PACK_COLOR_GREEN			= 200
		
		//Color adjustment - blue color component of sphere energy pack
		private constant integer				SPHERE_ENERGY_PACK_COLOR_BLUE			= 255
		
		//Transparency adjustment - transparency of sphere energy pack
		private constant integer				SPHERE_ENERGY_PACK_TRANSPARENCY			= 255
		
		//Sphere energy pack effect path
		private constant string					SPHERE_ENERGY_PACK_EFFECT_PATH			= "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
		
		//minimum time before energy pack could explode
		private constant real					SPHERE_ENERGY_PACK_EXPLODE_MIN_TIME		= 5.
		
		//maximum time for energy to explode
		private constant real					SPHERE_ENERGY_PACK_EXPLODE_MAX_TIME		= 6.
		
		//Scale of the explode effect (unit)
		private constant real					SPHERE_ENERGY_PACK_EXPLODE_SCALE		= 0.5
		
		//Color adjustment - red color component of sphere energy pack explode effect
		private constant integer				SPHERE_ENERGY_PACK_EXPLODE_COLOR_RED	= 50
		
		//Color adjustment - green color component of sphere energy pack explode effect
		private constant integer				SPHERE_ENERGY_PACK_EXPLODE_COLOR_GREEN	= 50
		
		//Color adjustment - blue color component of sphere energy pack explode effect
		private constant integer				SPHERE_ENERGY_PACK_EXPLODE_COLOR_BLUE	= 255
		
		//Transparency adjustment - transparency of sphere energy pack explode effect
		private constant integer				SPHERE_ENERGY_PACK_EXPLODE_TRANSPARENCY	= 255
		
		//exploe effect of the energy pack
		private constant string					SPHERE_ENERGY_PACK_EXPLODE_PATH			= "Units\\NightElf\\Wisp\\WispExplode.mdl"
		
		/*
				SPHERE_MISSILE Configuration (missile that shoots out when energy pack exploded)
		*/
		
		//The minimum time taken for the missile to land at ground
		private constant real					SPHERE_MISSILE_MIN_HEIGHT				= 50.
		
		//The maximum time taken for the missile to land at ground
		private constant real					SPHERE_MISSILE_MAX_HEIGHT				= 150.
		
		//Scaling of the effect (unit)
		private constant real					SPHERE_MISSILE_SCALE					= 1.
		
		//Color adjustment - red color component of missile
		private constant integer				SPHERE_MISSILE_COLOR_RED				= 255
		
		//Color adjustment - green color component of missile
		private constant integer				SPHERE_MISSILE_COLOR_GREEN				= 255
		
		//Color adjustment - blue color component of missile
		private constant integer				SPHERE_MISSILE_COLOR_BLUE				= 255
		
		//Transparency adjustment - transparency of missile
		private constant integer				SPHERE_MISSILE_TRANSPARENCY				= 255
		
		//Missile effect attached to the unit
		private constant string					SPHERE_MISSILE_EFFECT_PATH				= "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
		
		//Scaling of the effect (unit) after landed
		private constant real					SPHERE_MISSILE_LANDED_SCALE				= 1.
		
		//Color adjustment - red color component of missile after landed
		private constant integer				SPHERE_MISSILE_LANDED_COLOR_RED			= 255
		
		//Color adjustment - green color component of missile after landed
		private constant integer				SPHERE_MISSILE_LANDED_COLOR_GREEN		= 255
		
		//Color adjustment - blue color component of missile after landed
		private constant integer				SPHERE_MISSILE_LANDED_COLOR_BLUE		= 255
		
		//Transparency adjustment - transparency of missile after landed
		private constant integer				SPHERE_MISSILE_LANDED_TRANSPARENCY		= 255
		
		//Missile effect attach to the unit after landed ground
		private constant string					SPHERE_MISSILE_LANDED_EFFECT_PATH		= "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
		
 		/*
				Level Data Configuration (data value will be affected according to spell lv)
				
				All of them are configurable at function Init
		*/
		
		//Sphere height 
		private			 real		 array		SPHERE_HEIGHT
		
		//Sphere maximum scale 
		private			 real		 array		SPHERE_MAX_SCALE
		
		//Sphere generates time 
		private			 real		 array		SPHERE_GENERATE_TIME
		
		//Sphere energy pack release amount 
		private			 integer	 array		SPHERE_RELEASE_PACK_MAX
		
		//Sphere missile minimum spawn 
		private			 integer	 array		SPHERE_ENERGY_PACK_MIN_MISSILE
		
		//Sphere missile maximum spawn 
		private			 integer	 array		SPHERE_ENERGY_PACK_MAX_MISSILE
		
		//Sphere missile minimum travelling distance from shpere 
		private			 real		 array		SPHERE_MISSILE_MIN_DIST
		
		//Sphere missile maximum travelling distance from sphere 
		private			 real		 array		SPHERE_MISSILE_MAX_DIST
		
		//The damage deal to enemy after missile landed on the ground 
		private			 real		 array		SPHERE_FIRST_DAMAGE
		
		//The AoE of the SPHERE_FIRST_DAMAGE 
		private			 real		 array		SPHERE_FIRST_DAMAGE_AOE
		
		//Sphere summons unit type after missile landed on the ground 
		private			 integer	 array		SPHERE_FIRST_SUMMON
		
		//SPHERE_FIRST_SUMMON time life 
		private			 real		 array		SPHERE_FIRST_SUMMONS_TIME_LIFE
		
		//SPHERE_FIRST_SUMMON scale 
		private			 real	 	 array		SPHERE_FIRST_SUMMONS_SCALE
		
		//Red color component of SPHERE_FIRST_SUMMON 
		private			 integer	 array		SPHERE_FIRST_SUMMONS_COLOR_RED
		
		//Green color component of SPHERE_FIRST_SUMMON 
		private			 integer	 array		SPHERE_FIRST_SUMMONS_COLOR_GREEN
		
		//Blue color component of SPHERE_FIRST_SUMMON 
		private			 integer	 array		SPHERE_FIRST_SUMMONS_COLOR_BLUE
		
		//Transparency of SPHERE_FIRST_SUMMON 
		private			 integer	 array		SPHERE_FIRST_SUMMONS_TRANSPARENCY
	endglobals
	
	private function Init takes nothing returns nothing
		//[1] indicates Lv1, [2] indicates Lv2 and [3] indicates Lv3
		//Arrays are used instead of globals for preventing overwhelming globals as well as unnessasary coding.
		
		//Setting sphere height
		set SPHERE_HEIGHT[1] = 300.
		set SPHERE_HEIGHT[2] = 300.
		set SPHERE_HEIGHT[3] = 300.
		
		//Setting sphere maximum scale
		set SPHERE_MAX_SCALE[1] = 2.0
		set SPHERE_MAX_SCALE[2] = 2.5
		set SPHERE_MAX_SCALE[3] = 2.5
		
		//Setting sphere generates time
		set SPHERE_GENERATE_TIME[1] = 3.0
		set SPHERE_GENERATE_TIME[2] = 3.0
		set SPHERE_GENERATE_TIME[3] = 3.0
		
		//The energy pack released from the sphere
		set SPHERE_RELEASE_PACK_MAX[1] = 3
		set SPHERE_RELEASE_PACK_MAX[2] = 4
		set SPHERE_RELEASE_PACK_MAX[3] = 5
		
		//Minimum missle could be spawn after energy pack explode
		set SPHERE_ENERGY_PACK_MIN_MISSILE[1] = 2
		set SPHERE_ENERGY_PACK_MIN_MISSILE[2] = 3
		set SPHERE_ENERGY_PACK_MIN_MISSILE[3] = 3
		
		//Maximum missile could be spawn after energy pack explode
		set SPHERE_ENERGY_PACK_MAX_MISSILE[1] = 3
		set SPHERE_ENERGY_PACK_MAX_MISSILE[2] = 4
		set SPHERE_ENERGY_PACK_MAX_MISSILE[3] = 5
		
		//Minimum travel distance from shpere (horizontal)
		set SPHERE_MISSILE_MIN_DIST[1] = 100.
		set SPHERE_MISSILE_MIN_DIST[2] = 100.
		set SPHERE_MISSILE_MIN_DIST[3] = 100.
		
		//Maximum travel distance from sphere (horizontal)
		//Usually should cap at AoE of the spell
		set SPHERE_MISSILE_MAX_DIST[1] = 600.
		set SPHERE_MISSILE_MAX_DIST[2] = 600.
		set SPHERE_MISSILE_MAX_DIST[3] = 600.
		
		//Setting the damage amount after the missile landed the ground
		set SPHERE_FIRST_DAMAGE[1] = 10.
		set SPHERE_FIRST_DAMAGE[2] = 20.
		set SPHERE_FIRST_DAMAGE[3] = 30.
		
		//Setting the AoE of the SPHERE_FIRST_DAMAGE
		set SPHERE_FIRST_DAMAGE_AOE[1] = 128.
		set SPHERE_FIRST_DAMAGE_AOE[2] = 160.
		set SPHERE_FIRST_DAMAGE_AOE[3] = 192.
		
		//Setting the unit-id of the first summons type after missile landed
		//Using : Water Elemental (war3 default unit)
		set SPHERE_FIRST_SUMMON[1] = 'hwat'
		set SPHERE_FIRST_SUMMON[2] = 'hwt2'
		set SPHERE_FIRST_SUMMON[3] = 'hwt3'
		
		//Scaling of the summons unit
		set SPHERE_FIRST_SUMMONS_SCALE[1] = 0.6
		set SPHERE_FIRST_SUMMONS_SCALE[2] = 0.6
		set SPHERE_FIRST_SUMMONS_SCALE[3] = 0.6
		
		//Color adjustment - red color componenet of summons
		set SPHERE_FIRST_SUMMONS_COLOR_RED[1] = 255
		set SPHERE_FIRST_SUMMONS_COLOR_RED[2] = 255
		set SPHERE_FIRST_SUMMONS_COLOR_RED[2] = 255
		
		//Color adjustment - green color componenet of summons
		set SPHERE_FIRST_SUMMONS_COLOR_GREEN[1] = 255
		set SPHERE_FIRST_SUMMONS_COLOR_GREEN[2] = 255
		set SPHERE_FIRST_SUMMONS_COLOR_GREEN[3] = 255
		
		//Color adjustment - blue color componenet of summons
		set SPHERE_FIRST_SUMMONS_COLOR_BLUE[1] = 255
		set SPHERE_FIRST_SUMMONS_COLOR_BLUE[2] = 255
		set SPHERE_FIRST_SUMMONS_COLOR_BLUE[3] = 255
		
		//Transparency adjustment - transparency of the summons
		set SPHERE_FIRST_SUMMONS_TRANSPARENCY[1] = 255
		set SPHERE_FIRST_SUMMONS_TRANSPARENCY[2] = 255
		set SPHERE_FIRST_SUMMONS_TRANSPARENCY[3] = 255
		
		//Apply the time life for the SPHERE_FIRST_SUMMON unit type
		set SPHERE_FIRST_SUMMONS_TIME_LIFE[1] = 10.
		set SPHERE_FIRST_SUMMONS_TIME_LIFE[2] = 10.
		set SPHERE_FIRST_SUMMONS_TIME_LIFE[3] = 10.
	endfunction
	
	//Unit filter before damaging
	native UnitAlive takes unit id returns boolean
	
	private function EC_UnitFilter takes player p, unit u returns boolean
		return IsUnitEnemy(u, p) and UnitAlive(u)
	endfunction
//================================================================================
//==========================End Configurable Part=================================
//================================================================================





//================================================================================
//===========================Non-Configurable Part================================
//================================================================================
	globals
		//Sphere's phases
		private	constant integer			SPHERE_GENERATING_PHASE 		= 0
		
		private constant integer			SPHERE_RELEASING_PHASE 			= 1
		
		private constant integer			SPHERE_AWAITING_PHASE			= 2
		
		private constant integer			SPHERE_LANDING_PHASE			= 3
		
		//A constant used by SphereMissile struct for increasing verticle velocity every TIMER_TIMEOUT
		private constant real				SPHERE_MISSILE_CONSTANT			= GRAVITATIONAL_FORCE*TIMER_TIMEOUT*TIMER_TIMEOUT
	endglobals
	
	private function UnitAddRemoveCrowForm takes unit u returns nothing
		if UnitAddAbility(u, 'Amrf') then
			call UnitRemoveAbility(u, 'Amrf')
		endif
	endfunction
//================================================================================
//=========================End Non-Configurable Part==============================
//================================================================================

	private keyword ElementalChaos

	//-----------------------------------------------------------------
	//-----------------------------------------------------------------
	//A linked list created by myself
	private module LinkedList
		static method create takes nothing returns thistype
			local thistype this = instanceCount + 1
			set instanceCount = this
			
			if thistype(0).next == 0 then
				set thistype(0).next = this
				set lastNode = this
			else
				set lastNode.next = this
				set this.prev = lastNode
				
				set lastNode = this
			endif
			
			set this.next = 0
			set thistype(0).prev = this
			
			return this
		endmethod
		
		method destroy takes nothing returns nothing
			set this.prev.next = this.next
			set this.next.prev = this.prev
			
			if this.next == 0 and this.prev == 0 then
				set instanceCount = 0
				
				//call instanceClear if exists
				static if thistype.instanceClear.exists then
					call thistype.instanceClear()
				endif
			elseif lastNode == this then
				set lastNode = this.prev
			endif
			
		endmethod
	endmodule
	//-----------------------------------------------------------------
	//-----------------------------------------------------------------
	
	
	
	private struct SphereMissile extends array
		//==========Sphere Side Effect Globals============
					static  timer			timerSM				= CreateTimer()
		private		static	group			g					= CreateGroup()
					
							player			owner			//Owner of the missile
							
							unit			dummy
							
							effect			e
							
							integer			spellLv
							
							real			x
							real			y
							real			speedX			//speed value travelling along x-axis
							real			speedY			//speed value travelling along y-axis
							real			speedv
							real			height
							
							ElementalChaos	ec
		//================================================
		
		//============Linked List Globals=================
		
		//spell's instance count
		private 	static  thistype 	instanceCount = 0
		
		//last allocated instance
		private 	static	thistype 	lastNode
		
		private 			thistype	next
		private 			thistype	prev
		//================================================
		
		private static method instanceClear takes nothing returns nothing
			call PauseTimer(timerSM)
		endmethod
		
		implement LinkedList
		
		static method onPeriodic takes nothing returns nothing
			local thistype this = thistype(0).next
			local unit u		= null
			
			loop
				exitwhen this == 0
				
				//Move the unit horizontally
				set this.x = this.x + this.speedX
				set this.y = this.y + this.speedY
				call SetUnitX(this.dummy, this.x)
				call SetUnitY(this.dummy, this.y)
				
				//Move the unit vertically
				//v = u + at
				//u = this.speedv , a = -GRAVITATIONAL_FORCE, t = TIMER_TIMEOUT
				set this.speedv = this.speedv + SPHERE_MISSILE_CONSTANT
				set this.height = this.height - this.speedv
				
				call SetUnitFlyHeight(this.dummy, this.height, 0.)
				
				//Check if missile reached ground
				if this.height <= 0 then
					//Destroy missile effect
					call DestroyEffect(this.e)
					
					//Damage the units caught in it
					call GroupEnumUnitsInRange(g, this.x, this.y, SPHERE_FIRST_DAMAGE_AOE[this.spellLv], null)
					
					loop
						set u = FirstOfGroup(g)
						exitwhen u == null
						
						if EC_UnitFilter(this.owner, u) then
							call UnitDamageTarget(this.dummy, u, SPHERE_FIRST_DAMAGE[this.spellLv], true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
						endif
						
						call GroupRemoveUnit(g, u)
					endloop
					
					//Spawn the summons out
					set u = CreateUnit(this.owner, SPHERE_FIRST_SUMMON[this.spellLv], this.x, this.y, GetRandomReal(0, 360))
					
					//Add the summons to unit group
					call GroupAddUnit(ec.unitGroup, u)
					
					//Apply time life (Summons)
					call UnitApplyTimedLife(u, 'BTLF', SPHERE_FIRST_SUMMONS_TIME_LIFE[this.spellLv])
					
					//Modify unit scale (Summons)
					call SetUnitScale(u, SPHERE_FIRST_SUMMONS_SCALE[this.spellLv], SPHERE_FIRST_SUMMONS_SCALE[this.spellLv], SPHERE_FIRST_SUMMONS_SCALE[this.spellLv])
					
					//Modify unit color (Summons)
					call SetUnitVertexColor(u, SPHERE_FIRST_SUMMONS_COLOR_RED[this.spellLv], SPHERE_FIRST_SUMMONS_COLOR_GREEN[this.spellLv], SPHERE_FIRST_SUMMONS_COLOR_BLUE[this.spellLv], SPHERE_FIRST_SUMMONS_TRANSPARENCY[this.spellLv])
					
					//Modify unit scale (dummy)
					call SetUnitScale(this.dummy, SPHERE_MISSILE_LANDED_SCALE, SPHERE_MISSILE_LANDED_SCALE, SPHERE_MISSILE_LANDED_SCALE)
					
					//Modify unit color (dummy)
					call SetUnitVertexColor(this.dummy, SPHERE_MISSILE_LANDED_COLOR_RED, SPHERE_MISSILE_LANDED_COLOR_GREEN, SPHERE_MISSILE_LANDED_COLOR_BLUE, SPHERE_MISSILE_LANDED_TRANSPARENCY)
					
					//Add landed effect
					call DestroyEffect(AddSpecialEffectTarget(SPHERE_MISSILE_LANDED_EFFECT_PATH, this.dummy, ATTACHMENT_PATH))
					
					//Apply time life (dummy)
					call UnitApplyTimedLife(this.dummy, 'BTLF', DUMMY_DEATH_TIME)
					
					call this.destroy()
					
					set this.owner = null
					set this.dummy = null
					set this.e	   = null
				endif
				
				set this = this.next
			endloop
		endmethod
	endstruct
	
	private struct SphereEnergyPack extends array
		//==========Sphere Side Effect Globals============
					static	timer			timerSEP			= CreateTimer()
		
							player			owner
							
							unit			dummy
							
							effect			e 					//effect attached to dummy
							
							integer			missileCount		//Number of missile spawn after energy pack explode
							integer			spellLv
							
							real			x 					//y coordinate of energy pack
							real			y 					//x coordinate of energy pack
							real			height 				//height of energy pack from sphere
							real			speedh 				//speed horizontal (constant)
							real			speedv 				//speed verticle (non-constant)
							real			remainingTime 		//time taken for energy pack explode
							
							real			destinationX		//energy's pack destination x-coordinate
							real			destinationY		//energy's pack destination y-coordinate
							real			destinationDistance //distance between energy pack and destination of energy pack
							
							ElementalChaos	ec
		//================================================
	
		//============Linked List Globals=================
		
		//spell's instance count
		private 	static  thistype 	instanceCount = 0
		
		//last allocated instance
		private 	static	thistype 	lastNode
		
		private 			thistype	next
		private 			thistype	prev
		//================================================
		
		private static method instanceClear takes nothing returns nothing
			call PauseTimer(timerSEP)
		endmethod
		
		implement LinkedList
		
		static method onPeriodic takes nothing returns nothing
			local SphereMissile sm
			
			local thistype  	this  = thistype(0).next
			local integer		i
			local real			angle
			local real			r
			local real			time
			
			loop
				exitwhen this == 0
				
				set angle = Atan2(this.destinationY-GetUnitY(this.dummy), this.destinationX-GetUnitX(this.dummy))
				
				//move the energy pack towards destinationX/Y (horizontally)
				set this.destinationDistance = this.destinationDistance - this.speedh
				set this.x = this.x + this.speedh * Cos(angle)
				set this.y = this.y + this.speedh * Sin(angle)
				call SetUnitX(this.dummy, this.x)
				call SetUnitY(this.dummy, this.y)
				
				//move the energy pack towards destiantionHeight (vertically)
				set this.height = this.height + this.speedv
				call SetUnitFlyHeight(this.dummy, this.height, 0.)
				
				//Decrease the explode time by TIMER_TIMEOUT seconds
				set this.remainingTime = this.remainingTime - TIMER_TIMEOUT
				
				//Check if energy pack reaches its destination OR remainingTime <= 0
				if this.destinationDistance <= 0 or this.remainingTime <= 0 then
					//destroy the energy pack effect
					call DestroyEffect(this.e)
					
					//change the unit (effect) color
					call SetUnitVertexColor(this.dummy, SPHERE_ENERGY_PACK_EXPLODE_COLOR_RED, SPHERE_ENERGY_PACK_EXPLODE_COLOR_GREEN, SPHERE_ENERGY_PACK_EXPLODE_COLOR_BLUE, SPHERE_ENERGY_PACK_EXPLODE_TRANSPARENCY)
					
					//change the unit scale
					call SetUnitScale(this.dummy, SPHERE_ENERGY_PACK_EXPLODE_SCALE, SPHERE_ENERGY_PACK_EXPLODE_SCALE, SPHERE_ENERGY_PACK_EXPLODE_SCALE)
					
					//add the explode effect to the dummy
					call DestroyEffect(AddSpecialEffectTarget(SPHERE_ENERGY_PACK_EXPLODE_PATH, this.dummy, ATTACHMENT_PATH))
					
					//add life time to the dummy
					call UnitApplyTimedLife(this.dummy, 'BTLF', DUMMY_DEATH_TIME)
				
					set i = 0
					loop
						set i = i + 1
						
						//Create a new missile
						set sm = SphereMissile.create()
						
						//Setting up missile requires data
						set sm.height		 = GetRandomReal(SPHERE_MISSILE_MIN_HEIGHT, SPHERE_MISSILE_MAX_HEIGHT)
						set sm.spellLv		 = this.spellLv
						set sm.owner		 = this.owner
						set sm.ec			 = this.ec
						set sm.x			 = this.x
						set sm.y			 = this.y
						set sm.dummy		 = CreateUnit(this.owner, DUMMY_UNIT_ID, this.y, this.x, 0.)
						
						call SetUnitX(sm.dummy, this.x)
						call SetUnitY(sm.dummy, this.y)
						//Kinemtic formula is used : v*v = u*u + 2as
						//Check if height is positive
						if sm.height >= 0 then
							//Rearrange : u = SquareRoot(v*v - 2as)
							//a = -GRAVITATIONAL_FORCE (against gravitational force), v = 0
							//So : u = -1 * SquareRoot(2*GRAVITATIONAL_FORCE*s)
							set sm.speedv = -1 * SquareRoot(2*GRAVITATIONAL_FORCE*sm.height)
						else
							//a = GRAVITATIONAL_FORCE, u = 0
							set sm.speedv = 0.
						endif
						
						//Set the height to the height where it should be
						set sm.height		 = this.height + sm.height
						//To obtain the total time taken for the motion, t1 = upward motion (if present), t2 = downward motion
						//Calculation are in unit : PER SECOND
						//For t1 : use v = u + at
						//v = 0, u = sm.speedv, a = -GRAVITATIONAL_FORCE, t1 = ?
						//So : t1 = -u/a
						//For t2 : use s = ut + (1/2)at*t
						//s = sm.height (total height), u = 0, a = GRAVITATIONAL_FORCE, t2 = ?
						//So : t2 = SquareRoot(2s/a)
						//	   t2 = SquareRoot((2*sm.height)/GRAVITATIONAL_FORCE)
						set time			 = (-sm.speedv/GRAVITATIONAL_FORCE) + SquareRoot((2*sm.height)/GRAVITATIONAL_FORCE)
						//Covert sm.speedv's unit from PER SECOND to PER TIMER_TIMEOUT
						set sm.speedv		 = sm.speedv * TIMER_TIMEOUT
						//let r = distance
						//speedh = r/time
						//Cos(angle) = constant, Sin(value) = constant
						//so : (r/time) * Cos/Sin(angle) unit : PER SECOND
						//Convert PER SECOND to PER TIMER_TIMEOUT :
						// (r/time) * Cos/Sin(angle) * TIMER_TIMEOUT
						set r				 = GetRandomReal(SPHERE_MISSILE_MIN_DIST[this.spellLv], SPHERE_MISSILE_MAX_DIST[this.spellLv])
						set angle			 = GetRandomReal(-bj_PI, bj_PI)
						set sm.speedX		 = (r/time) * Cos(angle) * TIMER_TIMEOUT
						set sm.speedY		 = (r/time) * Sin(angle) * TIMER_TIMEOUT
						
						//Setting up dummy's data
						call SetUnitScale(sm.dummy, SPHERE_MISSILE_SCALE, SPHERE_MISSILE_SCALE, SPHERE_MISSILE_SCALE)
						
						call UnitAddRemoveCrowForm(sm.dummy)
						call SetUnitFlyHeight(sm.dummy, this.height, 0.)
						
						call SetUnitVertexColor(sm.dummy, SPHERE_MISSILE_COLOR_RED, SPHERE_MISSILE_COLOR_GREEN, SPHERE_MISSILE_COLOR_BLUE, SPHERE_MISSILE_TRANSPARENCY)
						
						set sm.e			 = AddSpecialEffectTarget(SPHERE_MISSILE_EFFECT_PATH, sm.dummy, ATTACHMENT_PATH)
						
						if sm == 1 then
							call TimerStart(SphereMissile.timerSM, TIMER_TIMEOUT, true, function SphereMissile.onPeriodic)
						endif
						
						exitwhen i == this.missileCount //exitwhen created missile == number of missile gonna be fired
					endloop
					
					call this.destroy()
					
					set this.owner = null
					set this.dummy = null
					set this.e	   = null
				endif
				
				set this = this.next
			endloop
		endmethod
	endstruct
	
	
	
	
	
	private struct SphereParticleEffect extends array
		//==========Sphere Side Effect Globals============
					static	timer		timerSPE			= CreateTimer()
		
							unit		dummy
							
							effect		e					 	//effect attached to dummy
							
							real		x 						//y coordinate of particle
							real		y 						//x coordinate of particle
							real		distance 				//distance between sphere and particle
							real		height					//height between sphere and particle
							real		speedh 					//speed horizontal (constant)
							real		speedv 					//speed verticle (constant)
							
							real		destinationHeight 		//sphere height
							real		destinationX			//sphere x coordinate
							real		destinationY 			//sphere y coordinate
		//================================================
		
		//============Linked List Globals=================
		
		//spell's instance count
		private 	static  thistype 	instanceCount = 0
		
		//last allocated instance
		private 	static	thistype 	lastNode
		
		private 			thistype	next
		private 			thistype	prev
		//================================================
		
		private static method instanceClear takes nothing returns nothing
			call PauseTimer(timerSPE)
		endmethod
		
		implement LinkedList
		
		static method onPeriodic takes nothing returns nothing
			local thistype  this  = thistype(0).next
			local real 		angle
			local real 		x2
			
			loop
				exitwhen this == 0
				
				if this.distance >= SPHERE_PARTICLE_END_DIST then
					set this.distance = this.distance - this.speedh
					
					set x2 = GetUnitX(this.dummy)-this.destinationX
					set angle = Atan2(GetUnitY(this.dummy)-this.destinationY, x2)
					
					//move the particle toward sphere (verticle)
					set this.height = this.height - this.speedv
					call SetUnitFlyHeight(this.dummy, this.destinationHeight + this.height * Cos(Atan2(this.destinationHeight-this.height,x2)), 0.)
					
					//move the particle toward sphere (horizontal)
					set this.x = this.x - this.speedh * Cos(angle)
					set this.y = this.y - this.speedh * Sin(angle)
					
					call SetUnitX(this.dummy, this.x)
					call SetUnitY(this.dummy, this.y)
					
				else
					call DestroyEffect(this.e)
					call RemoveUnit(this.dummy)
					
					call this.destroy()
					
					set this.e = null
					set this.dummy = null
				endif
				set this = this.next
			endloop
		endmethod
	endstruct
	
	
	
	
	
	private struct ElementalChaos extends array
		//============Elemental Chaos Globals=============
		private		static	timer		timerEC					= CreateTimer() //Elemental Chaos Timer
		
		private				unit		triggerUnit
		private				unit		sphereDummy
		
		private				effect		sphereEffect
		
		public				group		unitGroup
		
		private				player		owner
		
		private				integer		spellLv
		private				integer		phase //spell phase
		private				integer		releasedPack //amount of energy pack released from sphere
		
		private				real		targetX //spell target X
		private				real		targetY //spell target Y
		private				real		spellDuration
		private				real		sphereScale
		private				real		sphereScaleConst //sphere scale increasement constant every TIMER_TIMEOUT seconds
		private				real		packReleaseTimePassed //energy pack release interval, increases every TIMER_TIMEOUT seconds
		
		private				real		sphereGenerateTime
		private				real		sphereHeight
		//================================================
		
		
		//============Linked List Globals=================
		
		//spell's instance count
		private 	static  thistype 	instanceCount = 0
		
		//last allocated instance
		private 	static	thistype 	lastNode
		
		private 			thistype	next
		private 			thistype	prev
		//================================================
		//instanceClear will be called if no more instance is running
		private static method instanceClear takes nothing returns nothing
			call PauseTimer(timerEC)
		endmethod
		
		implement LinkedList
		
		private static method onPeriodic takes nothing returns nothing
			local SphereParticleEffect  particleEffect
			local SphereEnergyPack		energyPack
			
			local thistype  this = thistype(0).next
			local real		rand
			
			loop
				exitwhen this == 0
				
				set spellDuration = spellDuration + TIMER_TIMEOUT
				//phase 0 indicates sphere generating phase
				if this.phase == SPHERE_GENERATING_PHASE then
					//=================================================================================
					//Generate Effect of the sphere (the effect shows when sphere is generating)
					//=================================================================================
					//if (sphere generate time - passed spell timing) >= SPHERE_PARTICLE_STOP_TIMING
					if (this.sphereGenerateTime-this.spellDuration) >= SPHERE_PARTICLE_STOP_TIMING then
						set particleEffect = SphereParticleEffect.create()
						
						//Setting up particle data
						set particleEffect.destinationHeight	= this.sphereHeight
						set particleEffect.destinationX			= this.targetX
						set particleEffect.destinationY			= this.targetY
						set particleEffect.distance				= GetRandomReal(SPHERE_PARTICLE_MIN_DIST, SPHERE_PARTICLE_MAX_DIST)
						set particleEffect.height				= GetRandomReal(SPHERE_PARTICLE_MIN_HEIGHT, SPHERE_PARTICLE_MAX_HEIGHT)
						set particleEffect.speedh				= GetRandomReal(SPHERE_PARTICLE_MIN_SPEED, SPHERE_PARTICLE_MAX_SPEED)
						//speedv = height / time
						//time = ditance / speedh
						set particleEffect.speedv				= particleEffect.height/(particleEffect.distance/particleEffect.speedh)
						set particleEffect.x					= this.targetX + particleEffect.distance * Cos(GetRandomReal(-bj_PI, bj_PI))
						set particleEffect.y					= this.targetY + particleEffect.distance * Sin(GetRandomReal(-bj_PI, bj_PI))
						set particleEffect.dummy				= CreateUnit(this.owner, DUMMY_UNIT_ID, particleEffect.x, particleEffect.y, 0)
						set particleEffect.e					= AddSpecialEffectTarget(SPHERE_PARTICLE_EFFECT_PATH, particleEffect.dummy, ATTACHMENT_PATH)
						
						//Set unit scale randomly
						set rand = GetRandomReal(SPHERE_PARTICLE_MIN_SCALE, SPHERE_PARTICLE_MAX_SCALE)
						call SetUnitScale(particleEffect.dummy, rand, rand, rand)
						
						//add and remove crow form
						call UnitAddRemoveCrowForm(particleEffect.dummy)
						
						//rendomly obtain a number so that height can be appear lower or higher thn the sphere
						if GetRandomInt(0, 1) == 1 then
							set particleEffect.height = particleEffect.height * -1
						endif
						
						//set unit height
						call SetUnitFlyHeight(particleEffect.dummy, this.sphereHeight + particleEffect.height, 0)
					
						//adjust the color compoennt of the unit
						call SetUnitVertexColor(particleEffect.dummy, SPHERE_PARTICLE_COLOR_RED, SPHERE_PARTICLE_COLOR_GREEN, SPHERE_PARTICLE_COLOR_BLUE, SPHERE_PARTICLE_TRANSPARENCY)
						
						if particleEffect == 1 then
							call TimerStart(SphereParticleEffect.timerSPE, TIMER_TIMEOUT, true, function SphereParticleEffect.onPeriodic)
						endif
						
						//Setting sphere scale, reason it is inside the block is to prevent sphere increasing scale when no more particle effects
						//are generated.
						set this.sphereScale = this.sphereScale + this.sphereScaleConst
						call SetUnitScale(this.sphereDummy, this.sphereScale, this.sphereScale, this.sphereScale)
					endif
					//=================================================================================
					//===========================End Sphere Generate Effect============================
					//=================================================================================
					
					//Check if the spell duration >= sphere generate time
					if this.spellDuration >= this.sphereGenerateTime then
						//Sphere generate phase ends, switch spell phase to 1
						set this.phase = SPHERE_RELEASING_PHASE
					endif
				elseif this.phase == SPHERE_RELEASING_PHASE then
					//If released energy pack count < SPHERE_RELEASE_PACK_MAX[spellLv]
					if this.releasedPack < SPHERE_RELEASE_PACK_MAX[this.spellLv] then
					/************************************************************************************************************/
					/**/if this.packReleaseTimePassed >= SPHERE_PACK_RELEASE_INTERVAL then
					/**/	set this.packReleaseTimePassed  = 0.
					/**/	set this.releasedPack			= this.releasedPack + 1
					/**/
					/**/	set energyPack = SphereEnergyPack.create()
					/**/	
					/**/	//Setting up energy pack data
					/**/	set energyPack.destinationDistance	 = GetRandomReal(SPHERE_ENERGY_PACK_MIN_DIST, SPHERE_ENERGY_PACK_MAX_DIST) //final distance between sphere and energy pack
					/**/	set energyPack.speedh 				 = GetRandomReal(SPHERE_ENERGY_PACK_MIN_SPEED, SPHERE_ENERGY_PACK_MAX_SPEED)
					/**/	//Height/time
					/**/	set energyPack.speedv				 = GetRandomReal(SPHERE_ENERGY_PACK_MIN_HEIGHT, SPHERE_ENERGY_PACK_MAX_HEIGHT)/*
																   *//(energyPack.destinationDistance/energyPack.speedh) //formula same as particleEffect
					/**/	set energyPack.remainingTime			 = GetRandomReal(SPHERE_ENERGY_PACK_EXPLODE_MIN_TIME, SPHERE_ENERGY_PACK_EXPLODE_MAX_TIME)
					/**/	set energyPack.missileCount			 = GetRandomInt(SPHERE_ENERGY_PACK_MIN_MISSILE[this.spellLv], SPHERE_ENERGY_PACK_MAX_MISSILE[this.spellLv])
					/**/	set energyPack.spellLv				 = this.spellLv
					/**/	set energyPack.x 					 = this.targetX
					/**/	set energyPack.y 					 = this.targetY
					/**/	set energyPack.height 				 = this.sphereHeight
					/**/	set energyPack.owner				 = this.owner
					/**/	set energyPack.ec					 = this
					/**/	
					/**/	//create a unit to obtain the destinationX and destinationY
					/**/	set rand							 = GetRandomReal(-bj_PI, bj_PI)
					/**/	set energyPack.dummy				 = CreateUnit(this.owner, DUMMY_UNIT_ID, this.targetX + energyPack.destinationDistance * Cos(rand), this.targetY + energyPack.destinationDistance * Sin(rand), 0)
					/**/	set energyPack.destinationX			 = GetUnitX(energyPack.dummy)
					/**/	set energyPack.destinationY			 = GetUnitY(energyPack.dummy)
					/**/	set energyPack.e					 = AddSpecialEffectTarget(SPHERE_ENERGY_PACK_EFFECT_PATH, energyPack.dummy, ATTACHMENT_PATH)
					/**/
					/**/	//Move the dummy unit back where it should originally be
					/**/	call SetUnitX(energyPack.dummy, this.targetX)
					/**/	call SetUnitY(energyPack.dummy, this.targetY)
					/**/
					/**/	//Set unit scale
					/**/	set rand = GetRandomReal(SPHERE_ENERGY_PACK_MIN_SCALE, SPHERE_ENERGY_PACK_MAX_SCALE)
					/**/	call SetUnitScale(energyPack.dummy, rand, rand, rand)
					/**/
					/**/	//set unit color	
					/**/	call SetUnitVertexColor(energyPack.dummy, SPHERE_ENERGY_PACK_COLOR_RED, SPHERE_ENERGY_PACK_COLOR_GREEN, SPHERE_ENERGY_PACK_COLOR_BLUE, SPHERE_ENERGY_PACK_TRANSPARENCY)
					/**/
					/**/	//Set unit fly height
					/**/	call UnitAddRemoveCrowForm(energyPack.dummy)
					/**/	call SetUnitFlyHeight(energyPack.dummy, this.sphereHeight, 0.)
					/**/
					/**/	if energyPack == 1 then
					/**/		call TimerStart(SphereEnergyPack.timerSEP, TIMER_TIMEOUT, true, function SphereEnergyPack.onPeriodic)
					/**/	endif
					/**/else
					/**/	//increase the time passed by TIMER_TIMEOUT seconds
					/**/	set this.packReleaseTimePassed = this.packReleaseTimePassed + TIMER_TIMEOUT
					/**/endif
					/************************************************************************************************************/
					else
						set this.phase = SPHERE_AWAITING_PHASE
					endif
				endif
				
				set this = this.next
			endloop
			
		endmethod
		
		private static method onCast takes nothing returns boolean
			local thistype this
			
			if GetSpellAbilityId() == SPELL_ID then
				set this = thistype.create() //create a new instance
				
				//Storing data
				set this.triggerUnit				= GetTriggerUnit()
				set this.owner						= GetOwningPlayer(this.triggerUnit)
				set this.targetX					= GetSpellTargetX()
				set this.targetY					= GetSpellTargetY()
				set this.unitGroup					= CreateGroup()
				set this.spellLv					= GetUnitAbilityLevel(this.triggerUnit, SPELL_ID)
				set this.phase						= SPHERE_GENERATING_PHASE
				set this.sphereGenerateTime			= SPHERE_GENERATE_TIME[this.spellLv]
				set this.sphereHeight				= SPHERE_HEIGHT[this.spellLv]
				set this.sphereScale				= SPHERE_INITIAL_SCALE
													//sphereScaleConst is dependent on SPHERE_MAX_SCALE, SPHERE_GENERATE_TIME, TIMER_TIMEOUT and SHPERE_PARTICLE_STOP_TIMING
													//(this.sphereGenerateTime-SPHERE_PARTICLE_STOP_TIMING) : as sphere stop increase scale if no more particle is generating
													// divide by TIMER_TIMEOUT : to obtain the overall running times
													// SPHERE_MAX_SCALE[this.spellLv] : obtain the maximum scale of the sphere
													// maximum scale / running times : so that a constant is obtained for scale increasement every TIMER_TIMEOUT
				set this.sphereScaleConst 			= SPHERE_MAX_SCALE[this.spellLv]/((this.sphereGenerateTime-SPHERE_PARTICLE_STOP_TIMING)/TIMER_TIMEOUT)
				set this.spellDuration				= 0.
				set this.packReleaseTimePassed		= 0.
				set this.releasedPack				= 0
				set this.sphereDummy 				= CreateUnit(this.owner, DUMMY_UNIT_ID, this.targetX, this.targetY, 0.)
				
				//Add and remove crow form
				call UnitAddRemoveCrowForm(this.sphereDummy)
				
				//Set the unit height
				call SetUnitFlyHeight(this.sphereDummy, this.sphereHeight, 0.)
				
				//Setting the sphere to initial scale value
				call SetUnitScale(this.sphereDummy, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE)
				
				//Setting the sphere color
				call SetUnitVertexColor(this.sphereDummy, SPHERE_COLOR_RED, SPHERE_COLOR_GREEN, SPHERE_COLOR_BLUE, SPHERE_TRANSPARENCY)
				
				//Add the sphere effect to the unit
				set this.sphereEffect = AddSpecialEffectTarget(SPHERE_EFFECT_PATH, this.sphereDummy, ATTACHMENT_PATH)
				
				if this == 1 then
					call TimerStart(timerEC, TIMER_TIMEOUT, true, function thistype.onPeriodic)
				endif
			endif
			return false
		endmethod
		
		private static method onInit takes nothing returns nothing
			local trigger t = CreateTrigger()
			call TriggerAddCondition(t, Condition(function thistype.onCast))
			call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
			
			set t = null
		endmethod
	endstruct

endscope
 
Level 20
Joined
Apr 14, 2012
Messages
2,901
Is Unit Indexer allowed? I prefer that instead of using Dynamic Indexing, that is if it's permitted.

...But what should not happen is that your final result will mainly just be the usage of other's work.

Actually now that I think about it, a summoning spell using mostly the unit indexer... what would that be like
 
Level 20
Joined
Apr 14, 2012
Messages
2,901
Unit Indexer should definitely be allowed, but using that will narrow your code down. So it's up to you to use it. Personally, if a system is not that necessary I'd prefer to code it into my own code for my own spell's specific functionality instead of using the system as a whole.

What do you mean it will narrow the code down?
 

Ardenian

A

Ardenian

Since we are already discussing about multiple abilities, aren't we, are phases allowed ?
Phases as in casting multiple abilities one after each other, as different stages of the spell occur/to trigger them ?
 

Ardenian

A

Ardenian

Hm, I feared so. Well, gladly I myself do not absolutly depend on it, though it would be a neat customization.

Oh, you mean similar to your StageID tutorial ? Hm, will think about that, thanks!
 
Level 4
Joined
Apr 28, 2016
Messages
33
At last, my second WIP of summoning spell. I still using basic effect, so I want to make a simple but cool effect when summoning and taking form. I also edit the tooltip, the spell will like this:

Summons a formless ectoplasm that can shift into any non-hero and non-ancient unit. Ectoplasm can trick the enemies by its form, moving and attacking like others creep. If ectoplasm can get a new form before the lifetime gone, it will permanent until it dies. When dies, ectoplasm will damage around its and sending a virus which slowing and damaging per second to enemies on radius 300 AOE for 5 seconds.
Level 1 - Deals 100 damage, 60 per second.
Level 2 - Deals 200 damage, 60 per second.
Level 3 - Deals 300 damage, 60 per second.
Cooldown : 60/55/50
Mana Cost : 150/175/200
Ectoplasm Lifetime : 20 seconds

Is anyone know how to make .gif image in better way? Because mine in original size is 10 MB, and can't be upload to this site :( So i must upload it in other website....
l396SRmnEVvCbxKFO.gif
 
At last, my second WIP of summoning spell. I still using basic effect, so I want to make a simple but cool effect when summoning and taking form. I also edit the tooltip, the spell will like this:



Is anyone know how to make .gif image in better way? Because mine in original size is 10 MB, and can't be upload to this site :( So i must upload it in other website....

Use screen2gif, so you can modify the camera size while recording.
 
Oh, you mean similar to your StageID tutorial ? Hm, will think about that, thanks!

Well, that's not what I meant XD
I meant as in something like the ability reacting differently each cast
like activate to start collecting orbs from corpses, activate again to detonate orbs, activate again to go back to collecting, etc.
StageIDs would be a method you could use to implement it but not what I meant strictly speaking XD
 
WIP# 2

Probably going to make a test effect tomorrow and share a GIF

JASS:
scope Devourer initializer Init
    globals
        /*
        *   Timeout for all timers running in this spell
        */
        private constant real TIMEOUT = 0.03125
        /*
        *   The ability id of the spell
        */
        private constant integer ABIL_ID = 0
        /*
        *   rawcode id for the creeps (sfx units)
        */
        private constant integer CREEP_ID = 0
        //------------------------------------------------------
        /*
        *   Spell on cast
        */
        //------------------------------------------------------
        /*
        *   Required trees for summoning
        */
        private constant integer REQUIRED_TREES = 6
        private constant integer REQUIRED_TREES_PER_LVL = -1
        /*
        *   Radius for checking the required trees
        */
        private constant real TREE_RADIUS_CHECK = 256
        private constant real TREE_RADIUS_CHECK_PER_LVL = 0
        /*
        *   Spawn delay of the devourer
        */
        private constant real SPAWN_DELAY = 6
        private constant real SPAWN_DELAY_PER_LVL = -1
        /*
        *   How long the the devourer last
        */
        private constant real TIMED_LIFE = 10
        private constant real TIMED_LIFE_PER_LVL = 10
        //------------------------------------------------------
        /*
        *   Souls configurables
        */
        //------------------------------------------------------
        /*
        *   souls drop when unit dies in the given radius
        */
        private constant real SOUL_DROP_RADIUS = 800
        private constant real SOUL_DROP_RADIUS_PER_LVL = 0
        /*
        *   How long does the souls last
        */
        private constant real SOUL_DROP_DURATION = 10
        private constant real SOUL_DROP_DURATION_PER_LVL = 0
        /*
        *   Required distance to activate to souls
        */
        private constant real SOUL_FEED_RADIUS = 500
        private constant real SOUL_FEED_RADIUS_PER_LVL = 100
        /*
        *   Speed of the souls
        */
        private constant real SOUL_SPEED = 1000
        /*
        *   Bonus life duration added when souls reaches the devourer  
        */
        private constant real TIMED_LIFE_BONUS = 0
        private constant real TIMED_LIFE_BONUS_PER_LVL = 1
        //------------------------------------------------------
        /*
        *   Mana Release/Shockwave Configurables
        */
        //------------------------------------------------------
        /*
        *   Consumes all mana that are lost within the given range
        */
        private constant real MANA_FEED_RADIUS = 1000
        private constant real MANA_FEED_RADIUS_PER_LVL = 0
        /*
        *   Required mana to release the shockwave  
        */        
        private constant real MANA_RELEASE_REQ = 450
        private constant real MANA_RELEASE_REQ_PER_LVL = -50
        /*
        *   How large the shockwave is
        */
        private constant real MANA_RELEASE_RADIUS = 600
        private constant real MANA_RELEASE_RADIUS_PER_LVL = 0
        /*
        *   How long does the shockwave grow
        *   from it's release point to it's max radius
        */
        private constant real MANA_RELEASE_GROW_TIME = 2
        /*
        *   Damage dealt to the affected units 
        */
        private constant real MANA_RELEASE_DMG = 256
        private constant real MANA_RELEASE_DMG_PER_LVL = 0
        private constant attacktype MANA_RELEASE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype MANA_RELEASE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Is the shockwave locked to the devourer?
        *   locked = follows the devourer's position instead
        *            of the position of release.
        */
        private constant boolean MANA_RELEASED_LOCKED = true
        /*
        *   Cooldown of the shockwave
        */
        private constant real MANA_RELEASE_CD = 0
        private constant real MANA_RELEASE_CD_PER_LVL = 0
        /*
        *   Does the devourer collect mana even
        *   if the shockwave is on cooldown?
        */
        private constant boolean COLLECT_MANA_ON_CD = false
        //------------------------------------------------------
        /*
        *   Swarm configurables
        */
        //------------------------------------------------------
        /*
        *   Starting polar offset of the swarms
        */
        private constant real SWARM_SPAWN_OFFSET = 128
        private constant real SWARM_SPAWN_OFFSET_PER_LVL = 0
        /*
        *   How far doest the swarm travel
        *   (from the offset)
        */
        private constant real SWARM_TRAVEL_DIST = 768
        private constant real SWARM_TRAVEL_DIST_PER_LVL = 0
        /*
        *   Radius of the swarm
        */
        private constant real SWARM_RADIUS = 128
        private constant real SWARM_RADIUS_PER_LVL = 0
        /*
        *   Damage dealt by the swarm
        */
        private constant real SWARM_DAMAGE = 0
        private constant real SWARM_DAMAGE_PER_LVL = 75
        private constant attacktype SWARM_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype SWARM_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Swarm cooldown
        */
        private constant real SWARM_CD = 12
        private constant real SWARM_CD_PER_LVL = -2
        /*
        *   Swarm speed
        */
        private constant real SWARM_SPEED = 1024
        //------------------------------------------------------
        /*
        *   On Cast Effects
        */
        //------------------------------------------------------
        /*
        *   Portal Fields
        */
        /*
        *   Model of the portal
        */
        private constant string PORTAL_MODEL = ""
        /*
        *   Height of the portal
        */
        private constant real PORTAL_Z = 64
        /*
        *   Starting appearance of the portal
        */
        private constant real PORTAL_START_SCALE = 0
        private constant integer PORTAL_START_ALPHA = 255
        private constant integer PORTAL_START_RED = 255
        private constant integer PORTAL_START_GREEN = 255
        private constant integer PORTAL_START_BLUE = 255
        /*
        *   End appearance of the portal
        */
        private constant real PORTAL_END_SCALE = 2
        private constant integer PORTAL_END_ALPHA = 255
        private constant integer PORTAL_END_RED = 255
        private constant integer PORTAL_END_GREEN = 255
        private constant integer PORTAL_END_BLUE = 255
        /*
        *   Portal shard fields
        */
        /*
        *   Show the portal shards?
        */
        private constant boolean SHOW_SHARDS = true
        /*
        *   Model of the shards
        */
        private constant string SHARD_MODEL = ""
        /*
        *   Start appearance of the shards
        */
        private constant real SHARD_START_SCALE = 1
        private constant integer SHARD_START_ALPHA = 255
        private constant integer SHARD_START_RED = 255
        private constant integer SHARD_START_GREEN = 255
        private constant integer SHARD_START_BLUE = 255
        /*
        *   End appearance of the shards
        */
        private constant real SHARD_END_SCALE = 0
        private constant integer SHARD_END_ALPHA = 255
        private constant integer SHARD_END_RED = 255
        private constant integer SHARD_END_GREEN = 255
        private constant integer SHARD_END_BLUE = 255
        /*
        *   Spawn distance of the shards
        */
        private constant real SHARD_DISTANCE = 500
        private constant real SHARD_DISTANCE_VAR = 125
        /*
        *   Spawn angular variation of the shards
        */
        private constant real SHARD_ANGLE = 0
        private constant real SHARD_ANGLE_VAR = bj_PI
        private constant real SHARD_Z_ANGLE = bj_PI/2
        private constant real SHARD_Z_ANGLE_VAR = SHARD_Z_ANGLE
        /*
        *   How often shards spawn
        */
        private constant real SHARD_PERIOD = 0.0625
        /*
        *   How many shards spawn per period
        */
        private constant real SHARD_COUNT = 2
        /*
        *   Speed of the shards
        */
        private constant real SHARD_SPEED = 500
        private constant real SHARD_SPEED_VAR = 125
        //------------------------------------------------------
        /*
        *   On Spawn effects 
        */
        private constant string ON_SPAWN_SFX = ""
        /*
        *   Show creep nova
        */
        private constant boolean SHOW_CREEP_NOVA = true
        /*
        *   Start appearance of the nova creeps
        */
        private constant real NCREEP_START_SCALE = 1
        private constant integer NCREEP_START_ALPHA = 200
        private constant integer NCREEP_START_RED = 0
        private constant integer NCREEP_START_GREEN = 0
        private constant integer NCREEP_START_BLUE = 0
        /*
        *   End appearance of the nova creeps
        */
        private constant real NCREEP_END_SCALE = 1
        private constant integer NCREEP_END_ALPHA = 0
        private constant integer NCREEP_END_RED = 0
        private constant integer NCREEP_END_GREEN = 0
        private constant integer NCREEP_END_BLUE = 0
        /*
        *   Number of creeps released
        */
        private constant integer NCREEP_COUNT = 32
        /*
        *   Speed of the creeps
        */
        private constant real NCREEP_SPEED = 375
        private constant real NCREEP_SPEED_VAR = 125
        /*
        *   Creep life time 
        */
        private constant real NCREEP_TIME = 1.25
        /*
        *   Animation played by the creep
        */
        private constant string NCREEP_ANIM = "walk"
        /*
        *   Nova creep height
        */
        private constant real NCREEP_Z = 0
        //------------------------------------------------------
        /*
        *   Devourer appearances
        */
        private constant real DEVOURER_SCALE = 0.75
        private constant real DEVOURER_SCALE_PER_LVL = 0.25
        
        private constant integer DEVOURER_ALPHA = 200
        private constant integer DEVOURER_RED = 100
        private constant integer DEVOURER_GREEN = 100
        private constant integer DEVOURER_BLUE = 100
        
        private constant real DEVOURER_Z = 0
        
        private constant string DEVOURER_DEATH_SFX = ""
        //------------------------------------------------------
        /*
        *   Shadow Trail fields
        */
        private constant boolean SHOW_TRAIL = true
        /*
        *   Trail period
        */
        private constant real TRAIL_PERIOD = 0.09375
        /*
        *   Trail duration
        */
        private constant real TRAIL_DURATION = 1.5
        /*
        *   Trail animation
        */
        private constant string TRAIL_ANIM = "walk"
        //------------------------------------------------------
        /*
        *   Aura fields
        */
        private constant boolean SHOW_AURA = true
        /*
        *   Aura creep spawn period
        */
        private constant real AURA_SPAWN_PERIOD = 0.125
        /*
        *   Number of creep spawn
        */
        private constant integer ACREEP_COUNT = 1
        /*
        *   Start appearance of the aura creeps
        */
        private constant real ACREEP_START_SCALE = 0.25
        private constant integer ACREEP_START_ALPHA = 200
        private constant integer ACREEP_START_RED = 0
        private constant integer ACREEP_START_GREEN = 0
        private constant integer ACREEP_START_BLUE = 0
        /*
        *   End appearance of the aura creeps
        */
        private constant real ACREEP_END_SCALE = 0.25
        private constant integer ACREEP_END_ALPHA = 0
        private constant integer ACREEP_END_RED = 0
        private constant integer ACREEP_END_GREEN = 0
        private constant integer ACREEP_END_BLUE = 0
        /*
        *   Aura creep speed
        */
        private constant real ACREEP_SPEED = 200
        private constant real ACREEP_SPEED_VAR = 50
        /*
        *   Aura creep duration
        */
        private constant real ACREEP_TIME = 1.25
        /*
        *   Aura creep animation
        */
        private constant string ACREEP_ANIM = "walk"
        /*
        *   Aura creep height
        */
        private constant real ACREEP_Z = 0
        //------------------------------------------------------
        /*
        *   Souls
        */
        private constant string SOUL_MODEL = ""
        
        private constant real SOUL_SCALE = 1
        private constant integer SOUL_ALPHA = 128
        private constant integer SOUL_RED = 255
        private constant integer SOUL_GREEN = 255
        private constant integer SOUL_BLUE = 255
        //------------------------------------------------------
        /*
        *   Mana feed
        */
        private constant string MANA_FEED_SFX = ""
        private constant string MANA_FEED_ATTACH = ""
        //------------------------------------------------------
        /*
        *   Shockwave fields
        */
        private constant string MANA_RELEASE_SFX = ""
        /*
        *   Number of shockwave segments
        */
        private constant integer WAVE_COUNT = 12
        /*
        *   Shockwave appearance
        */
        private constant string WAVE_MODEL = ""
        
        private constant real WAVE_SCALE = 1.25
        private constant integer WAVE_ALPHA = 255
        private constant integer WAVE_RED = 255
        private constant integer WAVE_GREEN = 255
        private constant integer WAVE_BLUE = 255
        
        private constant real WAVE_Z = 32
        //------------------------------------------------------
        /*
        *   Swarm fields
        */
        private constant string SWARM_RELEASE_SFX = ""
        private constant string SWARM_MODEL = ""
        /*
        *   Swarm appearance
        */
        private constant real SWARM_SCALE = 1
        private constant integer SWARM_RED = 255
        private constant integer SWARM_GREEN = 255
        private constant integer SWARM_BLUE = 255
        private constant integer SWARM_ALPHA = 255
        /*
        *   Number of swarm segment
        */
        private constant integer SWARM_COUNT = 5
        /*
        *   Arc rate of the Swarm
        */
        private constant real SWARM_ARC = 0.25
    endglobals
    /*
    *   Setup Devourer IDs
    */
    globals
        private integer array DEVOURER_ID
    endglobals
    
    private function Init takes nothing returns nothing
        set DEVOURER_ID[1] = 0
        set DEVOURER_ID[2] = 0
        set DEVOURER_ID[3] = 0
    endfunction
    /*
    *   For checking alive units 
    */
    native UnitAlive takes unit u returns boolean
    /*
    *   For calculating Values with respect to levels
    */
    private function GetLevelValueR takes real base, real increment, integer level returns real
        return base + increment*level
    endfunction
    
    private function GetLevelValueI takes integer base, integer increment, integer level returns integer
        return base + increment*level
    endfunction
    /*
    *   For calculating value variance
    */
    private function GetVarianceR takes real base, real variance returns real
        if variance == 0 then
            return base
        endif
        return GetRandomReal(base - variance, base + variance)
    endfunction
    private function GetVarianceI takes integer base, integer variance returns integer
        if variance == 0 then
            return base
        endif
        return GetRandomInt(base - variance, base + variance)
    endfunction
    /*
    *   For calculating value over time 
    */
    private function LinearR takes real a, real b, real t returns real
        return a + (b - a)*t
    endfunction
    
    private function LinearI takes integer a, integer b, real t returns integer
        return R2I(LinearR(I2R(a), I2R(b), t))
    endfunction
    /**************************************
    *
    *   For getting the location surface z
    *
    **************************************/
    static if (not LIBRARY_ZLibrary) then
        globals
            private constant location p = Location(0.0, 0.0)
        endglobals
    
        function GetSurfaceZ takes real x, real y returns real
            call MoveLocation(p, x, y)
            return GetLocationZ(p)
        endfunction
    endif
    /*
    *   For handling lists and periods
    */
    //! textmacro DEVOURER_LIST_TIMER
        private static thistype array next
        private static thistype array prev
        
        private static constant timer t = CreateTimer()
        
        private static method insert takes real timeout, code c returns thistype
            local thistype this = allocate()
            set next[this] = 0
            set prev[this] = prev[0]
            set next[prev[0]] = this
            set prev[0] = this
            if prev[this] == 0 then
                call TimerStart(t, timeout, true, c)
            endif 
            
            return this
        endmethod
        
        private method remove takes nothing returns nothing
            call deallocate()
            set next[prev[this]] = next[this]
            set prev[next[this]] = prev[this]
        endmethod
    //! endtextmacro
    /*
    *   Structs for holding appearance values and position values
    */
    private struct Appearance
        integer red
        integer green
        integer blue
        integer alpha
        real scale
    endstruct
    private struct Vector
        real x
        real y
        real z
    endstruct
    /*
    *   Struct for timed appearances (fade, color change, etc.)
    */
    private struct TimedAppearance
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        private Appearance start
        private Appearance end
        private real current
        private real max
        
        method destroy takes nothing returns nothing
            call remove()
            
            set u = null
            call start.destroy()
            call end.destroy()
            
            set current = 0
            set max = 0
        endmethod
        
        private static method period takes nothing returns nothing
            local thistype this = next[0]
            local real pct
            local integer a
            local integer r
            local integer g
            local integer b
            local real s
            loop
                exitwhen 0 == this
                if current < max then
                    set current = current + TIMEOUT
                    set pct = current/max
                    set a = LinearI(start.alpha, end.alpha, pct)
                    set r = LinearI(start.red, end.red, pct)
                    set g = LinearI(start.green, end.green, pct)
                    set b = LinearI(start.blue, end.blue, pct)
                    set s = LinearR(start.scale, end.scale, pct)
                    
                    call SetUnitVertexColor(u, r, g, b, a)
                    call SetUnitScale(u, s, 0, 0)
                else
                    call destroy()
                endif
                set this = next[this]
            endloop
        endmethod
        
        static method add takes unit temp, Appearance startColor, Appearance endColor, real maxTime returns nothing
            local thistype this = insert(TIMEOUT, function thistype.period)
            set start = startColor
            set end = endColor
            set current = 0
            set max = maxTime
            set u = temp
        endmethod
    endstruct
    /*
    *   Struct for effect dummies
    */
    private struct Particle
        private unit u
        private effect mdl
        static method create takes string sfx, real x, real y, real z, real face returns thistype
            local thistype this = allocate()
            set u = GetRecycledUnit(x, y, false, face)
            call SetUnitFlyHeight(u, z - GetSurfaceZ(x, y), 0)
            set mdl = AddSpecialEffectTarget(sfx, u, "origin")
            return this
        endmethod
        method destroy takes nothing returns nothing
            call AddRecycleTimer(u, 2.0)
            call DestroyEffect(mdl)
            set u = null
            set mdl = null
        endmethod
    endstruct
    /*
    *   Projectile motion
    */
    module Missile
        private Vector offset
        
        private unit target
        private real size
        private real speed
        
        private real missileTime
        
        private method destroyMissile takes nothing returns nothing
            call onImpact(target)
            set target = null
            set speed = 0
            set missileTime = 0
        endmethod
        
        private method move takes nothing returns nothing
            local real ux = GetUnitX(u)
            local real uy = GetUnitY(u)
            local real uz = GetSurfaceZ(ux, uy) + GetUnitFlyHeight(u)
            
            local real tx
            local real ty
            local real tz
            
            local real dx
            local real dy
            local real dz
            
            local real a2
            local real a3
            
            if UnitAlive(target) then
                set tx = GetUnitX(target)
                set ty = GetUnitY(target)
                set tz = GetSurfaceZ(tx, ty) + GetUnitFlyHeight(target)
                
                set dx = tx - ux
                set dy = ty - uy
                set dz = tz - uz
                if GetMagnitude3d(dx, dy, dz) <= size then
                    call destroyMissile()
                endif
                
                set a2 = Atan2(dy, dx)
                set a3 = Atan2(dz, GetMagnitude2d(dx, dy))
                
            elseif missileTime > 0 then
                set missileTime = missileTime - TIMEOUT
                set ux = GetBoundedX(ux + offset.x)
                set uy = GetBoundedY(uy + offset.y)
                call SetUnitX(u, ux)
                call SetUnitY(u, uy)
                call SetUnitFlyHeight(u, (uz + offset.z) - GetSurfaceZ(ux, uy), 0) 
            else
                call destroyMissile()
            endif
        endmethod
        
        method setTarget takes unit temp returns nothing
            set target = temp
            set size = GetUnitCollision(temp)
        endmethod
    endmodule
    /*
    *   Struct for creeps
    */
    private struct Creep
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        
        method onImpact takes unit temp returns nothing
        
        endmethod
        implement Missile
    endstruct
    
    private struct Soul
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        
        method onImpact takes unit temp returns nothing
        
        endmethod
        
        implement Missile
    endstruct
    
    private struct Swarm
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct 
    
    private struct Shockwave
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct
    
    private struct Devourer
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct
    
    private struct SummonDevourer
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct
endscope
 

Ardenian

A

Ardenian

Well, that's not what I meant XD
I meant as in something like the ability reacting differently each cast
like activate to start collecting orbs from corpses, activate again to detonate orbs, activate again to go back to collecting, etc.
StageIDs would be a method you could use to implement it but not what I meant strictly speaking XD

Well, StageID seems to be a great way to keep overview then, that's why I mentioned it ^^
Hm, I will see whether it contributes to my spell, thank you!
 
Level 17
Joined
Sep 8, 2007
Messages
994
WIP# 2

Probably going to make a test effect tomorrow and share a GIF

JASS:
scope Devourer initializer Init
    globals
        /*
        *   Timeout for all timers running in this spell
        */
        private constant real TIMEOUT = 0.03125
        /*
        *   The ability id of the spell
        */
        private constant integer ABIL_ID = 0
        /*
        *   rawcode id for the creeps (sfx units)
        */
        private constant integer CREEP_ID = 0
        //------------------------------------------------------
        /*
        *   Spell on cast
        */
        //------------------------------------------------------
        /*
        *   Required trees for summoning
        */
        private constant integer REQUIRED_TREES = 6
        private constant integer REQUIRED_TREES_PER_LVL = -1
        /*
        *   Radius for checking the required trees
        */
        private constant real TREE_RADIUS_CHECK = 256
        private constant real TREE_RADIUS_CHECK_PER_LVL = 0
        /*
        *   Spawn delay of the devourer
        */
        private constant real SPAWN_DELAY = 6
        private constant real SPAWN_DELAY_PER_LVL = -1
        /*
        *   How long the the devourer last
        */
        private constant real TIMED_LIFE = 10
        private constant real TIMED_LIFE_PER_LVL = 10
        //------------------------------------------------------
        /*
        *   Souls configurables
        */
        //------------------------------------------------------
        /*
        *   souls drop when unit dies in the given radius
        */
        private constant real SOUL_DROP_RADIUS = 800
        private constant real SOUL_DROP_RADIUS_PER_LVL = 0
        /*
        *   How long does the souls last
        */
        private constant real SOUL_DROP_DURATION = 10
        private constant real SOUL_DROP_DURATION_PER_LVL = 0
        /*
        *   Required distance to activate to souls
        */
        private constant real SOUL_FEED_RADIUS = 500
        private constant real SOUL_FEED_RADIUS_PER_LVL = 100
        /*
        *   Speed of the souls
        */
        private constant real SOUL_SPEED = 1000
        /*
        *   Bonus life duration added when souls reaches the devourer  
        */
        private constant real TIMED_LIFE_BONUS = 0
        private constant real TIMED_LIFE_BONUS_PER_LVL = 1
        //------------------------------------------------------
        /*
        *   Mana Release/Shockwave Configurables
        */
        //------------------------------------------------------
        /*
        *   Consumes all mana that are lost within the given range
        */
        private constant real MANA_FEED_RADIUS = 1000
        private constant real MANA_FEED_RADIUS_PER_LVL = 0
        /*
        *   Required mana to release the shockwave  
        */        
        private constant real MANA_RELEASE_REQ = 450
        private constant real MANA_RELEASE_REQ_PER_LVL = -50
        /*
        *   How large the shockwave is
        */
        private constant real MANA_RELEASE_RADIUS = 600
        private constant real MANA_RELEASE_RADIUS_PER_LVL = 0
        /*
        *   How long does the shockwave grow
        *   from it's release point to it's max radius
        */
        private constant real MANA_RELEASE_GROW_TIME = 2
        /*
        *   Damage dealt to the affected units 
        */
        private constant real MANA_RELEASE_DMG = 256
        private constant real MANA_RELEASE_DMG_PER_LVL = 0
        private constant attacktype MANA_RELEASE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype MANA_RELEASE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Is the shockwave locked to the devourer?
        *   locked = follows the devourer's position instead
        *            of the position of release.
        */
        private constant boolean MANA_RELEASED_LOCKED = true
        /*
        *   Cooldown of the shockwave
        */
        private constant real MANA_RELEASE_CD = 0
        private constant real MANA_RELEASE_CD_PER_LVL = 0
        /*
        *   Does the devourer collect mana even
        *   if the shockwave is on cooldown?
        */
        private constant boolean COLLECT_MANA_ON_CD = false
        //------------------------------------------------------
        /*
        *   Swarm configurables
        */
        //------------------------------------------------------
        /*
        *   Starting polar offset of the swarms
        */
        private constant real SWARM_SPAWN_OFFSET = 128
        private constant real SWARM_SPAWN_OFFSET_PER_LVL = 0
        /*
        *   How far doest the swarm travel
        *   (from the offset)
        */
        private constant real SWARM_TRAVEL_DIST = 768
        private constant real SWARM_TRAVEL_DIST_PER_LVL = 0
        /*
        *   Radius of the swarm
        */
        private constant real SWARM_RADIUS = 128
        private constant real SWARM_RADIUS_PER_LVL = 0
        /*
        *   Damage dealt by the swarm
        */
        private constant real SWARM_DAMAGE = 0
        private constant real SWARM_DAMAGE_PER_LVL = 75
        private constant attacktype SWARM_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype SWARM_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Swarm cooldown
        */
        private constant real SWARM_CD = 12
        private constant real SWARM_CD_PER_LVL = -2
        /*
        *   Swarm speed
        */
        private constant real SWARM_SPEED = 1024
        //------------------------------------------------------
        /*
        *   On Cast Effects
        */
        //------------------------------------------------------
        /*
        *   Portal Fields
        */
        /*
        *   Model of the portal
        */
        private constant string PORTAL_MODEL = ""
        /*
        *   Height of the portal
        */
        private constant real PORTAL_Z = 64
        /*
        *   Starting appearance of the portal
        */
        private constant real PORTAL_START_SCALE = 0
        private constant integer PORTAL_START_ALPHA = 255
        private constant integer PORTAL_START_RED = 255
        private constant integer PORTAL_START_GREEN = 255
        private constant integer PORTAL_START_BLUE = 255
        /*
        *   End appearance of the portal
        */
        private constant real PORTAL_END_SCALE = 2
        private constant integer PORTAL_END_ALPHA = 255
        private constant integer PORTAL_END_RED = 255
        private constant integer PORTAL_END_GREEN = 255
        private constant integer PORTAL_END_BLUE = 255
        /*
        *   Portal shard fields
        */
        /*
        *   Show the portal shards?
        */
        private constant boolean SHOW_SHARDS = true
        /*
        *   Model of the shards
        */
        private constant string SHARD_MODEL = ""
        /*
        *   Start appearance of the shards
        */
        private constant real SHARD_START_SCALE = 1
        private constant integer SHARD_START_ALPHA = 255
        private constant integer SHARD_START_RED = 255
        private constant integer SHARD_START_GREEN = 255
        private constant integer SHARD_START_BLUE = 255
        /*
        *   End appearance of the shards
        */
        private constant real SHARD_END_SCALE = 0
        private constant integer SHARD_END_ALPHA = 255
        private constant integer SHARD_END_RED = 255
        private constant integer SHARD_END_GREEN = 255
        private constant integer SHARD_END_BLUE = 255
        /*
        *   Spawn distance of the shards
        */
        private constant real SHARD_DISTANCE = 500
        private constant real SHARD_DISTANCE_VAR = 125
        /*
        *   Spawn angular variation of the shards
        */
        private constant real SHARD_ANGLE = 0
        private constant real SHARD_ANGLE_VAR = bj_PI
        private constant real SHARD_Z_ANGLE = bj_PI/2
        private constant real SHARD_Z_ANGLE_VAR = SHARD_Z_ANGLE
        /*
        *   How often shards spawn
        */
        private constant real SHARD_PERIOD = 0.0625
        /*
        *   How many shards spawn per period
        */
        private constant real SHARD_COUNT = 2
        /*
        *   Speed of the shards
        */
        private constant real SHARD_SPEED = 500
        private constant real SHARD_SPEED_VAR = 125
        //------------------------------------------------------
        /*
        *   On Spawn effects 
        */
        private constant string ON_SPAWN_SFX = ""
        /*
        *   Show creep nova
        */
        private constant boolean SHOW_CREEP_NOVA = true
        /*
        *   Start appearance of the nova creeps
        */
        private constant real NCREEP_START_SCALE = 1
        private constant integer NCREEP_START_ALPHA = 200
        private constant integer NCREEP_START_RED = 0
        private constant integer NCREEP_START_GREEN = 0
        private constant integer NCREEP_START_BLUE = 0
        /*
        *   End appearance of the nova creeps
        */
        private constant real NCREEP_END_SCALE = 1
        private constant integer NCREEP_END_ALPHA = 0
        private constant integer NCREEP_END_RED = 0
        private constant integer NCREEP_END_GREEN = 0
        private constant integer NCREEP_END_BLUE = 0
        /*
        *   Number of creeps released
        */
        private constant integer NCREEP_COUNT = 32
        /*
        *   Speed of the creeps
        */
        private constant real NCREEP_SPEED = 375
        private constant real NCREEP_SPEED_VAR = 125
        /*
        *   Creep life time 
        */
        private constant real NCREEP_TIME = 1.25
        /*
        *   Animation played by the creep
        */
        private constant string NCREEP_ANIM = "walk"
        /*
        *   Nova creep height
        */
        private constant real NCREEP_Z = 0
        //------------------------------------------------------
        /*
        *   Devourer appearances
        */
        private constant real DEVOURER_SCALE = 0.75
        private constant real DEVOURER_SCALE_PER_LVL = 0.25
        
        private constant integer DEVOURER_ALPHA = 200
        private constant integer DEVOURER_RED = 100
        private constant integer DEVOURER_GREEN = 100
        private constant integer DEVOURER_BLUE = 100
        
        private constant real DEVOURER_Z = 0
        
        private constant string DEVOURER_DEATH_SFX = ""
        //------------------------------------------------------
        /*
        *   Shadow Trail fields
        */
        private constant boolean SHOW_TRAIL = true
        /*
        *   Trail period
        */
        private constant real TRAIL_PERIOD = 0.09375
        /*
        *   Trail duration
        */
        private constant real TRAIL_DURATION = 1.5
        /*
        *   Trail animation
        */
        private constant string TRAIL_ANIM = "walk"
        //------------------------------------------------------
        /*
        *   Aura fields
        */
        private constant boolean SHOW_AURA = true
        /*
        *   Aura creep spawn period
        */
        private constant real AURA_SPAWN_PERIOD = 0.125
        /*
        *   Number of creep spawn
        */
        private constant integer ACREEP_COUNT = 1
        /*
        *   Start appearance of the aura creeps
        */
        private constant real ACREEP_START_SCALE = 0.25
        private constant integer ACREEP_START_ALPHA = 200
        private constant integer ACREEP_START_RED = 0
        private constant integer ACREEP_START_GREEN = 0
        private constant integer ACREEP_START_BLUE = 0
        /*
        *   End appearance of the aura creeps
        */
        private constant real ACREEP_END_SCALE = 0.25
        private constant integer ACREEP_END_ALPHA = 0
        private constant integer ACREEP_END_RED = 0
        private constant integer ACREEP_END_GREEN = 0
        private constant integer ACREEP_END_BLUE = 0
        /*
        *   Aura creep speed
        */
        private constant real ACREEP_SPEED = 200
        private constant real ACREEP_SPEED_VAR = 50
        /*
        *   Aura creep duration
        */
        private constant real ACREEP_TIME = 1.25
        /*
        *   Aura creep animation
        */
        private constant string ACREEP_ANIM = "walk"
        /*
        *   Aura creep height
        */
        private constant real ACREEP_Z = 0
        //------------------------------------------------------
        /*
        *   Souls
        */
        private constant string SOUL_MODEL = ""
        
        private constant real SOUL_SCALE = 1
        private constant integer SOUL_ALPHA = 128
        private constant integer SOUL_RED = 255
        private constant integer SOUL_GREEN = 255
        private constant integer SOUL_BLUE = 255
        //------------------------------------------------------
        /*
        *   Mana feed
        */
        private constant string MANA_FEED_SFX = ""
        private constant string MANA_FEED_ATTACH = ""
        //------------------------------------------------------
        /*
        *   Shockwave fields
        */
        private constant string MANA_RELEASE_SFX = ""
        /*
        *   Number of shockwave segments
        */
        private constant integer WAVE_COUNT = 12
        /*
        *   Shockwave appearance
        */
        private constant string WAVE_MODEL = ""
        
        private constant real WAVE_SCALE = 1.25
        private constant integer WAVE_ALPHA = 255
        private constant integer WAVE_RED = 255
        private constant integer WAVE_GREEN = 255
        private constant integer WAVE_BLUE = 255
        
        private constant real WAVE_Z = 32
        //------------------------------------------------------
        /*
        *   Swarm fields
        */
        private constant string SWARM_RELEASE_SFX = ""
        private constant string SWARM_MODEL = ""
        /*
        *   Swarm appearance
        */
        private constant real SWARM_SCALE = 1
        private constant integer SWARM_RED = 255
        private constant integer SWARM_GREEN = 255
        private constant integer SWARM_BLUE = 255
        private constant integer SWARM_ALPHA = 255
        /*
        *   Number of swarm segment
        */
        private constant integer SWARM_COUNT = 5
        /*
        *   Arc rate of the Swarm
        */
        private constant real SWARM_ARC = 0.25
    endglobals
    /*
    *   Setup Devourer IDs
    */
    globals
        private integer array DEVOURER_ID
    endglobals
    
    private function Init takes nothing returns nothing
        set DEVOURER_ID[1] = 0
        set DEVOURER_ID[2] = 0
        set DEVOURER_ID[3] = 0
    endfunction
    /*
    *   For checking alive units 
    */
    native UnitAlive takes unit u returns boolean
    /*
    *   For calculating Values with respect to levels
    */
    private function GetLevelValueR takes real base, real increment, integer level returns real
        return base + increment*level
    endfunction
    
    private function GetLevelValueI takes integer base, integer increment, integer level returns integer
        return base + increment*level
    endfunction
    /*
    *   For calculating value variance
    */
    private function GetVarianceR takes real base, real variance returns real
        if variance == 0 then
            return base
        endif
        return GetRandomReal(base - variance, base + variance)
    endfunction
    private function GetVarianceI takes integer base, integer variance returns integer
        if variance == 0 then
            return base
        endif
        return GetRandomInt(base - variance, base + variance)
    endfunction
    /*
    *   For calculating value over time 
    */
    private function LinearR takes real a, real b, real t returns real
        return a + (b - a)*t
    endfunction
    
    private function LinearI takes integer a, integer b, real t returns integer
        return R2I(LinearR(I2R(a), I2R(b), t))
    endfunction
    /**************************************
    *
    *   For getting the location surface z
    *
    **************************************/
    static if (not LIBRARY_ZLibrary) then
        globals
            private constant location p = Location(0.0, 0.0)
        endglobals
    
        function GetSurfaceZ takes real x, real y returns real
            call MoveLocation(p, x, y)
            return GetLocationZ(p)
        endfunction
    endif
    /*
    *   For handling lists and periods
    */
    //! textmacro DEVOURER_LIST_TIMER
        private static thistype array next
        private static thistype array prev
        
        private static constant timer t = CreateTimer()
        
        private static method insert takes real timeout, code c returns thistype
            local thistype this = allocate()
            set next[this] = 0
            set prev[this] = prev[0]
            set next[prev[0]] = this
            set prev[0] = this
            if prev[this] == 0 then
                call TimerStart(t, timeout, true, c)
            endif 
            
            return this
        endmethod
        
        private method remove takes nothing returns nothing
            call deallocate()
            set next[prev[this]] = next[this]
            set prev[next[this]] = prev[this]
        endmethod
    //! endtextmacro
    /*
    *   Structs for holding appearance values and position values
    */
    private struct Appearance
        integer red
        integer green
        integer blue
        integer alpha
        real scale
    endstruct
    private struct Vector
        real x
        real y
        real z
    endstruct
    /*
    *   Struct for timed appearances (fade, color change, etc.)
    */
    private struct TimedAppearance
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        private Appearance start
        private Appearance end
        private real current
        private real max
        
        method destroy takes nothing returns nothing
            call remove()
            
            set u = null
            call start.destroy()
            call end.destroy()
            
            set current = 0
            set max = 0
        endmethod
        
        private static method period takes nothing returns nothing
            local thistype this = next[0]
            local real pct
            local integer a
            local integer r
            local integer g
            local integer b
            local real s
            loop
                exitwhen 0 == this
                if current < max then
                    set current = current + TIMEOUT
                    set pct = current/max
                    set a = LinearI(start.alpha, end.alpha, pct)
                    set r = LinearI(start.red, end.red, pct)
                    set g = LinearI(start.green, end.green, pct)
                    set b = LinearI(start.blue, end.blue, pct)
                    set s = LinearR(start.scale, end.scale, pct)
                    
                    call SetUnitVertexColor(u, r, g, b, a)
                    call SetUnitScale(u, s, 0, 0)
                else
                    call destroy()
                endif
                set this = next[this]
            endloop
        endmethod
        
        static method add takes unit temp, Appearance startColor, Appearance endColor, real maxTime returns nothing
            local thistype this = insert(TIMEOUT, function thistype.period)
            set start = startColor
            set end = endColor
            set current = 0
            set max = maxTime
            set u = temp
        endmethod
    endstruct
    /*
    *   Struct for effect dummies
    */
    private struct Particle
        private unit u
        private effect mdl
        static method create takes string sfx, real x, real y, real z, real face returns thistype
            local thistype this = allocate()
            set u = GetRecycledUnit(x, y, false, face)
            call SetUnitFlyHeight(u, z - GetSurfaceZ(x, y), 0)
            set mdl = AddSpecialEffectTarget(sfx, u, "origin")
            return this
        endmethod
        method destroy takes nothing returns nothing
            call AddRecycleTimer(u, 2.0)
            call DestroyEffect(mdl)
            set u = null
            set mdl = null
        endmethod
    endstruct
    /*
    *   Projectile motion
    */
    module Missile
        private Vector offset
        
        private unit target
        private real size
        private real speed
        
        private real missileTime
        
        private method destroyMissile takes nothing returns nothing
            call onImpact(target)
            set target = null
            set speed = 0
            set missileTime = 0
        endmethod
        
        private method move takes nothing returns nothing
            local real ux = GetUnitX(u)
            local real uy = GetUnitY(u)
            local real uz = GetSurfaceZ(ux, uy) + GetUnitFlyHeight(u)
            
            local real tx
            local real ty
            local real tz
            
            local real dx
            local real dy
            local real dz
            
            local real a2
            local real a3
            
            if UnitAlive(target) then
                set tx = GetUnitX(target)
                set ty = GetUnitY(target)
                set tz = GetSurfaceZ(tx, ty) + GetUnitFlyHeight(target)
                
                set dx = tx - ux
                set dy = ty - uy
                set dz = tz - uz
                if GetMagnitude3d(dx, dy, dz) <= size then
                    call destroyMissile()
                endif
                
                set a2 = Atan2(dy, dx)
                set a3 = Atan2(dz, GetMagnitude2d(dx, dy))
                
            elseif missileTime > 0 then
                set missileTime = missileTime - TIMEOUT
                set ux = GetBoundedX(ux + offset.x)
                set uy = GetBoundedY(uy + offset.y)
                call SetUnitX(u, ux)
                call SetUnitY(u, uy)
                call SetUnitFlyHeight(u, (uz + offset.z) - GetSurfaceZ(ux, uy), 0) 
            else
                call destroyMissile()
            endif
        endmethod
        
        method setTarget takes unit temp returns nothing
            set target = temp
            set size = GetUnitCollision(temp)
        endmethod
    endmodule
    /*
    *   Struct for creeps
    */
    private struct Creep
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        
        method onImpact takes unit temp returns nothing
        
        endmethod
        implement Missile
    endstruct
    
    private struct Soul
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        
        method onImpact takes unit temp returns nothing
        
        endmethod
        
        implement Missile
    endstruct
    
    private struct Swarm
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct 
    
    private struct Shockwave
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct
    
    private struct Devourer
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct
    
    private struct SummonDevourer
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct
endscope

I feel like there are more constants than actual code lol.
 
I feel like there are more constants than actual code lol.

That's because the actual code.isn't even at 25% ;)

[edit]
WIP # 3 (900+ lines ;_;)

JASS:
scope Devourer initializer Init
    globals
        /*
        *   Timeout for all timers running in this spell
        */
        private constant real TIMEOUT = 0.03125
        /*
        *   The ability id of the spell
        */
        private constant integer ABIL_ID = 0
        /*
        *   rawcode id for the creeps (sfx units)
        */
        private constant integer CREEP_ID = 'nspb'
        /*
        *   rawcode id of dummies
        */
        private constant integer DUMMY_ID = 'dumi'
        //------------------------------------------------------
        /*
        *   Spell on cast
        */
        //------------------------------------------------------
        /*
        *   Required trees for summoning
        */
        private constant integer REQUIRED_TREES = 6
        private constant integer REQUIRED_TREES_PER_LVL = -1
        /*
        *   Radius for checking the required trees
        */
        private constant real TREE_RADIUS_CHECK = 256
        private constant real TREE_RADIUS_CHECK_PER_LVL = 0
        /*
        *   Spawn delay of the devourer
        */
        private constant real SPAWN_DELAY = 6
        private constant real SPAWN_DELAY_PER_LVL = -1
        /*
        *   How long the the devourer last
        */
        private constant real TIMED_LIFE = 10
        private constant real TIMED_LIFE_PER_LVL = 10
        //------------------------------------------------------
        /*
        *   Souls configurables
        */
        //------------------------------------------------------
        /*
        *   souls drop when unit dies in the given radius
        */
        private constant real SOUL_DROP_RADIUS = 800
        private constant real SOUL_DROP_RADIUS_PER_LVL = 0
        /*
        *   How long does the souls last
        */
        private constant real SOUL_DROP_DURATION = 10
        private constant real SOUL_DROP_DURATION_PER_LVL = 0
        /*
        *   Required distance to activate to souls
        */
        private constant real SOUL_FEED_RADIUS = 500
        private constant real SOUL_FEED_RADIUS_PER_LVL = 100
        /*
        *   Speed of the souls
        */
        private constant real SOUL_SPEED = 1000
        /*
        *   Bonus life duration added when souls reaches the devourer  
        */
        private constant real TIMED_LIFE_BONUS = 0
        private constant real TIMED_LIFE_BONUS_PER_LVL = 1
        //------------------------------------------------------
        /*
        *   Mana Release/Shockwave Configurables
        */
        //------------------------------------------------------
        /*
        *   Consumes all mana that are lost within the given range
        */
        private constant real MANA_FEED_RADIUS = 1000
        private constant real MANA_FEED_RADIUS_PER_LVL = 0
        /*
        *   Required mana to release the shockwave  
        */        
        private constant real MANA_RELEASE_REQ = 450
        private constant real MANA_RELEASE_REQ_PER_LVL = -50
        /*
        *   How large the shockwave is
        */
        private constant real MANA_RELEASE_RADIUS = 600
        private constant real MANA_RELEASE_RADIUS_PER_LVL = 0
        /*
        *   How long does the shockwave grow
        *   from it's release point to it's max radius
        */
        private constant real MANA_RELEASE_GROW_TIME = 2
        /*
        *   Damage dealt to the affected units 
        */
        private constant real MANA_RELEASE_DMG = 256
        private constant real MANA_RELEASE_DMG_PER_LVL = 0
        private constant attacktype MANA_RELEASE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype MANA_RELEASE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Is the shockwave locked to the devourer?
        *   locked = follows the devourer's position instead
        *            of the position of release.
        */
        private constant boolean MANA_RELEASED_LOCKED = true
        /*
        *   Cooldown of the shockwave
        */
        private constant real MANA_RELEASE_CD = 0
        private constant real MANA_RELEASE_CD_PER_LVL = 0
        /*
        *   Does the devourer collect mana even
        *   if the shockwave is on cooldown?
        */
        private constant boolean COLLECT_MANA_ON_CD = false
        //------------------------------------------------------
        /*
        *   Swarm configurables
        */
        //------------------------------------------------------
        /*
        *   Starting polar offset of the swarms
        */
        private constant real SWARM_SPAWN_OFFSET = 128
        private constant real SWARM_SPAWN_OFFSET_PER_LVL = 0
        /*
        *   How far doest the swarm travel
        *   (from the offset)
        */
        private constant real SWARM_TRAVEL_DIST = 768
        private constant real SWARM_TRAVEL_DIST_PER_LVL = 0
        /*
        *   Radius of the swarm
        */
        private constant real SWARM_RADIUS = 128
        private constant real SWARM_RADIUS_PER_LVL = 0
        /*
        *   Damage dealt by the swarm
        */
        private constant real SWARM_DAMAGE = 0
        private constant real SWARM_DAMAGE_PER_LVL = 75
        private constant attacktype SWARM_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype SWARM_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Swarm cooldown
        */
        private constant real SWARM_CD = 12
        private constant real SWARM_CD_PER_LVL = -2
        /*
        *   Swarm speed
        */
        private constant real SWARM_SPEED = 1024
        //------------------------------------------------------
        /*
        *   On Cast Effects
        */
        //------------------------------------------------------
        /*
        *   Portal Fields
        */
        /*
        *   Model of the portal
        */
        private constant string PORTAL_MODEL = "Abilities\\Spells\\Undead\\Possession\\PossessionTarget.mdl"
        /*
        *   Height of the portal
        */
        private constant real PORTAL_Z = 64
        /*
        *   Starting appearance of the portal
        */
        private constant real PORTAL_START_SCALE = 0
        private constant integer PORTAL_START_ALPHA = 255
        private constant integer PORTAL_START_RED = 255
        private constant integer PORTAL_START_GREEN = 255
        private constant integer PORTAL_START_BLUE = 255
        /*
        *   End appearance of the portal
        */
        private constant real PORTAL_END_SCALE = 3
        private constant integer PORTAL_END_ALPHA = 255
        private constant integer PORTAL_END_RED = 255
        private constant integer PORTAL_END_GREEN = 255
        private constant integer PORTAL_END_BLUE = 255
        /*
        *   Portal shard fields
        */
        /*
        *   Show the portal shards?
        */
        private constant boolean SHOW_SHARDS = true
        /*
        *   Model of the shards
        */
        private constant string SHARD_MODEL = "Abilities\\Spells\\Undead\\Possession\\PossessionTarget.mdl"
        /*
        *   Start appearance of the shards
        */
        private constant real SHARD_START_SCALE = 1
        private constant integer SHARD_START_ALPHA = 255
        private constant integer SHARD_START_RED = 255
        private constant integer SHARD_START_GREEN = 255
        private constant integer SHARD_START_BLUE = 255
        /*
        *   End appearance of the shards
        */
        private constant real SHARD_END_SCALE = 0
        private constant integer SHARD_END_ALPHA = 255
        private constant integer SHARD_END_RED = 255
        private constant integer SHARD_END_GREEN = 255
        private constant integer SHARD_END_BLUE = 255
        /*
        *   Spawn distance of the shards
        */
        private constant real SHARD_DISTANCE = 500
        private constant real SHARD_DISTANCE_VAR = 125
        /*
        *   Spawn angular variation of the shards
        */
        private constant real SHARD_ANGLE = 0
        private constant real SHARD_ANGLE_VAR = bj_PI
        private constant real SHARD_Z_ANGLE = bj_PI/2
        private constant real SHARD_Z_ANGLE_VAR = SHARD_Z_ANGLE
        /*
        *   How often shards spawn
        */
        private constant real SHARD_PERIOD = 0.0625
        /*
        *   How many shards spawn per period
        */
        private constant integer SHARD_COUNT = 2
        /*
        *   Speed of the shards
        */
        private constant real SHARD_SPEED = 500
        private constant real SHARD_SPEED_VAR = 125
        //------------------------------------------------------
        /*
        *   On Spawn effects 
        */
        private constant string ON_SPAWN_SFX = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl"
        /*
        *   Show creep nova
        */
        private constant boolean SHOW_CREEP_NOVA = true
        /*
        *   Start appearance of the nova creeps
        */
        private constant real NCREEP_START_SCALE = 1
        private constant integer NCREEP_START_ALPHA = 200
        private constant integer NCREEP_START_RED = 0
        private constant integer NCREEP_START_GREEN = 0
        private constant integer NCREEP_START_BLUE = 0
        /*
        *   End appearance of the nova creeps
        */
        private constant real NCREEP_END_SCALE = 1
        private constant integer NCREEP_END_ALPHA = 0
        private constant integer NCREEP_END_RED = 0
        private constant integer NCREEP_END_GREEN = 0
        private constant integer NCREEP_END_BLUE = 0
        /*
        *   Number of creeps released
        */
        private constant integer NCREEP_COUNT = 32
        /*
        *   Speed of the creeps
        */
        private constant real NCREEP_SPEED = 375
        private constant real NCREEP_SPEED_VAR = 125
        /*
        *   Creep life time 
        */
        private constant real NCREEP_TIME = 1.25
        /*
        *   Animation played by the creep
        */
        private constant string NCREEP_ANIM = "walk"
        /*
        *   Nova creep height
        */
        private constant real NCREEP_Z = 0
        //------------------------------------------------------
        /*
        *   Devourer appearances
        */
        private constant real DEVOURER_SCALE = 0.75
        private constant real DEVOURER_SCALE_PER_LVL = 0.25
        
        private constant integer DEVOURER_ALPHA = 200
        private constant integer DEVOURER_RED = 100
        private constant integer DEVOURER_GREEN = 100
        private constant integer DEVOURER_BLUE = 100
        
        private constant real DEVOURER_Z = 0
        
        private constant string DEVOURER_DEATH_SFX = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl"
        //------------------------------------------------------
        /*
        *   Shadow Trail fields
        */
        private constant boolean SHOW_TRAIL = true
        /*
        *   Trail period
        */
        private constant real TRAIL_PERIOD = 0.09375
        /*
        *   Trail duration
        */
        private constant real TRAIL_DURATION = 1.5
        /*
        *   Trail animation
        */
        private constant string TRAIL_ANIM = "walk"
        //------------------------------------------------------
        /*
        *   Aura fields
        */
        private constant boolean SHOW_AURA = true
        /*
        *   Aura creep spawn period
        */
        private constant real AURA_SPAWN_PERIOD = 0.125
        /*
        *   Number of creep spawn
        */
        private constant integer ACREEP_COUNT = 1
        /*
        *   Start appearance of the aura creeps
        */
        private constant real ACREEP_START_SCALE = 0.25
        private constant integer ACREEP_START_ALPHA = 200
        private constant integer ACREEP_START_RED = 0
        private constant integer ACREEP_START_GREEN = 0
        private constant integer ACREEP_START_BLUE = 0
        /*
        *   End appearance of the aura creeps
        */
        private constant real ACREEP_END_SCALE = 0.25
        private constant integer ACREEP_END_ALPHA = 0
        private constant integer ACREEP_END_RED = 0
        private constant integer ACREEP_END_GREEN = 0
        private constant integer ACREEP_END_BLUE = 0
        /*
        *   Aura creep speed
        */
        private constant real ACREEP_SPEED = 200
        private constant real ACREEP_SPEED_VAR = 50
        /*
        *   Aura creep duration
        */
        private constant real ACREEP_TIME = 1.25
        /*
        *   Aura creep animation
        */
        private constant string ACREEP_ANIM = "walk"
        /*
        *   Aura creep height
        */
        private constant real ACREEP_Z = 0
        //------------------------------------------------------
        /*
        *   Souls
        */
        private constant string SOUL_MODEL = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl"
        
        private constant real SOUL_SCALE = 1
        private constant integer SOUL_ALPHA = 128
        private constant integer SOUL_RED = 255
        private constant integer SOUL_GREEN = 255
        private constant integer SOUL_BLUE = 255
        //------------------------------------------------------
        /*
        *   Mana feed
        */
        private constant string MANA_FEED_SFX = "Abilities\\Spells\\Undead\\AbsorbMana\\AbsorbManaBirthMissile.mdl"
        private constant string MANA_FEED_ATTACH = "head"
        //------------------------------------------------------
        /*
        *   Shockwave fields
        */
        private constant string MANA_RELEASE_SFX = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl"
        /*
        *   Number of shockwave segments
        */
        private constant integer WAVE_COUNT = 12
        /*
        *   Shockwave appearance
        */
        private constant string WAVE_MODEL = "Abilities\\Spells\\Undead\\OrbOfDeath\\AnnihilationMissile.mdl"
        
        private constant real WAVE_SCALE = 1.25
        private constant integer WAVE_ALPHA = 255
        private constant integer WAVE_RED = 255
        private constant integer WAVE_GREEN = 255
        private constant integer WAVE_BLUE = 255
        
        private constant real WAVE_Z = 32
        //------------------------------------------------------
        /*
        *   Swarm fields
        */
        private constant string SWARM_RELEASE_SFX = ""
        private constant string SWARM_MODEL = ""
        /*
        *   Swarm appearance
        */
        private constant real SWARM_SCALE = 1
        private constant integer SWARM_RED = 255
        private constant integer SWARM_GREEN = 255
        private constant integer SWARM_BLUE = 255
        private constant integer SWARM_ALPHA = 255
        /*
        *   Number of swarm segment
        */
        private constant integer SWARM_COUNT = 5
        /*
        *   Arc rate of the Swarm
        */
        private constant real SWARM_ARC = 0.25
    endglobals
    /*
    *   Setup Devourer IDs
    */
    globals
        private integer array DEVOURER_ID
    endglobals
    
    private function SetupDevourerId takes nothing returns nothing
        set DEVOURER_ID[1] = 0
        set DEVOURER_ID[2] = 0
        set DEVOURER_ID[3] = 0
    endfunction
    /*
    *   For checking alive units 
    */
    native UnitAlive takes unit u returns boolean
    /*
    *   For calculating Values with respect to levels
    */
    private function GetLevelValueR takes real base, real increment, integer level returns real
        return base + increment*level
    endfunction
    
    private function GetLevelValueI takes integer base, integer increment, integer level returns integer
        return base + increment*level
    endfunction
    /*
    *   For calculating value variance
    */
    private function GetVarianceR takes real base, real variance returns real
        if variance == 0 then
            return base
        endif
        return GetRandomReal(base - variance, base + variance)
    endfunction
    private function GetVarianceI takes integer base, integer variance returns integer
        if variance == 0 then
            return base
        endif
        return GetRandomInt(base - variance, base + variance)
    endfunction
    /*
    *   For calculating value over time 
    */
    private function LinearR takes real a, real b, real t returns real
        return a + (b - a)*t
    endfunction
    private function LinearI takes integer a, integer b, real t returns integer
        return R2I(LinearR(I2R(a), I2R(b), t))
    endfunction
    /**************************************
    *
    *   For getting the location surface z
    *
    **************************************/
    static if (not LIBRARY_ZLibrary) then
        globals
            private constant location p = Location(0.0, 0.0)
        endglobals
        function GetSurfaceZ takes real x, real y returns real
            call MoveLocation(p, x, y)
            return GetLocationZ(p)
        endfunction
    endif
    /*
    *   For handling lists and periods
    */
    //! textmacro DEVOURER_LIST_TIMER
        private static thistype array next
        private static thistype array prev
        private static constant timer t = CreateTimer()
        private static method insert takes code c returns thistype
            // allocate new instance
            local thistype this = allocate()
            // insert it to the list
            set next[this] = 0
            set prev[this] = prev[0]
            set next[prev[0]] = this
            set prev[0] = this
            // check if list contains only a single instance
            if prev[this] == 0 then
                // if true, start timer
                call TimerStart(t, TIMEOUT, true, c)
            endif 
            // return instance
            return this
        endmethod
        private method remove takes nothing returns nothing
            // dealloc instance
            call deallocate()
            // remove isntance from list
            set next[prev[this]] = next[this]
            set prev[next[this]] = prev[this]
        endmethod
    //! endtextmacro
    /*
    *   Structs for holding appearance values and position values
    */
    private struct Appearance
        integer red
        integer green
        integer blue
        integer alpha
        real scale
    endstruct
    private struct Vector
        real x
        real y
        real z
    endstruct
    /*
    *   Struct for timed appearances (fade, color change, etc.)
    */
    private struct TimedAppearance
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        // start and end appearances
        private Appearance start
        private Appearance end
        // timer 
        private real current
        private real max
        
        method destroy takes nothing returns nothing
            call remove()
            
            set u = null
            call start.destroy()
            call end.destroy()
            
            set current = 0
            set max = 0
        endmethod
        
        private static method period takes nothing returns nothing
            local thistype this = next[0]
            local real pct
            local integer a
            local integer r
            local integer g
            local integer b
            local real s
            loop
                exitwhen 0 == this
                if current < max then
                    set current = current + TIMEOUT
                    set pct = current/max
                    set a = LinearI(start.alpha, end.alpha, pct)
                    set r = LinearI(start.red, end.red, pct)
                    set g = LinearI(start.green, end.green, pct)
                    set b = LinearI(start.blue, end.blue, pct)
                    set s = LinearR(start.scale, end.scale, pct)
                    
                    call SetUnitVertexColor(u, r, g, b, a)
                    call SetUnitScale(u, s, 0, 0)
                else
                    call destroy()
                endif
                set this = next[this]
            endloop
        endmethod
        
        static method add takes unit temp, Appearance startColor, Appearance endColor, real maxTime returns nothing
            local thistype this = insert(function thistype.period)
            set start = startColor
            set end = endColor
            set current = 0
            set max = maxTime
            set u = temp
        endmethod
    endstruct
    /*
    *   Struct for effect dummies
    */
    private struct Particle
        readonly unit u
        readonly Vector pos
        private effect mdl
        static method create takes string sfx, real x, real y, real z, real face returns thistype
            local thistype this = allocate()
            set u = GetRecycledUnit(x, y, false, face)
            set pos = Vector.create()
            set pos.x = x
            set pos.y = y
            set pos.z = GetSurfaceZ(x, y) + z
            call SetUnitFlyHeight(u, z, 0)
            set mdl = AddSpecialEffectTarget(sfx, u, "origin")
            return this
        endmethod
        method destroy takes nothing returns nothing
            call AddRecycleTimer(u, 2.0)
            call DestroyEffect(mdl)
            set u = null
            set mdl = null
        endmethod
    endstruct
    /*
    *   Projectile motion
    */
    //! textmacro DEV_PROJECTILE
        private Vector offset
        
        private unit target
        private real size
        private real speed
        
        private real missileTime
        
        private boolean started
        
        private method destroyMissile takes nothing returns nothing
            if started then
                call onImpact(target)
                set target = null
                set speed = 0
                set missileTime = 0
                set started = false
            endif
        endmethod
        
        private method move takes nothing returns nothing
            local real ux 
            local real uy 
            local real uz 
            local real tx
            local real ty
            local real tz
            local real dx
            local real dy
            local real dz
            local real a2
            local real a3
            if started then
                set ux = GetUnitX(u)
                set uy = GetUnitY(u)
                set uz = GetSurfaceZ(ux, uy) + GetUnitFlyHeight(u) 
                if UnitAlive(target) then
                    set tx = GetUnitX(target)
                    set ty = GetUnitY(target)
                    set tz = GetSurfaceZ(tx, ty) + GetUnitFlyHeight(target)
                
                    set dx = tx - ux
                    set dy = ty - uy
                    set dz = tz - uz
                    if GetMagnitude3D(dx, dy, dz) <= size then
                        call destroyMissile()
                    endif
                
                    set a2 = Atan2(dy, dx)
                    set a3 = Atan2(dz, GetMagnitude2D(dx, dy))
                
                    set ux = ux + speed*Cos(a2)*Cos(a3) 
                    set uy = uy + speed*Sin(a2)*Cos(a3)
                    set uz = uz + speed*Sin(a3)
                
                    set ux = GetBoundedX(ux)
                    set uy = GetBoundedY(uy)
                    set uz = uz - GetSurfaceZ(ux, uy)
                
                    call SetUnitX(u, ux)
                    call SetUnitY(u, uy)
                    call SetUnitFlyHeight(u, uz, 0)
                    
                    call SetUnitFacing(u, a2*bj_RADTODEG)
                    
                    if GetUnitTypeId(u) == DUMMY_ID then
                        call SetUnitAnimationByIndex(u, R2I(a3*bj_RADTODEG + 90.5))
                    endif
                elseif missileTime > 0 then
                    set missileTime = missileTime - TIMEOUT
                    set ux = GetBoundedX(ux + offset.x)
                    set uy = GetBoundedY(uy + offset.y)
                    call SetUnitX(u, ux)
                    call SetUnitY(u, uy)
                    call SetUnitFlyHeight(u, (uz + offset.z) - GetSurfaceZ(ux, uy), 0) 
                else
                    call destroyMissile()
                endif
            endif
        endmethod
        
        private method setTarget takes unit temp returns nothing
            set target = temp
            set size = GetUnitCollision(temp)
        endmethod
        
        private method setTime takes real time returns nothing
            set missileTime = time
        endmethod
        
        private method start takes real vel, real angle2d, real angle3d returns nothing
            set offset = Vector.create()
            set speed = vel*TIMEOUT
            set offset.x = speed*Cos(angle2d)*Cos(angle3d)
            set offset.y = speed*Sin(angle2d)*Cos(angle3d)
            set offset.z = speed*Sin(angle3d)
            
            set started = true
        endmethod
    //! endtextmacro
    /*
    *   Struct for creeps
    */
    private struct Creep
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        private boolean isNova
        
        method onImpact takes unit temp returns nothing
            call remove()
            call RemoveUnit(u)
        endmethod
        
        //! runtextmacro DEV_PROJECTILE()
        
        private static method period takes nothing returns nothing
            local thistype this = next[0]
            loop
                exitwhen 0 == this
                call move()
                if isNova then
                    call SetUnitAnimation(u, NCREEP_ANIM)
                else
                    call SetUnitAnimation(u, ACREEP_ANIM)
                endif
                set this = next[this]
            endloop
        endmethod
        
        static method spawn takes player p, real x, real y, boolean aura returns nothing
            local thistype this = insert(function thistype.period)
            local Appearance start = Appearance.create()
            local Appearance end = Appearance.create()
            local real angle2d = GetRandomReal(-bj_PI, bj_PI)
            local real velocity
            local real time 
            local real z
            if aura then
                set start.red = ACREEP_START_RED
                set start.green = ACREEP_START_GREEN
                set start.blue = ACREEP_START_BLUE
                set start.alpha = ACREEP_START_ALPHA
                set start.scale = ACREEP_START_SCALE
                
                set end.red = ACREEP_END_RED
                set end.green = ACREEP_END_GREEN
                set end.blue = ACREEP_END_BLUE
                set end.alpha = ACREEP_END_ALPHA
                set end.scale = ACREEP_END_SCALE
                
                set velocity = GetVarianceR(ACREEP_SPEED, ACREEP_SPEED_VAR)
                
                set time = ACREEP_TIME
                
                set z = ACREEP_Z
            else
                set start.red = NCREEP_START_RED
                set start.green = NCREEP_START_GREEN
                set start.blue = NCREEP_START_BLUE
                set start.alpha = NCREEP_START_ALPHA
                set start.scale = NCREEP_START_SCALE
                
                set end.red = NCREEP_END_RED
                set end.green = NCREEP_END_GREEN
                set end.blue = NCREEP_END_BLUE
                set end.alpha = NCREEP_END_ALPHA
                set end.scale = NCREEP_END_SCALE
                
                set velocity = GetVarianceR(NCREEP_SPEED, NCREEP_SPEED_VAR)
                
                set time = NCREEP_TIME
                
                set z = NCREEP_Z
            endif
            set isNova = not aura
            
            set u = CreateUnit(p, CREEP_ID, x, y, angle2d*bj_RADTODEG)
            call PauseUnit(u, true)
            call UnitAddAbility(u, 'Aloc')
            if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
            endif
            call SetUnitFlyHeight(u, z, 0)
            
            call setTime(time)
            call start(velocity, angle2d, 0)
            
            call TimedAppearance.add(u, start, end, time)
        endmethod
    endstruct
    
    private struct Soul
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        private unit owner
        method onImpact takes unit temp returns nothing
        
        endmethod
        //! runtextmacro DEV_PROJECTILE()
    endstruct
    
    private struct Swarm
        //! runtextmacro DEVOURER_LIST_TIMER()
        private Table particles
    endstruct 
    
    private struct Shockwave
        //! runtextmacro DEVOURER_LIST_TIMER()
        private Table particles
    endstruct
    
    private struct Devourer
        //! runtextmacro DEVOURER_LIST_TIMER()
    endstruct
    
    private struct Shards
        //! runtextmacro DEVOURER_LIST_TIMER()
        private unit u
        private unit owner
        method onImpact takes unit temp returns nothing
        
        endmethod
        //! runtextmacro DEV_PROJECTILE()
    endstruct
    
    private struct SummonDevourer
        //! runtextmacro DEVOURER_LIST_TIMER()
        private Particle portal
        private real time
        
        private real spawnTime
        private real spawnTimer
        
        private integer level
        
        method destroy takes nothing returns nothing
        endmethod
        
        private static method period takes nothing returns nothing
            local thistype this = next[0]
            local integer i
            loop
                exitwhen 0 == this
                if time > 0 then
                    set time = time - TIMEOUT
                    if SHOW_SHARDS then
                        set spawnTime = spawnTime - TIMEOUT
                        if spawnTime >= 0 then
                            set spawnTimer = SHARD_PERIOD
                            set i = SHARD_COUNT
                            loop
                                exitwhen i == 0
                                
                                set i = i - 1
                            endloop
                        endif
                    endif
                endif
                set this = next[this]
            endloop
        endmethod
        
        static method onCast takes nothing returns boolean
            local thistype this
            local unit caster = GetTriggerUnit()
            local real x = GetSpellTargetX()
            local real y = GetSpellTargetY()
            local Appearance start
            local Appearance end 
            // if <cond> then
                set this = insert(function thistype.period)
                set level = GetUnitAbilityLevel(caster, ABIL_ID)
                set portal = Particle.create(PORTAL_MODEL, x, y, PORTAL_Z, 270)
                set time = GetLevelValueR(SPAWN_DELAY, SPAWN_DELAY_PER_LVL, level)
                
                set start.red = PORTAL_START_RED
                set start.green = PORTAL_START_GREEN
                set start.blue = PORTAL_START_BLUE
                set start.alpha = PORTAL_START_ALPHA
                set start.scale = PORTAL_START_SCALE
                
                set end.red = PORTAL_END_RED
                set end.green = PORTAL_END_GREEN
                set end.blue = PORTAL_END_BLUE
                set end.alpha = PORTAL_END_ALPHA
                set end.scale = PORTAL_END_SCALE
                
                call TimedAppearance.add(portal.u, start, end, time)
                
                if SHOW_SHARDS then
                    set spawnTime = SHARD_PERIOD
                endif
            return false
        endmethod
    endstruct
    
    private function Init takes nothing returns nothing
        call SetupDevourerId()
        
        call RegisterSpellEffectEvent(ABIL_ID, function SummonDevourer.onCast)
    endfunction
endscope

Currently, I am a bastard writing a code that doesn't even have a direction. Im doing it randomly ;V

I am currently writing the Spawn effect, so I might try uploading the GIF today or tomorrow
 
Last edited:
Level 13
Joined
Mar 29, 2012
Messages
530
WIP #2 (Concept)
attachment.php

That begin the summoning. =D

  • Ancient of Power Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- --------------------------- --------
      • -------- =========================== --------
      • Set AoP_Ability = Ancient of Power
      • Set AoP_Dummy = Ancient of Power Dummy
      • Set AoP_AbilityOrder = (Order(forceofnature))
      • Set AoP_AbilityLevels = 3
      • -------- =========================== --------
      • Set AoP_SummoningDuration[1] = 10.00
      • Set AoP_SummoningDuration[2] = 10.00
      • Set AoP_SummoningDuration[3] = 10.00
      • Set AoP_SummoningPowerThreshold[1] = 2600.00
      • Set AoP_SummoningPowerThreshold[2] = 3400.00
      • Set AoP_SummoningPowerThreshold[3] = 4000.00
      • Set AoP_SummoningStartSFX[1] = war3mapImported\GreenRing.mdl
      • Set AoP_SummoningStartSFX[2] = war3mapImported\PoisonStream.mdl
      • Set AoP_SummoningStartSFX[3] = war3mapImported\Ragingslam.mdl
      • Set AoP_SummoningSFX = war3mapImported\HarvestLife.mdl
      • Set AoP_sSFXFinalSize[1] = 1.00
      • Set AoP_sSFXFinalSize[2] = 1.15
      • Set AoP_sSFXFinalSize[3] = 1.30
      • Set AoP_sSFXHeight = 0.00
      • Set AoP_sGroundSFX = Abilities\Spells\NightElf\EntanglingRoots\EntanglingRootsTarget.mdl
      • Set AoP_sGroundSFXInterval = 0.05
      • Set AoP_sGroundSFXStart = 100.00
      • Set AoP_sGroundSFXSpeed = 480.00
      • Set AoP_sGroundSFXDensity = 2.00
      • Set AoP_sGroundSFXArea[1] = 300.00
      • Set AoP_sGroundSFXArea[2] = 300.00
      • Set AoP_sGroundSFXArea[3] = 300.00
      • Set AoP_sEdgeSFX = Objects\Spawnmodels\NightElf\EntBirthTarget\EntBirthTarget.mdl
      • Set AoP_sEdgeSFXInterval = 0.08
      • Set AoP_sEdgeSFXStart[1] = 200.00
      • Set AoP_sEdgeSFXStart[2] = 200.00
      • Set AoP_sEdgeSFXStart[3] = 200.00
      • Set AoP_sEdgeSFXSpeed = 480.00
      • Set AoP_sEdgeSFXSpinSpeed = 160.00
      • Set AoP_sEdgeSFXDensity = 0.75
      • Set AoP_sEdgeSFXArea[1] = 500.00
      • Set AoP_sEdgeSFXArea[2] = 500.00
      • Set AoP_sEdgeSFXArea[3] = 500.00
      • Set AoP_sEdgeEndSFX = war3mapImported\GreenHeal.mdx
      • -------- =========================== --------
      • Set AoP_SummonedUnit[1] = Tree of Life
      • Set AoP_SummonedUnit[2] = Tree of Ages
      • Set AoP_SummonedUnit[3] = Tree of Eternity
      • Set AoP_suFacingAngle = 270.00
      • Set AoP_suBirthAnimation = Stand
      • -------- =========================== --------
      • Set AoP_AbsorbInterval = 1.00
      • Set AoP_LifeDrain = 30.00
      • Set AoP_ManaDrain = 15.00
      • Set AoP_LifePower = 1.00
      • Set AoP_ManaPower = 2.00
      • -------- =========================== --------
      • Set AoP_ManaOrbModel = war3mapImported\Power_Orb.mdl
      • Set AoP_mOrbSize = 0.50
      • Set AoP_mOrbSpawnHeight = 150.00
      • Set AoP_mOrbSpawnOffset = 100.00
      • Set AoP_mOrbTravelTime = 1.00
      • Set AoP_mOrbArc = 0.75
      • Set AoP_mOrbCancelSFX = war3mapImported\Unleash the power.mdx
      • Set AoP_mOrbCancelSFXSize = 1.00
      • Set AoP_mOrbCancelSFXDeath = 1.00
      • -------- =========================== --------
      • Custom script: call ExecuteFunc("AoP_Initialize")
JASS:
constant function AoP_DummiesOwner takes nothing returns player
    return Player(PLAYER_NEUTRAL_PASSIVE)
endfunction

constant function AoP_FlyAbilityID takes nothing returns integer
    return 'Amrf'
endfunction

constant function AoP_FrameUpdate takes nothing returns real
    return 1 / 32.
endfunction

function AoP_IsUnitAlive takes unit u returns boolean
    return GetUnitTypeId(u) != 0 and not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction

function AoP_IsUnitChanneling takes unit u returns boolean
    return AoP_IsUnitAlive(u) and GetUnitCurrentOrder(u) == udg_AoP_AbilityOrder
endfunction

function AoP_GetMagnitude2D takes real x, real y returns real
    return SquareRoot(x*x+y*y)
endfunction

function AoP_GetMagnitude3D takes real x, real y, real z returns real
    return SquareRoot(x*x+y*y+z*z)
endfunction

function AoP_GetAngle takes real x, real y returns real
    return Atan2(y, x)
endfunction

function AoP_GetAngleZ takes real distance2D, real z returns real
    return Atan2(z, distance2D)
endfunction

function AoP_GetParabolaHeight takes real y0, real y1, real h, real d, real x returns real
    local real A = (2*(y0+y1)-4*h)/(d*d)
    local real B = (y1-y0-A*d*d)/d
    
    return A*x*x + B*x + y0
endfunction

function AoP_GetTerrainZ takes real x, real y returns real
    call MoveLocation(udg_AoP_ZLocator, x, y)
    return GetLocationZ(udg_AoP_ZLocator)
endfunction

function AoP_GetUnitZAlt takes unit u, real x, real y returns real
    return GetUnitFlyHeight(u) + AoP_GetTerrainZ(x, y)
endfunction

function AoP_UnitApplyFly takes unit u returns nothing
    if UnitAddAbility(u, AoP_FlyAbilityID()) and UnitRemoveAbility(u, AoP_FlyAbilityID()) then
    endif
endfunction

function AoP_SetUnitZAlt takes unit u, real z, real x, real y returns nothing
    call SetUnitFlyHeight(u, z - AoP_GetTerrainZ(x, y), 0)
endfunction

function AoP_SetUnitPosition takes unit u, real x, real y returns nothing
    call SetUnitX(u, x)
    call SetUnitY(u, y)
endfunction

function AoP_Destroy takes integer index returns nothing
    set udg_AoP_Recycle[udg_AoP_Recyclable] = index
    set udg_AoP_Recyclable = udg_AoP_Recyclable + 1
    set udg_AoP_Next[udg_AoP_Prev[index]] = udg_AoP_Next[index]
    set udg_AoP_Prev[udg_AoP_Next[index]] = udg_AoP_Prev[index]
    
    if udg_AoP_Next[0] == 0 then
        call PauseTimer(udg_AoP_Timer)
    endif
endfunction

function AoP_Create takes nothing returns integer
    local integer newIndex
    if udg_AoP_Recyclable == 0 then
        set udg_AoP_MaxIndex = udg_AoP_MaxIndex + 1
        set newIndex = udg_AoP_MaxIndex
    else
        set udg_AoP_Recyclable = udg_AoP_Recyclable - 1
        set newIndex = udg_AoP_Recycle[udg_AoP_Recyclable]
    endif
    set udg_AoP_Next[newIndex] = 0
    set udg_AoP_Next[udg_AoP_Prev[0]] = newIndex
    set udg_AoP_Prev[newIndex] = udg_AoP_Prev[0]
    set udg_AoP_Prev[0] = newIndex
    
    return newIndex
endfunction

function AoP_CreateParticle takes string model, real x, real y, real z, real size, real dur returns nothing
    local integer pIndex = AoP_Create()
    
    set udg_AoP_Particle[pIndex] = CreateUnit(AoP_DummiesOwner(), udg_AoP_Dummy, x, y, 0)
    call AoP_SetUnitPosition(udg_AoP_Particle[pIndex], x, y)
    call AoP_UnitApplyFly(udg_AoP_Particle[pIndex])
    call AoP_SetUnitZAlt(udg_AoP_Particle[pIndex], z, x, y)
    call SetUnitScale(udg_AoP_Particle[pIndex], size, 0, 0)
    set udg_AoP_AttachedModel[pIndex] = AddSpecialEffectTarget(model, udg_AoP_Particle[pIndex], "origin")
    
    set udg_AoP_realTimer[pIndex] = dur
    
    set udg_AoP_Stage[pIndex] = 5
endfunction

function AoP_CreateParticleTarget takes string model, unit u, real size, real dur returns nothing
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    call AoP_CreateParticle(model, x, y, AoP_GetUnitZAlt(u, x, y), size, dur)
endfunction

function AoP_StartSummoningEffect takes real x, real y, integer lvl returns nothing
    local integer eIndex = AoP_Create()
    local integer i = 0
    
    loop
        set i = i + 1
        exitwhen udg_AoP_SummoningStartSFX[i] == null
        
        call DestroyEffect(AddSpecialEffect(udg_AoP_SummoningStartSFX[i], x, y))
    endloop
    
    set udg_AoP_TargetX[eIndex] = x
    set udg_AoP_TargetY[eIndex] = y
    set udg_AoP_Level[eIndex] = lvl
    set udg_AoP_Dist[eIndex] = udg_AoP_sGroundSFXStart
    set udg_AoP_Dist2[eIndex] = udg_AoP_sEdgeSFXStart[udg_AoP_Level[eIndex]]
    set udg_AoP_Angle[eIndex] = GetRandomReal(-bj_PI, bj_PI)
    set udg_AoP_realTimer[eIndex] = 0
    set udg_AoP_realTimer2[eIndex] = 0
    
    set udg_AoP_Stage[eIndex] = 4
endfunction

function AoP_Periodic takes nothing returns nothing
    local real x
    local real y
    local real z
    local integer i
    local integer index = 0
    
    loop
        set index = udg_AoP_Next[index]
        exitwhen index == 0
        
        if udg_AoP_Stage[index] == 1 then
            
            if AoP_IsUnitChanneling(udg_AoP_Caster[index]) then
                if udg_AoP_Dist2[index] < udg_AoP_Dist[index] then
                    set udg_AoP_Dist2[index] = udg_AoP_Dist2[index] + udg_AoP_Speed[index]
                    set udg_AoP_Dist[0] = udg_AoP_Dist2[index] - udg_AoP_Dist[index] // Opposite to angle
                    set x = udg_AoP_TargetX[index] + udg_AoP_Dist[0] * Cos(udg_AoP_Angle[index])
                    set y = udg_AoP_TargetY[index] + udg_AoP_Dist[0] * Sin(udg_AoP_Angle[index])
                    set z = AoP_GetParabolaHeight(udg_AoP_SourceZ[index], udg_AoP_TargetZ[index], udg_AoP_MaxHeight[index], udg_AoP_Dist[index], udg_AoP_Dist2[index])
                    call SetUnitX(udg_AoP_Missile[index], x)
                    call SetUnitY(udg_AoP_Missile[index], y)
                    call AoP_SetUnitZAlt(udg_AoP_Missile[index], z, x, y)
                else
                    call DestroyEffect(udg_AoP_AttachedModel[index])
                    call KillUnit(udg_AoP_Missile[index])
                    
                    call AoP_StartSummoningEffect(udg_AoP_TargetX[index], udg_AoP_TargetY[index], udg_AoP_Level[index])
                    
                    set udg_AoP_SummoningEffect[index] = CreateUnit(udg_AoP_Owner[index], udg_AoP_Dummy, udg_AoP_TargetX[index], udg_AoP_TargetY[index], 0)
                    call AoP_SetUnitPosition(udg_AoP_SummoningEffect[index], udg_AoP_TargetX[index], udg_AoP_TargetY[index])
                    call AoP_UnitApplyFly(udg_AoP_SummoningEffect[index])
                    call SetUnitFlyHeight(udg_AoP_SummoningEffect[index], udg_AoP_sSFXHeight, 0)
                    call SetUnitScale(udg_AoP_SummoningEffect[index], 0, 0, 0)
                    set udg_AoP_AttachedModel[index] = AddSpecialEffectTarget(udg_AoP_SummoningSFX, udg_AoP_SummoningEffect[index], "origin")
                    
                    set udg_AoP_Dist[index] = 0
                    
                    set udg_AoP_Stage[index] = 2
                endif
            else
                // --- When caster stopped channeling
                call DestroyEffect(udg_AoP_AttachedModel[index])
                call KillUnit(udg_AoP_Missile[index])
                call AoP_CreateParticleTarget(udg_AoP_mOrbCancelSFX, udg_AoP_Missile[index], udg_AoP_mOrbCancelSFXSize, udg_AoP_mOrbCancelSFXDeath)
                call AoP_Destroy(index)
            endif
            
        elseif udg_AoP_Stage[index] == 2 then
            
            if AoP_IsUnitChanneling(udg_AoP_Caster[index]) then
                if udg_AoP_Dist[index] < udg_AoP_sSFXFinalSize[udg_AoP_Level[index]] then
                    
                else
                    
                endif
            else
                // --- When caster stopped channeling
                call AoP_Destroy(index)
            endif
            
        elseif udg_AoP_Stage[index] == 4 then
            
            if udg_AoP_Dist[index] < udg_AoP_sGroundSFXArea[udg_AoP_Level[index]] then
                set udg_AoP_Dist[index] = udg_AoP_Dist[index] + udg_AoP_sGroundSFXSpeed
                set udg_AoP_realTimer[index] = udg_AoP_realTimer[index] - AoP_FrameUpdate()
                if udg_AoP_realTimer[index] <= 0 then
                    set udg_AoP_Angle[0] = GetRandomReal(-bj_PI, bj_PI)
                    set i = udg_AoP_sGroundSFXCount
                    loop
                        exitwhen i <= 0
                        
                        set udg_AoP_Angle[0] = udg_AoP_Angle[0] + udg_AoP_sGroundSFXGap
                        set x = udg_AoP_TargetX[index] + udg_AoP_Dist[index] * Cos(udg_AoP_Angle[0])
                        set y = udg_AoP_TargetY[index] + udg_AoP_Dist[index] * Sin(udg_AoP_Angle[0])
                        call DestroyEffect(AddSpecialEffect(udg_AoP_sGroundSFX, x, y))
                        
                        set i = i - 1
                    endloop
                    
                    set udg_AoP_realTimer[index] = udg_AoP_sGroundSFXInterval
                endif
            endif
            
            if udg_AoP_Dist2[index] < udg_AoP_sEdgeSFXArea[udg_AoP_Level[index]] then
                set udg_AoP_Dist2[index] = udg_AoP_Dist2[index] + udg_AoP_sEdgeSFXSpeed
            else
                set udg_AoP_Dist2[index] = udg_AoP_sEdgeSFXArea[udg_AoP_Level[index]]
            endif
            set udg_AoP_Angle[index] = udg_AoP_Angle[index] + udg_AoP_sEdgeSFXSpinSpeed
            set udg_AoP_realTimer2[index] = udg_AoP_realTimer2[index] - AoP_FrameUpdate()
            if udg_AoP_realTimer2[index] <= 0 then
                set udg_AoP_Angle[0] = udg_AoP_Angle[index]
                set i = udg_AoP_sEdgeSFXCount
                loop
                    exitwhen i <= 0
                    
                    set udg_AoP_Angle[0] = udg_AoP_Angle[0] + udg_AoP_sEdgeSFXGap
                    set x = udg_AoP_TargetX[index] + udg_AoP_Dist2[index] * Cos(udg_AoP_Angle[0])
                    set y = udg_AoP_TargetY[index] + udg_AoP_Dist2[index] * Sin(udg_AoP_Angle[0])
                    call DestroyEffect(AddSpecialEffect(udg_AoP_sEdgeSFX, x, y))
                    if udg_AoP_Dist2[index] >= udg_AoP_sEdgeSFXArea[udg_AoP_Level[index]] then
                        
                    endif
                    
                    set i = i - 1
                endloop
                
                set udg_AoP_realTimer2[index] = udg_AoP_sEdgeSFXInterval
            endif
            
        elseif udg_AoP_Stage[index] == 5 then
            
            set udg_AoP_realTimer[index] = udg_AoP_realTimer[index] - AoP_FrameUpdate()
            if udg_AoP_realTimer[index] <= 0 then
                call DestroyEffect(udg_AoP_AttachedModel[index])
                call KillUnit(udg_AoP_Particle[index])
                call AoP_Destroy(index)
            endif
            
        endif
    endloop
endfunction

function AoP_onEffect takes nothing returns boolean
    local real sx
    local real sy
    local real stx
    local real sty
    local integer cIndex
    
    if GetSpellAbilityId() == udg_AoP_Ability then
        set cIndex = AoP_Create()
        
        set udg_AoP_Caster[cIndex] = GetTriggerUnit()
        set udg_AoP_Owner[cIndex] = GetTriggerPlayer()
        set udg_AoP_Level[cIndex] = GetUnitAbilityLevel(udg_AoP_Caster[cIndex], udg_AoP_Ability)
        
        set sx = GetUnitX(udg_AoP_Caster[cIndex])
        set sy = GetUnitY(udg_AoP_Caster[cIndex])
        set udg_AoP_SourceZ[cIndex] = AoP_GetTerrainZ(sx, sy) + udg_AoP_mOrbSpawnHeight
        
        set udg_AoP_TargetX[cIndex] = GetSpellTargetX()
        set udg_AoP_TargetY[cIndex] = GetSpellTargetY()
        set udg_AoP_TargetZ[cIndex] = AoP_GetTerrainZ(udg_AoP_TargetX[cIndex], udg_AoP_TargetY[cIndex])
        
        set stx = udg_AoP_TargetX[cIndex] - sx
        set sty = udg_AoP_TargetY[cIndex] - sy
        set udg_AoP_Angle[cIndex] = AoP_GetAngle(stx, sty)
        
        set udg_AoP_TargetX[0] = sx + udg_AoP_mOrbSpawnOffset * Cos(udg_AoP_Angle[cIndex])
        set udg_AoP_TargetY[0] = sy + udg_AoP_mOrbSpawnOffset * Sin(udg_AoP_Angle[cIndex])
        set udg_AoP_Dist[cIndex] = AoP_GetMagnitude2D(stx, sty) - udg_AoP_mOrbSpawnOffset
        set udg_AoP_Dist2[cIndex] = 0
        set udg_AoP_Speed[cIndex] = udg_AoP_Dist[cIndex] / udg_AoP_mOrbTravelTime
        set udg_AoP_MaxHeight[cIndex] = udg_AoP_Dist[cIndex] * udg_AoP_mOrbArc + udg_AoP_TargetZ[cIndex]
        
        set udg_AoP_Missile[cIndex] = CreateUnit(AoP_DummiesOwner(), udg_AoP_Dummy, udg_AoP_TargetX[0], udg_AoP_TargetY[0], udg_AoP_Angle[cIndex]*bj_RADTODEG)
        call AoP_UnitApplyFly(udg_AoP_Missile[cIndex])
        call AoP_SetUnitZAlt(udg_AoP_Missile[cIndex], udg_AoP_SourceZ[cIndex], udg_AoP_TargetX[0], udg_AoP_TargetY[0])
        call SetUnitScale(udg_AoP_Missile[cIndex], udg_AoP_mOrbSize, 0, 0)
        set udg_AoP_AttachedModel[cIndex] = AddSpecialEffectTarget(udg_AoP_ManaOrbModel, udg_AoP_Missile[cIndex], "origin")
        
        set udg_AoP_Stage[cIndex] = 1
        
        if udg_AoP_Prev[cIndex] == 0 then
            call TimerStart(udg_AoP_Timer, AoP_FrameUpdate(), true, function AoP_Periodic)
        endif
    endif
    
    return false
endfunction

function AoP_Initialize takes nothing returns nothing
    local integer i = 0
    
    loop
        set i = i + 1
        exitwhen i > udg_AoP_AbilityLevels
        
        
    endloop
    
    set udg_AoP_mOrbTravelTime = udg_AoP_mOrbTravelTime / AoP_FrameUpdate()
    
    set udg_AoP_sGroundSFXCount = R2I(4*udg_AoP_sGroundSFXDensity)
    set udg_AoP_sGroundSFXGap = 360 / udg_AoP_sGroundSFXCount * bj_DEGTORAD
    set udg_AoP_sGroundSFXSpeed = udg_AoP_sGroundSFXSpeed * AoP_FrameUpdate()
    
    set udg_AoP_sEdgeSFXCount = R2I(4*udg_AoP_sEdgeSFXDensity)
    set udg_AoP_sEdgeSFXGap = 360 / udg_AoP_sEdgeSFXCount * bj_DEGTORAD
    set udg_AoP_sEdgeSFXSpeed = udg_AoP_sEdgeSFXSpeed * AoP_FrameUpdate()
    set udg_AoP_sEdgeSFXSpinSpeed = udg_AoP_sEdgeSFXSpinSpeed * AoP_FrameUpdate() * bj_DEGTORAD
    
    if udg_AoP_ZLocator == null then
        set udg_AoP_ZLocator = Location(0, 0)
    endif
endfunction

//===========================================================================
function InitTrig_Ancient_of_Power takes nothing returns nothing
    set gg_trg_Ancient_of_Power = CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Ancient_of_Power, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(gg_trg_Ancient_of_Power, Condition(function AoP_onEffect))
endfunction
 

Attachments

  • WIP #2.gif
    WIP #2.gif
    1.3 MB · Views: 182
Status
Not open for further replies.
Top