• 🏆 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 20
Joined
Apr 14, 2012
Messages
2,901
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

With this I was sent a grim reminder: my spell fits nicely with the code and concept crtieria; I completely forgot about the visuals lol

Btw Ofel, thats some eyegasm sht right there xD
 
Level 22
Joined
Aug 27, 2013
Messages
3,973
Just got my free time, still playing with the effects. Goddamn it, I still can't find a good combination to make it good-looking. ;_;
Anyway, I changed my concept. This one might change again later but I hope not.

Hell's Gate
Performs a dark ritual to summon powerful demons. The ritual begins by summoning three orbs of soul. These orbs will be the power source for opening the gate. The ritual will be cancelled if one of the orbs is destroyed.​
XzghumI.gif
 
Level 17
Joined
Sep 8, 2007
Messages
994
Final Submission

Check the changelog for updates, because I keep updating things.


Void Undertow

icons_552_btn.jpg


Description

Preview

Code

Changelog

attachment.php

Preview 1

Preview 2

Preview 3

zephyr-gif.241601
zephyr2-gif.241604
zephyr3-gif.241606
JASS:
scope VoidUndertow initializer Init
/* *********************************************************************
*   VOID UNTERTOW                                         by xxdingo93xx
*
*   created for HWS-ZephyrChallange #14
*
*   Icon credits:
*       - BlizzardEntertainment ([url]http://www.hiveworkshop.com/forums/icons-541/btnspell_shadow_summonvoidwalker-54578/?prev=search%3Dvoid%26d%3Dlist%26r%3D20[/url])
*
*   Model credits:
*       - PeeKay ([url]http://www.hiveworkshop.com/forums/models-530/peekays-punishmissle-model-202088/?prev=search%3Ddarkness%26r%3D20%26d%3Dlist%26page%3D2[/url])
*       - alfredx_sotn ([url]http://www.hiveworkshop.com/forums/models-530/waterelemental-47544/?prev=search%3Dvoid%26d%3Dlist%26r%3D20[/url])
*       - JesusHipster ([url]http://www.hiveworkshop.com/forums/models-530/guardian-wisp-228249/?prev=search%3Dwisp%26d%3Dlist%26r%3D20[/url])
*       - Vortigon ([url]http://www.hiveworkshop.com/forums/models-530/dark-orb-257313/?prev=search%3Ddark%2520orb%26d%3Dlist%26r%3D20[/url])
*       - ILH ([url]http://www.hiveworkshop.com/forums/models-530/twilight-sparkle-250103/?prev=d%3Dlist%26r%3D20%26t%3D8[/url])
*       - Vexorian (for the dummy.mdx imported model)
*
*   Required libraries:
*       - TimerUtils (by Vexorian)
*       - Table (by Bribe)
*       - RegisterPlayerUnitEvent (by Magtheridon96)
*
*   If you want to use this spell in your map, you will need to undergo a
*   a few steps in order to make it work properly and customized to your
*   wishes. Just follow the steps mentioned below, but keep in mind that I
*   used a bunch of imported models for this spell. You might want to copy
*   them, as well.
*
*   Step 1: give credits if you use this ;-)
*   Step 2: copy the necessary units into your map:
*       - Netherworld Creature(Soul Undertow Level 1,2,3)
*       - Dummy (don't forget to copy the imported dummy.mdl as well)
*       - Dummy (rotated)
*   Step 3: copy the ability "Void Undertow" into your map.
*   Step 4: customize the globals to your likings:
*
***********************************************************************/
    globals
        // ========= RAWCODE SETTINGS =========
        //Default: 'n000'
        private constant integer    DUMMYID            = 'n000' //the rawcode of the regular dummy unit
        //Default: 'n001'
        private constant integer    SOUL_ID            = 'n001' //rawcode of the rotated dummy unit
        //Default: 'VoUn'
        private constant integer    AID                = 'VoUn' //the ability's rawcode
       
        // ========= GENERAL SETTINGS =========
        //Default: 1./32.
        private constant real TICK                     = 1./32. //The periodic time to run a trigger
        //Default: WEAPON_TYPE_WHOKNOWS
        private constant weapontype WT                 = WEAPON_TYPE_WHOKNOWS //weapontype of damage dealt
        //Default: ATTACK_TYPE_MAGIC
        private constant attacktype AT                 = ATTACK_TYPE_MAGIC
        //Default: DAMAGE_TYPE_UNKNOWN
        private constant damagetype DT                 = DAMAGE_TYPE_UNKNOWN
       
        ////////////////////////////////////////////////
        //    =========  VISUAL SETTINGS  =========
        // I tried to keep the order of the visual   
        // settings just like the order of the spell's
        // components, to make it easier to follow what  
        //                 you're doing.
        ////////////////////////////////////////////////
       
        // *** CIRCULAR SPAWN ***
        //
        //Default: 400.
        private constant real       SPAWN_DISTANCE      = 400.
        //Default: 3
        private constant integer    SPAWN_PART_CNT      = 3 //amount of particles being spawned
        //Default: 7.
        private constant real       SPAWN_SPINSPEED     = 7.  //radian-mode, so keep this value  -2*PI <= x <= 2*PI
        //Default: 45.
        private constant real       SPAWN_START_ANGLE   = 45. //degree-mode
        //Default: 1.
        private constant real       SPAWN_DURATION      = 1. //duration of this whole process. Will determine the movespeed
        //Default: "Abilities\\Spells\\NightElf\\FaerieDragonInvis\\FaerieDragon_Invis.mdl"
        private constant string     SPAWN_MODEL         = "Abilities\\Spells\\NightElf\\FaerieDragonInvis\\FaerieDragon_Invis.mdl" //"Abilities\\Spells\\NightElf\\FaerieDragonInvis\\FaerieDragon_Invis.mdl" //the particles modelpath
        //Default: "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
        private constant string     SPAWN_COL_MODEL     = "war3mapImported\\TwilightSparkle.mdx"//"Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl" //the collision modelpath
        //Default: 300.
        private constant real       SPAWN_START_Z       = 300. //starting height, will sink to SPAWN_END_Z over time
        //Default: 50.
        private constant real       SPAWN_END_Z         = 50.
       
        // *** UNIT IMAGE ***
        //
        //Default: "Abilities\\Spells\\Undead\\RegenerationAura\\ObsidianRegenAura.mdl"
        private constant string     IMAGE_MODELPATH     = "Abilities\\Spells\\Undead\\RegenerationAura\\ObsidianRegenAura.mdl" //unit image
        //Default: 1.
        private constant real       IMAGE_GROWTH_DURA   = 1. //duration of the effect
        //Default: 0.
        private constant real       IMAGE_HEIGHT        = 0. //the effect's height
        //Default: 3.
        private constant real       IMAGE_MAX_SIZE      = 2. //the scaling value the effect will grow up to overtime
       
       
        // *** VOID ORB ***
        //
        //Default: "war3mapImported\\DarkOrb.mdl"
        private constant string     VOIDORB_MODEL       = "war3mapImported\\DarkOrb.mdl" //ID of the dummy unit representing the voidorb
        //Default: 1.
        private constant real       VOIDORB_SIZE        = 0.1 //scaling value of orb. WARNING:
                                                         //if you use the default for VOIDORB_MODEL, increasing the size won't
                                                         //make the orb bigger but it will tremble instead. Kinda interesting,
                                                         //but it's up to you if go for it
        //Default: 4.
        private constant real       VOIDORB_SPEED       = 4. //orb's speed
        //Default: 1.05
        private constant real       VOIDORB_ACC         = 1.02 //orb's accumulation. Value will be multiplied with the voidorb_speed every TICK
        //Default 60.
        private constant real       VOIDORB_COL         = 60. //collision range between caster and orb
        //Default: 50.
        private constant real       VOIDORB_END_Z       = 50. //orb's height. Also DrainOrb's.
        //Default: 2.
        private constant real       VOIDORB_DELAY       = 0.4 // delay until the orb starts moving
       
       
        // *** DRAIN ORB ***
        //
        //Default: "war3mapImported\\Wisp.mdl"
        private constant string     DRAINORB_MODEL      = "war3mapImported\\Wisp.mdl" //
        //Default: 0.8
        private constant real       DRAINORB_SIZE       = 0.8
        //Default: 255
        private constant integer    DRAINORB_RED        = 255 //RGB value
        //Default: 255
        private constant integer    DRAINORB_BLUE       = 255 //RGB value
        //Default: 0
        private constant integer    DRAINORB_GREEN      = 0 //RGB value
        //Default: 255
        private constant integer    DRAINORB_ALPHA      = 255 //transparency
        //Default: 28.
        private constant real       DRAINORB_MAX_SPEED  = 24. //the maximum movespeed...
        //Default: 26.
        private constant real       DRAINORB_MIN_SPEED  = 28. //...and minimum movespeed, so the movespeed will vary a little.
        //Default: 1.
        private constant real       DRAINORB_ACC        = 1. //accumulation for drainorbs
        //Default: 60.
        private constant real       DRAINORB_COL        = 60. //collision range for drainorb <-> voidorb
        //Default 50.
        private constant real       DRAINORB_Z          = 50.
        //Default: 16.
        private constant real       DRAINORB_ZOFFSET    = 16. //fixing value to spawn the orb in the
                                                             //center of the target's body
       
        // *** LOCKED SUMMON ***
        //
        //Default: 60.
        private constant real       SUMMON_BACK_RNG     = 60. //the distance the summoned unit is behind the summoner
        //Default: 60.
        private constant real       SUMMON_BACK_HGH     = 60. //the flying height for the summoned unit
        //Default: "birth"
        private constant string     SUMMON_ANI          = "birth" //the summoned unit's animation on summoning
        //Default: 1.
        private constant real       SUMMON_ANI_RESET    = 1. //after this amount of seconds the unit's animation will be reset
        //Default: "war3mapImported\\VoidWalker.mdl"
        private constant string     SUMMON_MODELPATH    = "war3mapImported\\VoidWalker.mdl"//for the unit-image effect spawned on the actual summoned unit.
        //Default: 4
        private constant real       SUMMON_MAX_SIZE     = 4. //size of unit-image effect on summoned unit
        //Default: 1
        private constant real       SUMMON_GROWTH_DURA  = 1.
       
       
        // *** SOUL LIFETIME ***
        //
        //Default: 6
        private constant integer    SOUL_AMT            = 5 //amount of shards being spawned over the summoned unit
        //Default: 1.5
        private constant real       SOUL_MAX_SCALE      = 1.5//starting scaling value for the soul
        //Default: 0.5
        private constant real       SOUL_MIN_SCALE      = 0.5 //threshold for shrinking value.
                                                          //Once the scaling value has fallen below this threhsold, the soul vanishes.
                                                          //If you don't want any shrinking-effect whatsoever, give it the same value
                                                          //that SOUL_MAX_SCALE has.
        //Default: '75.
        private constant real       SOUL_RADIUS         = 100. //the radius determining the distance between souls and summoned unit
        //Default: 16.
        private constant real       SOUL_XOFFSET        = 16. //fixing value, since the souls apparently aren't spawned
                                                          //in the very center X-value of the summoned unit
                                                          //I suggest you leave this untouched, unless you know what you're doing
        //Default: 75.
        private constant real       SOUL_HEIGHT         = 100. //the base height for the souls
        //Default: 135.
        private constant real       SOUL_SLOPE          = 135.//the souls are spawned on a rotated plane in the XY-axis. This determines its degree
   
        ////////////////////////////////////////////////
        //    ========= GAMEPLAY SETTINGS =========     
        //  In this part you must determine gameplay-
        //              specific values.
        ////////////////////////////////////////////////

        //Default: 3
        private constant integer ABILITY_LEVELS = 3 //The amount of levels the spell can have
       
        //check the function Settings down below
        //to change spell-specific values.
        private integer array SUMMON_ID[ABILITY_LEVELS]
        private real array DRAINORB_RNG[ABILITY_LEVELS]
        private real array DRAINORB_DMG[ABILITY_LEVELS]
        private real array AVG_DMG_PER_UNIT[ABILITY_LEVELS]
        private real array SUMMON_LIFE_FAC[ABILITY_LEVELS]
        private real array SUMMON_HP_THRESHOLD[ABILITY_LEVELS]
        private real array  SUMMON_ATK_RANGE[ABILITY_LEVELS]
       
    endglobals

    private function Settings takes nothing returns nothing
        //the id of the summoned unit, just
        //in case you want a stronger or another unit to be summoned each level.
        set SUMMON_ID[0] = 'nVO1'//Level 1
        set SUMMON_ID[1] = 'nVO2'//Level 2
        set SUMMON_ID[2] = 'nVO3'//Level 3
       
        //must be the same attack range the corresponding summoned unit has.
        set SUMMON_ATK_RANGE[0] = 500.
        set SUMMON_ATK_RANGE[1] = 500.
        set SUMMON_ATK_RANGE[2] = 500.
       
        //the hitpoints one drainorb will steal
        set DRAINORB_DMG[0] = 15. //Level 1...
        set DRAINORB_DMG[1] = 15. //...you get the idea.
        set DRAINORB_DMG[2] = 15.
       
        //the average damage dealt to a unit by drainorbs
        //remember: it's AVERAGE, so it can vary around this value
        set AVG_DMG_PER_UNIT[0] = 50.
        set AVG_DMG_PER_UNIT[1] = 60.
        set AVG_DMG_PER_UNIT[2] = 70.
       
        //the detection range for the voidorb to spawn drainorbs
        set DRAINORB_RNG[0] = 400.
        set DRAINORB_RNG[1] = 450.
        set DRAINORB_RNG[2] = 500.
       
        // for every SUMMON_LIFE_FAC[i] drained hitpoints add 1 second lifetime
        set SUMMON_LIFE_FAC[0] = 20
        set SUMMON_LIFE_FAC[1] = 15
        set SUMMON_LIFE_FAC[2] = 10
       
        //at least SUMMON_HP_THRESHOLD[i] hitpoints need to be drained in order to summon
        set SUMMON_HP_THRESHOLD[0] = 30.
        set SUMMON_HP_THRESHOLD[1] = 40.
        set SUMMON_HP_THRESHOLD[2] = 50.
    endfunction
//===========================================================================
//===========================================================================
//===========================================================================
    globals
        private group TempGroup = CreateGroup()
        private location TempLoc = Location(0.,0.)
        private unit TempUnit = null
       
        //we use a table so that we can check whether
        //the casting unit has summoned a creature already.
        private Table LockedSummonTab
        private Table VoidOrbTab
    endglobals
   
    native UnitAlive takes unit u returns boolean
   
    //drainorb-targets must be: alive, non-structure, non-magic-immune, non-mechanical, enemied
    private function Enum takes nothing returns boolean
        local boolean result = UnitAlive(GetFilterUnit()) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)
        set result = result and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL)
        set result = result and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempUnit))
        return result
    endfunction
   
    private function A2PXY takes real x,real y,real xt,real yt returns real
        local real angle = bj_RADTODEG*Atan2(yt-y,xt-x)
        local real modulus = angle - I2R(R2I(angle / 360.)) * 360.
        if modulus < 0 then
            set modulus = modulus + 360.
        endif
        return modulus
    endfunction
   
    private function D2PXY takes real x1, real y1, real x2, real y2 returns real
        return SquareRoot(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
    endfunction
   
    private function UnitAllowFly takes unit u returns boolean
        return UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf')
    endfunction
   
    //Rounding is actually important, because I'm using precise values here and there
    private function RoundInteger takes real r returns integer
        local integer temp = R2I(r)
        local integer temp2 = R2I(r + 0.5)
        if  temp == temp2 then
            return temp
        else
            return temp2
        endif
    endfunction
   
    private keyword VoidOrb
    private keyword LockedSummon
    private keyword UnitImage
    private keyword DrainOrb
   
    //USE OF STRUCT:
    //spawns an amount of particles around the target point,
    //coming closer in a rotating motion until they collide.
    //This is phase 1 (of 3) for the spell.
    private struct CircularSpawn
       
        unit caster
        unit array dummies[SPAWN_PART_CNT]
        real angle
        real centerX
        real centerY
        real z
        real distance
        real distanceStep
        timer t
        effect array spx[SPAWN_PART_CNT]
        private static real zStep
       
        private static method onTick takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            local integer i = 0
            local real x
            local real y
            local real angle
           
            if data.distance > 1. then
                loop
                    exitwhen i >= SPAWN_PART_CNT
                    set angle = data.angle + SPAWN_START_ANGLE + i * (360. / (SPAWN_PART_CNT))
                   
                    set x = data.centerX + data.distance * Cos(angle * bj_DEGTORAD)
                    set y = data.centerY + data.distance * Sin(angle * bj_DEGTORAD)
                    set data.z = data.z + zStep
                   
                    call SetUnitX(data.dummies[i], x)
                    call SetUnitY(data.dummies[i], y)
                    call SetUnitFlyHeight(data.dummies[i], data.z, 0.)
                   
                    set i = i + 1
                endloop
                set data.distance = data.distance - data.distanceStep
                set data.angle = data.angle + SPAWN_SPINSPEED
            else
                //starting phase 2: creating a voidorb!
                call VoidOrb.create(data.caster, data.centerX, data.centerY)
               
                //the collision effect will be spawned at this point
                call DestroyEffect(AddSpecialEffect(SPAWN_COL_MODEL, data.centerX, data.centerY))
                call data.destroy()
            endif

        endmethod
       
        public static method create takes unit caster, real x, real y, real angle returns thistype
            local thistype data = thistype.allocate()
            local integer i = 0
            set data.centerX = x
            set data.centerY = y
            set data.z = SPAWN_START_Z
            set data.caster = caster
            set data.angle = angle
            set data.distance = SPAWN_DISTANCE
            set data.distanceStep = (SPAWN_DISTANCE * TICK) / SPAWN_DURATION
           
            loop
                exitwhen i >= SPAWN_PART_CNT
                set angle = data.angle + SPAWN_START_ANGLE + i * (360. / (SPAWN_PART_CNT))
                set x = data.centerX + data.distance * Cos(angle * bj_DEGTORAD)
                set y = data.centerY + data.distance * Sin(angle * bj_DEGTORAD)
                set data.dummies[i] = CreateUnit(GetOwningPlayer(caster), DUMMYID, x, y, 0.)
                call UnitAllowFly(data.dummies[i])
                set data.spx[i] = AddSpecialEffectTarget(SPAWN_MODEL, data.dummies[i], "origin")
                call SetUnitFlyHeight(data.dummies[i], data.z, 0.)
                set i = i + 1
            endloop
           
            set data.t = NewTimer()
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, TICK, true, function thistype.onTick)
           
            return data
        endmethod
       
        public method onDestroy takes nothing returns nothing
            local integer i = 0
            call ReleaseTimer(.t)
            loop
                exitwhen i >= SPAWN_PART_CNT
                call UnitApplyTimedLife(this.dummies[i], 'BTLK' ,0.01)
                call DestroyEffect(this.spx[i])
                set this.dummies[i] = null
                set this.spx[i] = null
                set i = i + 1
            endloop
            set this.caster = null
        endmethod
       
        private static method onInit takes nothing returns nothing
            set zStep = (SPAWN_END_Z - SPAWN_START_Z)*TICK / SPAWN_DURATION
        endmethod
    endstruct
   
    //USE OF struct:
    //will be used as parent class,
    //as we will have 2 different types
    //of homing missiles. So let's just sum
    //up the whole moving part into a
    //separate struct and make the special
    //missiles inherit from this one.
    private struct HomingMissile
        real x
        real y
        real z
        real minZ
        real collisionRange
        unit target
        unit missile
        real speed
        real speedAcc
        boolean timeout
        timer t
        effect spx
       
        private method onCollision takes nothing returns nothing
            set this.timeout = true
        endmethod
       
        static method onMove takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            local real angle = A2PXY(GetUnitX(data.missile), GetUnitY(data.missile), GetUnitX(data.target), GetUnitY(data.target))
            local real distance = D2PXY(GetUnitX(data.missile), GetUnitY(data.missile), GetUnitX(data.target), GetUnitY(data.target))
            local unit u = null
            local real zStep
           
            if (distance > data.collisionRange) then
                set data.speed = data.speed * data.speedAcc
                set data.x = GetUnitX(data.missile) + data.speed * Cos(angle * bj_DEGTORAD)
                set data.y = GetUnitY(data.missile) + data.speed * Sin(angle * bj_DEGTORAD)
               
                //we limit the height downwards, so the missile must
                //have at least a height of minZ.
                if data.z > data.minZ then
                    set zStep = (data.z-GetUnitFlyHeight(data.target)) / (distance / data.speed)
                    set data.z = data.z - zStep
                else
                    set data.z = data.minZ
                endif
               
                call SetUnitX(data.missile, data.x)
                call SetUnitY(data.missile, data.y)
                call SetUnitFlyHeight(data.missile, data.z, 0.)
                call SetUnitFacing(data.missile, angle)
            else
                call data.onCollision()
            endif
        endmethod
       
        private static method onDelay takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            call TimerStart(data.t, TICK, true, function thistype.onMove)
        endmethod
       
        public static method create takes real startX, real startY, real startZ, real minZ, real zOffset, unit target, real speed, real speedAcc, real collisionRange, string missileModel, real scalingValue, real delay returns thistype
            local thistype data = thistype.allocate()
            local real angle = A2PXY(startX, startY, GetUnitX(target), GetUnitY(target))
           
            set data.x = startX
            set data.y = startY
            set data.z = startZ + zOffset
            set data.minZ = minZ //must have at least this height
            set data.target = target
            set data.speed = speed
            set data.speedAcc = speedAcc
            set data.collisionRange = collisionRange
            set data.timeout = FALSE
            set data.missile = CreateUnit(GetOwningPlayer(target), DUMMYID, startX, startY, angle)
            set data.spx = AddSpecialEffectTarget(missileModel, data.missile, "origin")
           
            call UnitAllowFly(data.missile)
            call SetUnitScale(data.missile, scalingValue,scalingValue,scalingValue)
            call SetUnitFlyHeight(data.missile, startZ, 0.)
           
            set data.t = NewTimer()
            call TimerStart(data.t, delay, false, function thistype.onDelay)
            call SetTimerData(data.t, integer(data))
           
            return data
        endmethod
       
        public method onDestroy takes nothing returns nothing
            call ReleaseTimer(this.t)
            call UnitApplyTimedLife(this.missile, 'BTLK', 0.5)
            call DestroyEffect(this.spx)
            set this.missile = null
            set this.target = null
            set this.spx = null
        endmethod
    endstruct
   
    //USE OF STRUCT:
    //spawns an orb which will float back to the target unit
    //(so basically a homing missile). On it's way, it will
    //spawn several DrainOrbs on nearby enemies.
    //Phase 2 (of 3) for the spell.
    private struct VoidOrb extends HomingMissile
   
        readonly unit caster
        readonly integer level //current ability lvl
        integer dosc //DrainOrb spawn chance
        real drainedLife //the sum of all drained life so far
        timer VOtimer //separate timer for this special HomingMissile's matter
       
        public method addDrainedLife takes real value returns nothing
            set this.drainedLife = this.drainedLife + value
        endmethod
       
        private method onCollision takes nothing returns nothing
            local LockedSummon temp
            local real lifetime
            local integer index = GetHandleId(this.caster)
            //at least the threshold-amount of hitpoints must have been drained
            //to summon a netherworld creature
            if this.drainedLife >= SUMMON_HP_THRESHOLD[this.level] then
                set temp = LockedSummon(VoidOrbTab[index])
               
                //if the caster hasn't got a sidekick already...
                if (temp == 0) then
                    //...create him a new one with the calculated amount of lifetime
                    set VoidOrbTab[index] = integer(LockedSummon.create(this.caster, this.drainedLife / SUMMON_LIFE_FAC[this.level]))
                    call UnitImage.create(this.caster, IMAGE_MODELPATH, IMAGE_GROWTH_DURA, IMAGE_MAX_SIZE, IMAGE_HEIGHT)
                    call UnitImage.create(LockedSummon(VoidOrbTab[index]).u, SUMMON_MODELPATH, SUMMON_GROWTH_DURA, SUMMON_MAX_SIZE, SUMMON_BACK_HGH)
                   
                else
                    //...otherwise create him a new sidekick adding the remaining lifetime
                    //of the previous creature to the new one's.
                    //So basically, the lifetime does stack.
                    set lifetime = temp.slt.remTick * TICK + this.drainedLife / SUMMON_LIFE_FAC[this.level]
                    call temp.destroy()
                    set VoidOrbTab[index] = integer(LockedSummon.create(this.caster, lifetime))
                    call UnitImage.create(this.caster, IMAGE_MODELPATH, IMAGE_GROWTH_DURA, IMAGE_MAX_SIZE, IMAGE_HEIGHT)
                    call UnitImage.create(LockedSummon(VoidOrbTab[index]).u, SUMMON_MODELPATH, SUMMON_GROWTH_DURA, SUMMON_MAX_SIZE, SUMMON_BACK_HGH)
                endif
            endif
            call this.destroy()
        endmethod
       
        private static method onTick takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            local real angle = A2PXY(GetUnitX(data.missile), GetUnitY(data.missile), GetUnitX(data.caster), GetUnitY(data.caster))
            local real distance = D2PXY(GetUnitX(data.missile), GetUnitY(data.missile), GetUnitX(data.caster), GetUnitY(data.caster))
            local unit u = null
            if not data.timeout then
                set TempUnit = data.caster//to make filtering for enemies easier...
                call GroupEnumUnitsInRange(TempGroup, data.x, data.y, DRAINORB_RNG[data.level], Condition(function Enum))
                loop
                    set u = FirstOfGroup(TempGroup)
                    exitwhen u == null
                    //everytime this method runs, each enemied unit nearby the voidorb
                    //will have a data.dosc chance to be a victim of a drainorb.
                   
                    if GetRandomInt(0, 100) < data.dosc then
                        call DrainOrb.create(data, u)
                    endif
                    call GroupRemoveUnit(TempGroup, u)
                endloop
            else
                call data.onCollision()
            endif
        endmethod
       
        public static method onDelay takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            call TimerStart(data.VOtimer, TICK, true, function thistype.onTick)
        endmethod
       
        public static method create takes unit caster, real x, real y returns thistype
            local thistype data = thistype.allocate(x, y, VOIDORB_END_Z, VOIDORB_END_Z, 0., caster, VOIDORB_SPEED, VOIDORB_ACC, VOIDORB_COL, VOIDORB_MODEL, VOIDORB_SIZE, VOIDORB_DELAY)
           
            set data.caster = caster
            set data.level = GetUnitAbilityLevel(caster, AID) -1
            set data.drainedLife = 0.
           
            // this formula will calculate the chance on whether to spawn a drainorb
            // on a nearby enemy or not. About the 40: I guessed that a unit will
            // be approximately 40 * TICKS seconds in the orb's range (so the onTick
            // method will be run 40 times). The rest of the formula is just algebra.

            set data.dosc = RoundInteger(AVG_DMG_PER_UNIT[data.level] / (DRAINORB_DMG[data.level] * 40)* 100)
           
            set data.VOtimer = NewTimer()
            call SetTimerData(data.VOtimer, integer(data))
            call TimerStart(data.VOtimer, VOIDORB_DELAY, false, function thistype.onDelay)
           
            return data
        endmethod
       
        private method onDestroy takes nothing returns nothing
            call DestroyEffect(this.spx)
            call UnitApplyTimedLife(this.missile, 'BTLK', 0.5)
            set this.spx = null
            set this.missile = null
            set this.caster = null
            call ReleaseTimer(this.t)
            call ReleaseTimer(this.VOtimer)
        endmethod
    endstruct
   
    //USE OF STRUCT:
    //spawns an effect on the target which will grow in size
    //and fade over time (just like the effect from the ability avatar)
    //will be used twice when the voidorb has reached the caster:
    //once for the caster himself and once for the summoned unit
    private struct UnitImage
        unit u //target
        unit dummy //dummyunit with the effect spawned on it
        integer fade //current alpha value
        real size //current scaling value
        timer t
        effect spx
        integer fadeStep
        real sizeStep
        private static method onTick takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            local real x
            local real y
            if not (data.fade <= 0) then
                set data.fade = data.fade - data.fadeStep
                set data.size = data.size + data.sizeStep
                set x = GetUnitX(data.u)
                set y = GetUnitY(data.u)
                call SetUnitPosition(data.dummy, x, y)
                call SetUnitFacing(data.dummy, GetUnitFacing(data.u))
                call SetUnitVertexColor(data.dummy, 255,255,255,data.fade)
                call SetUnitScale(data.dummy, data.size, data.size,data.size)
           
            else
                call data.destroy()
            endif
        endmethod
       
        public static method create takes unit target, string modelpath, real duration, real size, real z returns thistype
            local thistype data = thistype.allocate()
            local real x = GetUnitX(target)
            local real y = GetUnitY(target)
           
            set data.u = target
            set data.dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMYID, GetUnitX(data.u), GetUnitY(data.u), GetUnitFacing(data.u))
            set data.spx = AddSpecialEffectTarget(modelpath, data.dummy,"origin")
            set data.fadeStep = R2I((255. * TICK) /  duration)
            set data.sizeStep = (size * TICK) /  duration
            call SetUnitFlyHeight(data.dummy, z, 0.)
            call SetUnitPosition(data.dummy, x, y)
            set data.fade = 255
            set data.size = 1.
           
            set data.t = NewTimer()
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, TICK, true, function thistype.onTick)
           
            return data
        endmethod
       
        private method onDestroy takes nothing returns nothing
            set this.u = null
            call RemoveUnit(this.dummy)
            call DestroyEffect(this.spx)
            call ReleaseTimer(this.t)
            set this.spx = null
            set this.dummy = null
        endmethod
    endstruct
   
    //USE OF STRUCT:
    //a homing missile, just like VoidOrb, however this one
    //will damage the unit it is spawned on. Also, the drained
    //hitpoints will only convert into lifetime for the summoned
    //unit, when the drainOrb has reached the voidOrb.
    private struct DrainOrb extends HomingMissile
        VoidOrb vo //the voidOrb it is homing to
        real drainedLife
        timer DOtimer
       
        private static method onTick takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            //there are only 2 possibilities: the drainOrb will either
            //successfully reach the voidOrb or the voidOrb will vanish
            //before the drainOrb could reach it. In both cases,
            //we must destroy this drainOrb and check for further conditions.
            if data.timeout or data.vo.missile == null then
                call data.destroy()
            endif
        endmethod
       
        //public static method create takes real startX, real startY, real startZ, unit target, real speed, real speedAcc, real collisionRange, string missileModel, real scalingValue, real delay returns thistype
        public static method create takes VoidOrb vo, unit enemy returns thistype
            local real speed = GetRandomReal(DRAINORB_MIN_SPEED, DRAINORB_MAX_SPEED)
            local thistype data = thistype.allocate(GetUnitX(enemy), GetUnitY(enemy), GetUnitFlyHeight(enemy), VOIDORB_END_Z, DRAINORB_ZOFFSET, vo.missile, speed, DRAINORB_ACC, DRAINORB_COL, DRAINORB_MODEL,DRAINORB_SIZE, 0.)

            set data.vo = vo
            call SetUnitVertexColor(data.missile, DRAINORB_RED, DRAINORB_GREEN, DRAINORB_BLUE, DRAINORB_ALPHA)
           
            // DAMAGE DEALT HERE
            set data.drainedLife = DRAINORB_DMG[vo.level]
            call UnitDamageTarget(data.vo.caster, enemy, DRAINORB_DMG[vo.level], true, true, AT, DT, WT)
           
            set data.DOtimer = NewTimer()
            call SetTimerData(data.DOtimer, integer(data))
            call TimerStart(data.DOtimer, TICK, true, function thistype.onTick)
           
            return data
        endmethod
       
        private method onDestroy takes nothing returns nothing
           
            //if the voidOrb has vanished before the drainOrb did...
            if (this.vo.missile == null) then
                call UnitApplyTimedLife(this.missile, 'BTLK', 0.5) //make the drainOrb disappear smoothly
            else
                //otherwise remove it. Because black holes don't disappear slowly, y'know.
                //or at least that's what Albert Einstein came up with.
                call RemoveUnit(this.missile)
               
                //only add drained life when DrainOrb has reached VoidOrb
                call this.vo.addDrainedLife(this.drainedLife)
            endif
            call ReleaseTimer(this.DOtimer)
        endmethod
    endstruct
   
    //USE OF STRUCT:
    //alternative for timed life (like summoned units have).
    //Spawns an amount of shards over the target unit which will
    //shrink and disappear over time one by one.
    private struct SoulLifetime
   
        unit u  //affected unit
        unit array souls[SOUL_AMT] //I called the dummies "souls" here. Just to be fancy.
        real array radians[SOUL_AMT]
        integer current //counter, used for indexing
        real soulSize //current soul scaling value
        real shrinkStep //shrinking step per soul
        integer tickAmt //amount of ticks per soul
        readonly integer remTick //remaining ticks
        timer t
        boolean timeout
        static real radDistance //radian-distance between each soul
        static real cosS//cos-value for the slope plane
        static real sinS//sin ---""---
       
       
        private static method onTick takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            local integer i = 0
           
            // Positioning the souls behind the unit.
            // this is basically just maths...
            local real angle = GetUnitFacing(data.u) * bj_PI/180.
            local real cosA = Cos(angle)
            local real sinA = Sin(angle)
            local real x
            local real y
            local real z
            local real x1
            loop
                exitwhen i >= SOUL_AMT
               
                set x = Cos(data.radians[i]) * SOUL_RADIUS
                set y = Sin(data.radians[i]) * SOUL_RADIUS
                set z = 0
                set x1 = x * cosS
                set z = x * sinS
                set x = x1*cosA - y*sinA
                set y = x1*sinA + y*cosA
               
                call SetUnitX(data.souls[i], GetUnitX(data.u)+SOUL_XOFFSET+x)
                call SetUnitY(data.souls[i], GetUnitY(data.u)+ y)
                call SetUnitFlyHeight(data.souls[i], SOUL_HEIGHT + z + GetUnitFlyHeight(data.u), 0.)
                set i = i+1
            endloop
           
            //Shrinking souls and checking timeout-conditions.
            //If there are no ticks left for the current soul...
            if data.remTick < 0 then
               
                //...make it disappear and decrement the current-counter...
                call UnitApplyTimedLife(data.souls[data.current], 'BTLF', 0.1)
                set data.souls[data.current] = null //nullifying the variable, just to be sure...
                set data.current = data.current - 1
               
                //... and declare timeout when there is no other soul left.
                if data.current < 0 then
                    set data.timeout = true
                endif
               
                set data.soulSize = SOUL_MAX_SCALE
                set data.remTick = data.tickAmt
            else //But if there are still ticks left for the current soul...
                //...just decrement the remaining ticks and shrink the soul.
                set data.soulSize = data.soulSize - data.shrinkStep
                set data.remTick = data.remTick - 1
                call SetUnitScale(data.souls[data.current], data.soulSize,data.soulSize,data.soulSize)
            endif
           
        endmethod
       
        public static method create takes unit target, real time returns thistype
            local thistype data = thistype.allocate()
            local integer i = 0
            local real angle = (GetUnitFacing(target)) * bj_PI/180.
            local real cosA = Cos(angle)
            local real sinA = Sin(angle)
            local real x
            local real y
            local real z
            local real x1

            set data.u = target
            set data.current = (SOUL_AMT-1) //the current soul that is is being used
            set data.tickAmt = R2I((time/TICK)/SOUL_AMT) //amount of ticks for each individual soul
            set data.remTick = data.tickAmt //total remaining ticks
            set data.shrinkStep = (SOUL_MAX_SCALE - SOUL_MIN_SCALE) / data.tickAmt
            set data.soulSize = SOUL_MAX_SCALE
            set data.timeout = false
           
            loop
                exitwhen i >= SOUL_AMT
                set data.radians[i] = -bj_PI/2 + i * radDistance
                set x = Cos(data.radians[i]) * SOUL_RADIUS
                set y = Sin(data.radians[i]) * SOUL_RADIUS
                set x1 = x * cosS
                set z = x * sinS
                set x = x1*cosA - y*sinA
                set y = x1*sinA + y*cosA

                set data.souls[i] = CreateUnit(GetOwningPlayer(data.u), SOUL_ID, GetUnitX(data.u) + x + SOUL_XOFFSET, GetUnitY(data.u) + y, GetUnitFacing(data.u))
                call SetUnitScale(data.souls[i], SOUL_MAX_SCALE, SOUL_MAX_SCALE, SOUL_MAX_SCALE)
                call UnitAllowFly(data.souls[i])
                call SetUnitFlyHeight(data.souls[i], SOUL_HEIGHT + z + GetUnitFlyHeight(data.u), 0.)
               
                set i = i+1
            endloop
           
            set data.t = NewTimer()
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, TICK, true, function SoulLifetime.onTick)
           
            return data
        endmethod
       
        public method onDestroy takes nothing returns nothing
            local integer i = 0
            call ReleaseTimer(this.t)
            set this.u = null
            loop
                exitwhen i >= SOUL_AMT
                if this.souls[i] != null then
                    call UnitApplyTimedLife(this.souls[i], 'BTLF', 0.5)
                    set this.souls[i] = null
                endif
                set i = i+1
            endloop
        endmethod
       
        private static method onInit takes nothing returns nothing
            local real slope = SOUL_SLOPE * bj_PI/180.
            //this values will be the same all the time,
            //so we just calculate them once.
            set cosS = Cos(slope)
            set sinS = Sin(slope)
            set radDistance = bj_PI/(SOUL_AMT-1)
        endmethod
    endstruct
   
    //USE OF STRUCT:
    //summons a unit behind the casters back. This unit
    //won't be able to move freely, but will be attached
    //to the target's back. Whenever the caster attacks,
    //the summoned unit will attack the same target.
    private struct LockedSummon
   
        SoulLifetime slt //each lockedSummon should have soulLifeTime.
        readonly unit u //the summoned unit
        unit lock //the unit it is locked onto
        real x
        real y
        integer level //ability level
        timer t
        boolean inFight //used for summoned unit's behaviour.
       
        //if the caster attacks a enemied unit, we order the summoned
        //to attack the same target.
        private static method onAttack takes nothing returns boolean
            //local thistype data = thistype(GetTriggerData(GetTriggeringTrigger()))
            local thistype data = thistype(LockedSummonTab[GetHandleId(GetAttacker())])
            local real distance = D2PXY(GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), data.x, data.y)

            //attacker must be caster
            if IsUnit(GetAttacker(), data.lock) then
               
                set TempUnit = data.lock //to make filtering for enemies easier.
                call GroupEnumUnitsInRange(TempGroup,data.x, data.y,SUMMON_ATK_RANGE[data.level] - SUMMON_BACK_HGH, Condition(function Enum))
               
                //attacked unit must fit the enumeration-criteria.
                if IsUnitInGroup(GetTriggerUnit(), TempGroup) then
                    call IssueTargetOrder(data.u, "attack", GetTriggerUnit())
                    set data.inFight = true //so now we know, the summoned unit is in a fight.
                endif
               
                call GroupClear(TempGroup)
            endif
           
            return false
        endmethod
       
        private static method onDeath takes nothing returns boolean
            local thistype data = thistype(LockedSummonTab[GetHandleId(GetDyingUnit())])
           
            if IsUnit(GetDyingUnit(), data.lock) then
                call data.destroy()
            endif
           
            return false
        endmethod
       
        private static method onTick takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            local real angle = GetUnitFacing(data.lock)
            local unit temp = null
           
            //as long as the summoned unit has lifetime left...
            if not data.slt.timeout then
           
                set data.x = GetUnitX(data.lock) - SUMMON_BACK_RNG * Cos(angle * bj_DEGTORAD)
                set data.y = GetUnitY(data.lock) - SUMMON_BACK_RNG * Sin(angle * bj_DEGTORAD)
                call SetUnitX(data.u, data.x)
                call SetUnitY(data.u, data.y)
                call SetUnitFlyHeight(data.u, SUMMON_BACK_HGH, 0.)
               
                set TempUnit = data.lock //to make filtering for enemies easier.
                call GroupEnumUnitsInRange(TempGroup,data.x, data.y,SUMMON_ATK_RANGE[data.level] - SUMMON_BACK_HGH, Condition(function Enum))
               
                //if there are no enemies around...
                if FirstOfGroup(TempGroup) == null then

                    if data.inFight then //...and we memorized the unit to fight...
                        set data.inFight = false //...we overthink that, because our unit can't
                                                 //fight when there's no enemy around...
                    endif
                else //... but if there are enemies...
                    if not data.inFight then //...and our unit is not fighting ...
                        //...make it fight...
                        call IssueTargetOrder(data.u, "attack", FirstOfGroup(TempGroup))
                       
                        set data.inFight = true //...and remember that...
                    endif
                    call GroupClear(TempGroup)
                endif
               
                //if the summoned unit is not fighting, we can make it face the same
                //direction as its summoner does and stop all other orders.
                if not data.inFight then
                    call IssueImmediateOrder(data.u, "stop")
                    call SetUnitFacing(data.u, angle)
                endif
            else
                call data.destroy()
            endif
        endmethod
       
        public static method create takes unit lock, real lifetime returns thistype
            local thistype data = thistype.allocate()
            local real angle = GetUnitFacing(lock)
            local integer level = GetUnitAbilityLevel(lock, AID) - 1
           
            set data.lock = lock
            set data.level = level
            set data.x = GetUnitX(data.lock) - SUMMON_BACK_RNG * Cos(angle * bj_DEGTORAD)
            set data.y = GetUnitY(data.lock) - SUMMON_BACK_RNG * Sin(angle * bj_DEGTORAD)
            set data.u = CreateUnit(GetOwningPlayer(data.lock), SUMMON_ID[level], data.x, data.y, angle)
            set data.slt = SoulLifetime.create(data.u, lifetime)
            set data.inFight = false
           
            call SetUnitAnimation(data.u, SUMMON_ANI)
            call UnitAllowFly(data.u)
            call SetUnitFlyHeight(data.u, SUMMON_BACK_HGH, 0.)
           
            //saving the data via a table, so that the non-periodic methods
            //onDeath and onAttack will have the data that way.
            set LockedSummonTab[GetHandleId(data.lock)] = integer(data)

            set data.t = NewTimer()
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, TICK, true, function thistype.onTick)
           
            return data
        endmethod

        public method onDestroy takes nothing returns nothing
            call ReleaseTimer(this.t)
            call UnitApplyTimedLife(this.u, 'BTLK', 0.1)
            call this.slt.destroy()
            set this.u = null
            set this.lock = null
        endmethod
       
        private static method onInit takes nothing returns nothing
            //we need to trigger actions whenever the summoner attacks or dies.
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.onAttack)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
        endmethod
    endstruct
   
    private function Conditions takes nothing returns boolean
        local unit caster = null
        local unit target = null
        local real x
        local real y
        if (GetSpellAbilityId() == AID) then
            set caster = GetSpellAbilityUnit()
            set x = GetSpellTargetX()
            set y = GetSpellTargetY()
           
            //the whole spell starts with a circularSpawn. So let the fun begin...
            call CircularSpawn.create(caster, x, y, A2PXY(GetUnitX(caster), GetUnitY(caster), x, y))
           
            set caster = null
        endif
        return false
    endfunction
   
    private function Init takes nothing returns nothing
        call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function Conditions)
        set VoidOrbTab = Table.create()
        set LockedSummonTab = Table.create()
        call Settings()
       
        //Preloading the strings containing a file
        call Preload(SPAWN_MODEL)
        call Preload(SPAWN_COL_MODEL)
        call Preload(IMAGE_MODELPATH)
        call Preload(VOIDORB_MODEL)
        call Preload(DRAINORB_MODEL)
        call Preload(SUMMON_MODELPATH)
       
    endfunction
endscope
24.05.2016- Preloaded files in the Init function
- scratched out unnecessary comments
11.05.2016- fixed summoned unit's behaviour
- got rid of the library TriggerUtils and used Table instead for this matter
- added method onDeath for LockedSummon, now the summoned unit disappears if caster dies
10.05.2016- added UnitImage effect on summoned unit
- changed A2PXY function so that it doesn't use ModuloReal
- added customizable height settings to CircularSpawn effect
- increased test-map quality
06.05.2016- added struct "HomingMissile" and used it for inheritance
- added documentation at certain code fragments
- changed tooltip
- made summoned unit have different stats per level
06.05.2016First upload
 

Attachments

  • 2016-05-06 (1).png
    2016-05-06 (1).png
    243.3 KB · Views: 339
  • xxdingo93xx + Void Undertow.w3x
    161.8 KB · Views: 55
Last edited:
No reason not to.

In fact I don't see any system that wouldn't be allowed. Unless it's more systems than your own code.

except if the resource is broken or deprecated :)

just like some of vexorian's lib

[edit]
WIP#4



25% of the spell's function is done.

Arachnophobic :V
attachment.php


[edit]
Can I ask for a request, dingo? can you use
tags, because that stretches the page (not all users have large monitors ;_; )
 

Attachments

  • hell to the noo.gif
    hell to the noo.gif
    2.8 MB · Views: 812
Last edited:
Level 37
Joined
Jul 22, 2015
Messages
3,485
@Almia - The effects at the start of your spell sort of reminds me of Tank's BUS :p looks great though!

On a side note, boop:
giphy.gif

Still missing a few more mechanics, including a whole phase after the player can start moving the orb, and a couple of needed special effects. I actually banged my head for a really long time figuring out why the smaller orbs weren't always landing in the center of the big orb... I finally figured out that it was because General Frank made his models "slightly" float *facepalm* Sitting at SEVEN friken GUI triggers :D
 
@Almia - The effects at the start of your spell sort of reminds me of Tank's BUS :p looks great though!

On a side note, boop:
giphy.gif

Still missing a few more mechanics, including a whole phase after the player can start moving the orb, and a couple of needed special effects. I actually banged my head for a really long time figuring out why the smaller orbs weren't always landing in the center of the big orb... I finally figured out that it was because General Frank made his models "slightly" float *facepalm* Sitting at SEVEN friken GUI triggers :D

After all, Tank started that effect trend :D
The particles variance was inspired from my ParticleSystem(but without Appearance Variation and acceleration)

The spider nova effect was inspired from an Eyecandy GIF made by Lambdadelta.
 
Level 4
Joined
Sep 13, 2014
Messages
106
Satanic ritual wip

Not entirely convinced with the name, especially as there already is a spell that happens to have a similar one, but I cannot think of a better idea.
 

Attachments

  • ZerGreenOne Satanic Ritual - Copy.w3x
    172.7 KB · Views: 50
Level 9
Joined
Oct 14, 2013
Messages
238
I've finished work on my spell. I just need to write the necessary comments. Looking at other entries, my spell doesn't have all that effects that other contestants use.

Here's two images as my last WIP.

Just showing the configurable constants that you can change for the spell:

  • Map Settings
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set Golems = (Last created hashtable)
      • Set Mana_Used_for_Stone_Integrity[1] = 50.00
      • Set Mana_Used_for_Stone_Integrity[2] = 60.00
      • Set Mana_Used_for_Stone_Integrity[3] = 70.00
      • Set Core_Golems_Base_HP = 250.00
      • Set Minimum_Scale_For_Hero = 80.00
      • Set Max_Level_of_Stone_Integrity = 3
      • Set Max_Number_of_Golem_Types = 3
      • Set Hp_Threshold_Until_Stone_Drops[1] = 200.00
      • Set Hp_Threshold_Until_Stone_Drops[2] = 180.00
      • Set Hp_Threshold_Until_Stone_Drops[3] = 160.00
      • Set Min_Distance_of_Dropped_Stone = 200.00
      • Set Max_Distance_of_Dropped_Stone = 400.00
      • Set Abilities_for_Dropping_Stones[1] = Stun (Dropping Stone) (Level 01)
      • Set Abilities_for_Dropping_Stones[2] = Stun (Dropping Stone) (Level 02)
      • Set Abilities_for_Dropping_Stones[3] = Stun (Dropping Stone) (Level 03)
      • Set Stone_Types[1] = Broken Rock
      • Set Stone_Types[2] = Broken Fire Stone
      • Set Stone_Types[3] = Broken Icy Stone
      • Set Impact_Time_of_Dropped_Stones = 0.50
      • Set First_Height_of_Dropped_Stone = 250.00
      • Set Second_Height_of_Dropped_Stone = 300.00
      • Set Time_For_Stones_On_Ground = 5.00
      • Set Minimum_Vibration_Intensity = 0.00
      • Set Maximum_Vibration_Intensity = 7.00
      • Set All_The_Tiny_Golm_Types[1] = Tiny Rock Golem - Level 01
      • Set All_The_Tiny_Golm_Types[2] = Tiny Fire Golem - Level 01
      • Set All_The_Tiny_Golm_Types[3] = Tiny Ice Golem - Level 01
      • Set All_The_Tiny_Golm_Types[4] = Tiny Rock Golem - Level 02
      • Set All_The_Tiny_Golm_Types[5] = Tiny Fire Golem - Level 02
      • Set All_The_Tiny_Golm_Types[6] = Tiny Ice Golem - Level 02
      • Set All_The_Tiny_Golm_Types[7] = Tiny Rock Golem - Level 03
      • Set All_The_Tiny_Golm_Types[8] = Tiny Fire Golem - Level 03
      • Set All_The_Tiny_Golm_Types[9] = Tiny Ice Golem - Level 03
      • Set All_The_Greater_Golm_Types[1] = Greater Golem - Level 01
      • Set All_The_Greater_Golm_Types[2] = Greater Golem - Level 02
      • Set All_The_Greater_Golm_Types[3] = Greater Golem - Level 03
      • -------- Optional Abilities --------
      • Set Abilities_for_Greater_Golems[1] = Freezing Aura (Level 01)
      • Set Abilities_for_Greater_Golems[2] = Freezing Aura (Level 02)
      • Set Abilities_for_Greater_Golems[3] = Freezing Aura (Level 03)
      • Set Abilities_for_Greater_Golems[4] = Frost Nova (Level 01)
      • Set Abilities_for_Greater_Golems[5] = Frost Nova (Level 02)
      • Set Abilities_for_Greater_Golems[6] = Frost Nova (Level 03)
      • Set Abilities_for_Greater_Golems[7] = Hardened Skin (Level 01)
      • Set Abilities_for_Greater_Golems[8] = Hardened Skin (Level 02)
      • Set Abilities_for_Greater_Golems[9] = Hardened Skin (Level 03)
      • Set Abilities_for_Greater_Golems[10] = Lava Splash (Level 01)
      • Set Abilities_for_Greater_Golems[11] = Lava Splash (Level 02)
      • Set Abilities_for_Greater_Golems[12] = Lava Splash (Level 03)
      • Set Abilities_for_Greater_Golems[13] = Fire Wave (Level 01)
      • Set Abilities_for_Greater_Golems[14] = Fire Wave (Level 02)
      • Set Abilities_for_Greater_Golems[15] = Fire Wave (Level 03)
      • Set Abilities_for_Greater_Golems[16] = Boulder Slam (Level 01)
      • Set Abilities_for_Greater_Golems[17] = Boulder Slam (Level 02)
      • Set Abilities_for_Greater_Golems[18] = Boulder Slam (Level 03)
      • Set Abilities_for_Greater_Golems[19] = Armor Bonus (Level 01)
      • Set Abilities_for_Greater_Golems[20] = Armor Bonus (Level 02)
      • Set Abilities_for_Greater_Golems[21] = Armor Bonus (Level 03)
      • Set Abilities_for_Greater_Golems[22] = Immolation (Level 01)
      • Set Abilities_for_Greater_Golems[23] = Immolation (Level 02)
      • Set Abilities_for_Greater_Golems[24] = Immolation (Level 03)
      • Set Percent_Of_Golem_HP_Returned = 0.50


And here's one of the triggers that moves the rocks and vibrates them and turns the rocks into different golems.


  • Dropping Stones
    • Events
      • Time - Every 0.01 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Dropping_Stones and do (Actions)
        • Loop - Actions
          • Set Temp_Location[2] = (Position of (Picked unit))
          • Set Temp_Real[2] = (((Load (Key Distance) of (Key (Picked unit)) from Golems) / Impact_Time_of_Dropped_Stones) / 100.00)
          • Set Temp_Location[3] = (Temp_Location[2] offset by Temp_Real[2] towards (Facing of (Picked unit)) degrees)
          • Unit - Move (Picked unit) instantly to Temp_Location[3]
          • Set Temp_Real[2] = (Second_Height_of_Dropped_Stone - (First_Height_of_Dropped_Stone + 10.00))
          • Set Temp_Real[4] = (Load (Key Dropping Time) of (Key (Picked unit)) from Golems)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Temp_Real[4] Greater than or equal to (2.00 x (Impact_Time_of_Dropped_Stones / 3.00))
            • Then - Actions
              • Set Temp_Real[4] = (Temp_Real[4] - 0.01)
              • Hashtable - Save Temp_Real[4] as (Key Dropping Time) of (Key (Picked unit)) in Golems
              • Animation - Change (Picked unit) flying height to ((Current flying height of (Picked unit)) + ((Temp_Real[2] / (Impact_Time_of_Dropped_Stones / 3.00)) / 100.00)) at 0.00
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Temp_Real[4] Less than (2.00 x (Impact_Time_of_Dropped_Stones / 3.00))
              • Temp_Real[4] Greater than or equal to (Impact_Time_of_Dropped_Stones / 3.00)
            • Then - Actions
              • Set Temp_Real[4] = (Temp_Real[4] - 0.01)
              • Hashtable - Save Temp_Real[4] as (Key Dropping Time) of (Key (Picked unit)) in Golems
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Temp_Real[2] Less than 0.00
                • Then - Actions
                  • Animation - Change (Picked unit) flying height to ((Current flying height of (Picked unit)) + ((Temp_Real[2] / (Impact_Time_of_Dropped_Stones / 3.00)) / 100.00)) at 0.00
                • Else - Actions
                  • Animation - Change (Picked unit) flying height to ((Current flying height of (Picked unit)) - ((Temp_Real[2] / (Impact_Time_of_Dropped_Stones / 3.00)) / 100.00)) at 0.00
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Temp_Real[4] Less than (Impact_Time_of_Dropped_Stones / 3.00)
            • Then - Actions
              • Animation - Change (Picked unit) flying height to ((Current flying height of (Picked unit)) - ((First_Height_of_Dropped_Stone / (Impact_Time_of_Dropped_Stones / 3.00)) / 100.00)) at 0.00
            • Else - Actions
          • Set Temp_Real[4] = (Load (Key Impact Time) of (Key (Picked unit)) from Golems)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Temp_Real[4] Less than or equal to 0.00
            • Then - Actions
              • Animation - Change (Picked unit)'s animation speed to 0.00% of its original speed
              • Unit Group - Remove (Picked unit) from Dropping_Stones
              • Unit Group - Add (Picked unit) to Dormant_Rocks_On_The_Ground
              • Unit - Add Abilities_for_Dropping_Stones[(Load (Key Level) of (Key (Picked unit)) from Golems)] to (Picked unit)
              • Unit - Order (Picked unit) to Orc Tauren Chieftain - War Stomp
              • Special Effect - Create a special effect at Temp_Location[2] using Abilities\Spells\Orc\WarStomp\WarStompCaster.mdl
              • Special Effect - Destroy (Last created special effect)
              • Hashtable - Save Handle Of(Position of (Picked unit)) as (Key Resting Place) of (Key (Picked unit)) in Golems
              • Hashtable - Save Time_For_Stones_On_Ground as (Key Resting Time) of (Key (Picked unit)) in Golems
              • Hashtable - Save Minimum_Vibration_Intensity as (Key Vibration Intensity) of (Key (Picked unit)) in Golems
              • For each (Integer A) from 1 to Max_Level_of_Stone_Integrity, do (Actions)
                • Loop - Actions
                  • Unit - Remove Abilities_for_Dropping_Stones[(Integer A)] from (Picked unit)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Unit-type of (Picked unit)) Equal to Broken Fire Stone
                • Then - Actions
                  • Special Effect - Create a special effect at Temp_Location[3] using Environment\LargeBuildingFire\LargeBuildingFire1.mdl
                  • Hashtable - Save Handle Of(Last created special effect) as (Key Effect) of (Key (Picked unit)) in Golems
                • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Unit-type of (Picked unit)) Equal to Broken Icy Stone
                • Then - Actions
                  • Special Effect - Create a special effect at Temp_Location[3] using Abilities\Weapons\FrostWyrmMissile\FrostWyrmMissile.mdl
                  • Hashtable - Save Handle Of(Last created special effect) as (Key Effect) of (Key (Picked unit)) in Golems
                • Else - Actions
            • Else - Actions
              • Set Temp_Real[4] = (Temp_Real[4] - 0.01)
              • Hashtable - Save Temp_Real[4] as (Key Impact Time) of (Key (Picked unit)) in Golems
          • Custom script: call RemoveLocation (udg_Temp_Location[2])
          • Custom script: call RemoveLocation (udg_Temp_Location[3])
      • Unit Group - Pick every unit in Dormant_Rocks_On_The_Ground and do (Actions)
        • Loop - Actions
          • Set Temp_Real[2] = (Load (Key Vibration Intensity) of (Key (Picked unit)) from Golems)
          • Set Temp_Real[4] = (Temp_Real[2] + (((Maximum_Vibration_Intensity - Minimum_Vibration_Intensity) / Time_For_Stones_On_Ground) / 100.00))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Temp_Real[4] Less than Maximum_Vibration_Intensity
            • Then - Actions
              • Set Temp_Real[2] = Temp_Real[4]
              • Hashtable - Save Temp_Real[2] as (Key Vibration Intensity) of (Key (Picked unit)) in Golems
            • Else - Actions
              • Set Temp_Real[2] = Maximum_Vibration_Intensity
              • Hashtable - Save Temp_Real[2] as (Key Vibration Intensity) of (Key (Picked unit)) in Golems
          • Set Temp_Location[2] = ((Load (Key Resting Place) of (Key (Picked unit)) in Golems) offset by Temp_Real[2] towards (Random angle) degrees)
          • Unit - Move (Picked unit) instantly to Temp_Location[2]
          • Set Temp_Real[2] = (Load (Key Resting Time) of (Key (Picked unit)) from Golems)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Temp_Real[2] Less than or equal to 0.00
            • Then - Actions
              • Unit Group - Remove (Picked unit) from Dormant_Rocks_On_The_Ground
              • Set Temp_Integer[3] = (Load (Key Level) of (Key (Picked unit)) from Golems)
              • For each (Integer A) from 1 to Max_Level_of_Stone_Integrity, do (Actions)
                • Loop - Actions
                  • For each (Integer B) from 1 to Max_Number_of_Golem_Types, do (Actions)
                    • Loop - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Integer A) Equal to Temp_Integer[3]
                          • (Unit-type of (Picked unit)) Equal to Stone_Types[(Integer B)]
                        • Then - Actions
                          • Unit - Create 1 All_The_Tiny_Golm_Types[((((Integer A) - 1) x 3) + (Integer B))] for (Owner of (Picked unit)) at (Load (Key Resting Place) of (Key (Picked unit)) in Golems) facing (Random angle) degrees
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Integer B) Equal to 1
                            • Then - Actions
                              • Special Effect - Create a special effect at (Load (Key Resting Place) of (Key (Picked unit)) in Golems) using Abilities\Weapons\GyroCopter\GyroCopterImpact.mdl
                              • Special Effect - Destroy (Last created special effect)
                              • Special Effect - Create a special effect at (Load (Key Resting Place) of (Key (Picked unit)) in Golems) using Abilities\Weapons\GyroCopter\GyroCopterImpact.mdl
                              • Special Effect - Destroy (Last created special effect)
                              • Special Effect - Create a special effect at (Load (Key Resting Place) of (Key (Picked unit)) in Golems) using Abilities\Weapons\GyroCopter\GyroCopterImpact.mdl
                              • Special Effect - Destroy (Last created special effect)
                              • Sound - Play RockGolemBirthSound <gen> at 100.00% volume, located at (Load (Key Resting Place) of (Key (Picked unit)) in Golems) with Z offset 0.00
                              • Hashtable - Save (Integer B) as (Key Type) of (Key (Last created unit)) in Golems
                            • Else - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Integer B) Equal to 2
                            • Then - Actions
                              • Special Effect - Create a special effect attached to the chest of (Last created unit) using Abilities\Weapons\RedDragonBreath\RedDragonMissile.mdl
                              • Hashtable - Save Handle Of(Last created special effect) as 1 of (Key (Last created unit)) in (Last created hashtable)
                              • Special Effect - Create a special effect attached to the right hand of (Last created unit) using Abilities\Weapons\RedDragonBreath\RedDragonMissile.mdl
                              • Hashtable - Save Handle Of(Last created special effect) as 2 of (Key (Last created unit)) in (Last created hashtable)
                              • Special Effect - Create a special effect attached to the left hand of (Last created unit) using Abilities\Weapons\RedDragonBreath\RedDragonMissile.mdl
                              • Hashtable - Save Handle Of(Last created special effect) as 3 of (Key (Last created unit)) in (Last created hashtable)
                              • Special Effect - Create a special effect attached to the head of (Last created unit) using Abilities\Weapons\RedDragonBreath\RedDragonMissile.mdl
                              • Hashtable - Save Handle Of(Last created special effect) as 4 of (Key (Last created unit)) in (Last created hashtable)
                              • Hashtable - Save (Integer B) as (Key Type) of (Key (Last created unit)) in Golems
                            • Else - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Integer B) Equal to 3
                            • Then - Actions
                              • Special Effect - Create a special effect attached to the chest of (Last created unit) using Abilities\Weapons\FrostWyrmMissile\FrostWyrmMissile.mdl
                              • Hashtable - Save Handle Of(Last created special effect) as 1 of (Key (Last created unit)) in (Last created hashtable)
                              • Special Effect - Create a special effect attached to the right hand of (Last created unit) using Abilities\Weapons\SpiritOfVengeanceMissile\SpiritOfVengeanceMissile.mdl
                              • Hashtable - Save Handle Of(Last created special effect) as 2 of (Key (Last created unit)) in (Last created hashtable)
                              • Special Effect - Create a special effect attached to the left hand of (Last created unit) using Abilities\Weapons\SpiritOfVengeanceMissile\SpiritOfVengeanceMissile.mdl
                              • Hashtable - Save Handle Of(Last created special effect) as 3 of (Key (Last created unit)) in (Last created hashtable)
                              • Special Effect - Create a special effect attached to the head of (Last created unit) using Abilities\Weapons\SpiritOfVengeanceMissile\SpiritOfVengeanceMissile.mdl
                              • Hashtable - Save Handle Of(Last created special effect) as 4 of (Key (Last created unit)) in (Last created hashtable)
                              • Hashtable - Save (Integer B) as (Key Type) of (Key (Last created unit)) in Golems
                            • Else - Actions
                          • Unit Group - Add (Last created unit) to Summoned_Tiny_Golems
                          • Set Temp_Unit[1] = (Load (Key Minion) of (Key (Picked unit)) in Golems)
                          • Special Effect - Destroy (Load (Key Effect) of (Key (Picked unit)) in Golems)
                          • Set Temp_Location[3] = (Load (Key Resting Place) of (Key (Picked unit)) in Golems)
                          • Hashtable - Clear all child hashtables of child (Key (Picked unit)) in Golems
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • ((Picked unit) is selected by (Owner of (Picked unit))) Equal to True
                            • Then - Actions
                              • Selection - Add (Last created unit) to selection for (Owner of (Picked unit))
                            • Else - Actions
                          • Unit - Remove (Picked unit) from the game
                          • Hashtable - Save Handle OfTemp_Unit[1] as (Key Minion) of (Key (Last created unit)) in Golems
                          • Hashtable - Save Temp_Integer[3] as (Key Level) of (Key (Last created unit)) in Golems
                          • Custom script: call RemoveLocation (udg_Temp_Location[3])
                        • Else - Actions
            • Else - Actions
              • Set Temp_Real[2] = (Temp_Real[2] - 0.01)
              • Hashtable - Save Temp_Real[2] as (Key Resting Time) of (Key (Picked unit)) in Golems
          • Custom script: call RemoveLocation (udg_Temp_Location[2])


They are still messy I need to tidy them and add some comments. So, I'll post my final entry, tomorrow or the day after that.

Edit
Wheww
I'm really bad at these thread posts. Each single time I try something I screw it up :)
 
Not entirely convinced with the name, especially as there already is a spell that happens to have a similar one, but I cannot think of a better idea.

Can't wait to see your summoned unit :)

Your ritual effect looks awesome :D

@Loner-Magixxar

tags or
tags please :)

As I have said before "not all users have large monitors"
 
Level 9
Joined
Oct 14, 2013
Messages
238
@Loner-Magixxar
Never use 0.01s timers. It's too much on the performance.

I remember the judge for the previous contest advised me to use 0.01 and, anyways my spell doesn't do much in that 0.01 fraction. Just some numbers getting added or subtracted and only at its best a couple of units' movements not hundreds of special effects.

But still I don't know! If that will cause issues in some PCs, I could change them to 0.02.
 
Level 9
Joined
Oct 14, 2013
Messages
238
Loner-Magixxar, a little tip. Check out the other entries for naming conventions of variables.:) (It's supposed to follow standard submission guidelines.)

No big deal. I can change them. It was only for the sake of simplicity.

Edit
I decided to post my map anyways for you to check it out.
There is some polishing to do and (applying your suggestions, too). but other than that it's complete:

I removed the cool down just for testing and spamming. But the whole purpose of the spell is to be more passive than active.

Please check it out and I'd appreciate if you point out any found bugs. I don't like flashy special effects (at least in this spell) so be prepared to see very few of them.

View attachment Stone Integrity V0.3.w3x

If the link doesn't work I don't know how to do it. I upload it and then attach it to my post.
 
Last edited:
Level 17
Joined
Sep 8, 2007
Messages
994
yayayay.jpg

(Note : edited some parts. Not running smooth when I have Google Chrome open)

currently at 1300~ lines ;_; have not yet written the 3(4) cores of the spell.

I might as well just change the some of the mechanics of the spell so that it would be simpler.

Gotta finish this tomorrow(if I can :D)

I really like it, it kinda looks like a different force of nature though.
Just with disgusting spiders and effect-p0rn.
 
I really like it, it kinda looks like a different force of nature though.
Just with disgusting spiders and effect-p0rn.
I actually intended the spell to be ironic to the night elves :D

I didn't even notice the small spiders actually. Really neat effect.
Thanks :D

The effect doesn't cause some massive fps drop now. The spawning effect is the one that causes some fps drop(but it's just a small value)
 
Level 13
Joined
Mar 29, 2012
Messages
530
I actually intended the spell to be ironic to the night elves

Lol, that's opposite to mine. Night elves are even powerful with Ancient of Power, mwaha!


I have question (this might not an exact place to ask but): Is variables from Trigger Editor (Ctrl+B) have limitation in term of number?

Edit:
Cool spell Almia. Looks pretty well-balanced and smooth effects.
 
Level 13
Joined
Mar 29, 2012
Messages
530
nope it doesnt

I see. Gonna continue the spell.

Edit
Here's another question: About variables naming convention, I have my variables named like this:
  • Set AoP_SummoningSFX = war3mapImported\HarvestLife.mdl
  • Set AoP_sSFXSize[1] = 0.50
  • Set AoP_sSFXSize[2] = 0.65
  • Set AoP_sSFXSize[3] = 0.80
  • Set AoP_sSFXHeight = 0.00
Is the 2 last variables okay with those names?
 
Level 17
Joined
Sep 8, 2007
Messages
994
I see. Gonna continue the spell.

Edit
Here's another question: About variables naming convention, I have my variables named like this:
  • Set AoP_SummoningSFX = war3mapImported\HarvestLife.mdl
  • Set AoP_sSFXSize[1] = 0.50
  • Set AoP_sSFXSize[2] = 0.65
  • Set AoP_sSFXSize[3] = 0.80
  • Set AoP_sSFXHeight = 0.00
Is the 2 last variables okay with those names?

it must be clear what is meant with the variable. Other than that, camelCase, CONSTANT_CAPSLOCK, and the usuals is what you should follow IMO.
 
Status
Not open for further replies.
Top