• 🏆 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.
Jeeze Almia, Hopefully it lives up to expectations for all that effort XD

I'm nearly done - just need to clean stuff up (code lines aren't neatly ordered and all that, configure it to a state I'm happy with (nerf production rate), add the priming to prevent that first cast lag and then thoroughly testing it

Here's a preview of the full effect
WIP%20GIF%202.jpg



JASS:
////////////////////////////////////////////////////////////////////
//                       NANO PLAGUE V1.00                        //
//  Author: Tank-Commander                                        //
//  Purpose: Sacrafice yourself to decimate mechanical forces     //
//                                                                //
//  Requires:                                                     //
//    - Custom Stat System (Created by Doomlord)                  //
//      - SimError (Created by Vexorian)                          //
//      - BoundInt (Created by Magtheridon96)                     //
//      The requirements of the CSS are in the map header         //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  Others mentioned in the requirements                     //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

constant function NP_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction

constant function NP_AbilityID takes nothing returns integer
    return 'A001'
endfunction

constant function NP_InfectAbilityID takes nothing returns integer
    return 'A002'
endfunction

constant function NP_DummyID takes nothing returns integer
    return 'u000'
endfunction

constant function NP_UseTeamColouredBuff takes nothing returns boolean
    return true
endfunction

constant function NP_TCBuffDummyID takes nothing returns integer
    return 'u001'
endfunction

constant function NP_BuffID takes nothing returns integer
    return 'A000'
endfunction

constant function NP_BuffFacing takes nothing returns real
    return 270.
endfunction

constant function NP_SummonedUnitID takes nothing returns integer
    return 'u002'
endfunction

constant function NP_SummonBuffID takes nothing returns integer
    return 'B001'
endfunction

constant function NP_AutoInfect takes nothing returns boolean
    return true
endfunction

constant function NP_AttackOrder takes nothing returns integer
    return 851983
endfunction

constant function NP_DummyPlayerID takes nothing returns player
    return Player(14)
endfunction

constant function NP_AttachmentPoint takes nothing returns string
    return "origin"
endfunction

constant function NP_BuffModel takes nothing returns string
    return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactory.mdl"
endfunction

constant function NP_BuffSpawnModel takes nothing returns string
    return "Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl"
endfunction

constant function NP_ProjectileModel takes nothing returns string
    return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactoryMissle.mdl"
endfunction

constant function NP_SpawnBuffModel takes nothing returns string
    return "Objects\\Spawnmodels\\Other\\ToonBoom\\ToonBoom.mdl"
endfunction

constant function NP_InfectionLimit takes nothing returns integer
    return 3
endfunction

constant function NP_BuffHeightOffset takes nothing returns real
    return 150.
endfunction

constant function NP_BuffOffset takes nothing returns real
    return 70.
endfunction

constant function NP_Gravity takes nothing returns real
    return 1.20
endfunction

function NP_AOE takes real level returns real
    return 250 + (50 * level)
endfunction

function NP_BuffSize takes real level returns real
    return 0.275 + (0.025 * level)
endfunction

function NP_ProjectileSize takes real level returns real
    return 0.8 + (0.025 * level)
endfunction

function NP_ProjectileFlightTime takes real level returns real
    return 2 + (-0.2 * level)
endfunction

function NP_InfectDuration takes real level returns real
    return 4 + (1 * level)
endfunction

function NP_InfectDPSHealth takes real level returns real
    return 25 + (25 * level)
endfunction

function NP_InfectDPSMana takes real level returns real
    return 0.
endfunction

function NP_InfectBonusDamageHealth takes real level returns real
    return 30 * level
endfunction

function NP_InfectBonusDamageMana takes real level returns real
    return 0.
endfunction

function NP_InstantDuplicationChance takes real level returns real
    return 0.05 * level
endfunction

function NP_ProductionPS takes real level returns real
    return 2.5 + (0.5 * level)
endfunction

function NP_SummonDuration takes real level returns real
    return 6 + (2 * level)
endfunction

function NP_SummonSize takes real level returns real
    return 0.15 + (0.05 * level)
endfunction

function NP_SummonHealthBonus takes integer level returns integer
    return -50 + 50 * level
endfunction

function NP_SummonHealthRegenBonus takes integer level returns integer
    return 1 * level
endfunction

function NP_SummonArmourBonus takes integer level returns integer
    return -1 + (1 * level)
endfunction

function NP_SummonAttackDamageBonus takes integer level returns integer
    return 5 * level
endfunction

function NP_SummonAttackSpeedBonus takes integer level returns integer
    return 0
endfunction

function NP_SwarmStrengthAttackBonus takes integer SwarmStrength returns integer
    return R2I(0.2 * SwarmStrength)
endfunction

constant function NP_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction

constant function NP_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction

constant function NP_WeaponType takes nothing returns weapontype
    return null
endfunction

constant function NP_InfectionStageID takes nothing returns integer
    return 1
endfunction

constant function NP_ProjectileStageID takes nothing returns integer
    return 2
endfunction

constant function NP_NanobotStageID takes nothing returns integer
    return 3
endfunction

constant function NP_BuffStageID takes nothing returns integer
    return 4
endfunction

constant function NP_WaitingStageID takes nothing returns integer
    return 5
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  Function used to get the Z location height at a given point   //
////////////////////////////////////////////////////////////////////
function NP_GetZ takes real x, real y returns real

    call MoveLocation(udg_NP_ZLoc, x, y)
    return GetLocationZ(udg_NP_ZLoc)
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to make sure that the location is within the    //
//  map bounds so that units cannot be moved outside of it and    //
//  get permanently stuck                                         //
////////////////////////////////////////////////////////////////////
function NP_ValidateLocation takes real x, real y returns boolean
   
    //Check if the point is within the map bounds
    return (x < udg_NP_MapMaxX) and (x > udg_NP_MapMinX) and (y < udg_NP_MapMaxY) and (y > udg_NP_MapMinY)

endfunction
////////////////////////////////////////////////////////////////////
//  Function used to remove instances from the linked list once   //
//  they have expired                                             //
////////////////////////////////////////////////////////////////////
function NP_RecycleNode takes integer Node returns nothing
    set udg_NP_RecycleNodes[udg_NP_RecyclableNodes] = Node
    set udg_NP_RecyclableNodes = udg_NP_RecyclableNodes + 1
    set udg_NP_NextNode[udg_NP_PrevNode[Node]] = udg_NP_NextNode[Node]
    set udg_NP_PrevNode[udg_NP_NextNode[Node]] = udg_NP_PrevNode[Node]
    
    //Stop the timer if this was the last instance
    if (udg_NP_PrevNode[0] == 0) then
        call PauseTimer(udg_NP_Timer)
    endif
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add new nodes to the linked list used by the //
//  system                                                        //
////////////////////////////////////////////////////////////////////
function NP_CreateNode takes nothing returns integer
    local integer Node

    if (udg_NP_RecyclableNodes == 0) then
        set udg_NP_NodeNumber = udg_NP_NodeNumber + 1
        set Node = udg_NP_NodeNumber
    else
        set udg_NP_RecyclableNodes = udg_NP_RecyclableNodes - 1
        set Node = udg_NP_RecycleNodes[udg_NP_RecyclableNodes]
    endif
    
    set udg_NP_NextNode[Node] = 0
    set udg_NP_NextNode[udg_NP_PrevNode[0]] = Node
    set udg_NP_PrevNode[Node] = udg_NP_PrevNode[0]
    set udg_NP_PrevNode[0] = Node
    
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to tell if the passed unit is infected with a   //
//  specific instance                                             //
////////////////////////////////////////////////////////////////////
function NP_IsInfected takes unit u, integer instance returns boolean
    local integer Node = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0 or (udg_NP_Instance[Node] == instance and udg_NP_Unit[Node] == u)
    endloop
    
    return udg_NP_Instance[Node] == instance and udg_NP_Unit[Node] == u
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to get the total amount of infections a given   //
//  unit currently has                                            //
////////////////////////////////////////////////////////////////////
function NP_GetInfections takes unit u returns integer
    local integer Node = 0
    local integer infections = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0
        
        if udg_NP_Unit[Node] == u then
            set infections = infections + 1
        endif
        
    endloop
    
    return infections 
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to attribute each infection to the original     //
//  unique cast of the ability, as each unit can be infected      //
//  multiple times, this is only used for getting summoned units  //
////////////////////////////////////////////////////////////////////
function NP_GetNode takes unit u returns integer
    local integer Node = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0 or udg_NP_Unit[Node] == u
    endloop
    
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to move all buffs on an infected unit when a    //
//  buff has either been added or removed from the unit           //
////////////////////////////////////////////////////////////////////
function NP_MoveBuffs takes unit u, integer buffCount returns nothing
    //Set up locals
    local integer Node = 0
    local real angle = 0.
    local real increment = (bj_PI * 2) / buffCount
    local integer tempInt = 0
    
    //Locate all buffs belonging to this unit
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen tempInt == buffCount
        
        if udg_NP_Caster[Node] == u and udg_NP_StageID[Node] == NP_BuffStageID() then
            
            //Set up their new position
            if buffCount > 1 then
                set udg_NP_Offset[Node] = NP_BuffOffset()
                set angle = angle + increment
                set udg_NP_BuffAngle[Node] = angle
            else
                set udg_NP_Offset[Node] = 0.
                set udg_NP_BuffAngle[Node] = 0.
            endif
            
            set tempInt = tempInt + 1
        endif
            
    endloop
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to remove the infection buff from units and     //
//  subsequently rearrange all others using the MoveBuff function //
////////////////////////////////////////////////////////////////////
function NP_DestroyBuff takes integer Node, unit u returns nothing
    local integer infections = NP_GetInfections(u)    
    
    //Remove the buff and recycle it
    call NP_RecycleNode(Node)
    call KillUnit(udg_NP_Unit[Node])
    
    if not(NP_UseTeamColouredBuff()) then
        call DestroyEffect(udg_NP_CurrentEffect[Node])
    endif
    
    //Rearrange remaining buffs
    if infections > 1 then
        call NP_MoveBuffs(u, infections - 1)
    endif
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add the infection buff to units and          //
//  subsequently rearrange all others using the MoveBuff function //
////////////////////////////////////////////////////////////////////
function NP_CreateBuff takes unit u, integer buffCount, real level, player pl returns integer
    //set up locals
    local integer Node = NP_CreateNode()
    
    //Create new buff
    if NP_UseTeamColouredBuff() then
        set udg_NP_Unit[Node] = CreateUnit(NP_DummyPlayerID(), NP_TCBuffDummyID(), GetUnitX(u), GetUnitY(u), NP_BuffFacing())
        call SetUnitColor(udg_NP_Unit[Node], GetPlayerColor(pl))
    else
        set udg_NP_Unit[Node] = CreateUnit(NP_DummyPlayerID(), NP_DummyID(), GetUnitX(u), GetUnitY(u), NP_BuffFacing())
        set udg_NP_CurrentEffect[Node] = AddSpecialEffectTarget(NP_BuffModel(), udg_NP_Unit[Node], NP_AttachmentPoint())
    endif
    
    //Set up buff data
    call PauseUnit(udg_NP_Unit[Node], true)
    if UnitAddAbility(udg_NP_Unit[Node], 'Amrf') and UnitRemoveAbility(udg_NP_Unit[Node], 'Amrf') then
    endif
    call SetUnitScale(udg_NP_Unit[Node], NP_BuffSize(level), 0., 0.)
    call SetUnitFlyHeight(udg_NP_Unit[Node], GetUnitFlyHeight(u) + NP_BuffHeightOffset(), 0.)
    set udg_NP_Caster[Node] = u
    set udg_NP_StageID[Node] = NP_BuffStageID()
    //Create spawn effect on the affected unit
    call DestroyEffect(AddSpecialEffectTarget(NP_BuffSpawnModel(), u, NP_AttachmentPoint()))
    
    //Move all Buffs to fit around the circle
    call NP_MoveBuffs(u, buffCount)
    
    //Return the newly created buff
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to produce new nanobots either from a virus     //
//  or from the instant duplication effect                        //
////////////////////////////////////////////////////////////////////
function NP_CreateProjectile takes real x, real y, real z, integer Node returns nothing
    local integer tempNode = NP_CreateNode()
    local real x2
    local real y2
    local real offset
    local real angle = GetRandomReal(-bj_PI, bj_PI)
    local real tempReal = udg_NP_ProjectileFlightTime[udg_NP_Instance[Node]] / NP_TimerSpeed()
    local real dy
    local real dx
    
    //Get target centre
    if udg_NP_TargetSelf[Node] then
        set x2 = GetUnitX(udg_NP_Unit[Node])
        set y2 = GetUnitY(udg_NP_Unit[Node])
    else
        set x2 = udg_NP_TargetX[Node]
        set y2 = udg_NP_TargetY[Node]
    endif
    
    //Get random location from the target point (Uniform distribution)
    set offset = GetRandomReal(0, udg_NP_AOE[udg_NP_Instance[Node]]) +  GetRandomReal(0, udg_NP_AOE[udg_NP_Instance[Node]])
    
    if offset > udg_NP_AOE[udg_NP_Instance[Node]] then
        set offset = 2 * udg_NP_AOE[udg_NP_Instance[Node]] - offset
    endif
    
    set x2 = x2 + offset * Cos(angle)
    set y2 = y2 + offset * Sin(angle)
    
    //Create projectile
    set udg_NP_StageID[tempNode] = NP_ProjectileStageID()
    set udg_NP_Unit[tempNode] = CreateUnit(NP_DummyPlayerID(), NP_DummyID(), x, y, angle * bj_RADTODEG)
    set udg_NP_Instance[tempNode] = udg_NP_Instance[Node]
    call SetUnitScale(udg_NP_Unit[tempNode], udg_NP_ProjectileSize[udg_NP_Instance[Node]], 0., 0.)
    set udg_NP_CurrentEffect[tempNode] = AddSpecialEffectTarget(NP_ProjectileModel(), udg_NP_Unit[tempNode], NP_AttachmentPoint())
    if UnitAddAbility(udg_NP_Unit[tempNode], 'Amrf') and UnitRemoveAbility(udg_NP_Unit[tempNode], 'Amrf') then
    endif
    call DestroyEffect(AddSpecialEffectTarget(NP_SpawnBuffModel(), udg_NP_Unit[tempNode], NP_AttachmentPoint()))
    call SetUnitFlyHeight(udg_NP_Unit[tempNode], z, 0.)
    set udg_NP_CurrentZ[tempNode] = z + NP_GetZ(x, y)
    call PauseUnit(udg_NP_Unit[tempNode], true)
    
    //Set up movement
    set udg_NP_XVel[tempNode] = (x2 - x) / (udg_NP_ProjectileFlightTime[udg_NP_Instance[Node]] / NP_TimerSpeed())
    set udg_NP_YVel[tempNode] = (y2 - y) / (udg_NP_ProjectileFlightTime[udg_NP_Instance[Node]] / NP_TimerSpeed())
    set udg_NP_ZVel[tempNode] = ((NP_GetZ(x2, y2) - udg_NP_CurrentZ[tempNode]) + ((NP_Gravity() * (tempReal * tempReal)) / 2)) / tempReal
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to run the main engine of the spell, handles    //
//  creation of new units, buff control and projectile control    //
////////////////////////////////////////////////////////////////////
function NP_Loop takes nothing returns nothing
    //Set up locals
    local integer Node = 0
    local integer tempNode = 0
    local real x
    local real y
    local real x2
    local real y2
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0
        
        //Infections which are producing nanobots
        if udg_NP_StageID[Node] == NP_InfectionStageID() then
            //Check if the infection is over
            if udg_NP_InfectionDuration[Node] > 0. and not(IsUnitType(udg_NP_Unit[Node], UNIT_TYPE_DEAD)) then
                //Deal infection damage
                set udg_NP_SwarmStrengthBonus[Node] = NP_SwarmStrengthAttackBonus(udg_NP_SwarmStrength[Node])
                call UnitDamageTarget(udg_NP_Caster[Node], udg_NP_Unit[Node], udg_NP_InfectionDamageHealth[udg_NP_Instance[Node]], true, false, NP_AttackType(), NP_DamageType(), NP_WeaponType())
                call SetUnitState(udg_NP_Unit[Node], UNIT_STATE_MANA, GetUnitState(udg_NP_Unit[Node], UNIT_STATE_MANA) - udg_NP_InfectionDamageMana[udg_NP_Instance[Node]])
            
                set udg_NP_InfectionDuration[Node] = udg_NP_InfectionDuration[Node] - NP_TimerSpeed()
                
                //Check if it's time to produce a new nanobot
                if udg_NP_CurrentProductionDelay[Node] <= 0. then
                    set udg_NP_CurrentProductionDelay[Node] = udg_NP_ProductionDelay[udg_NP_Instance[Node]]
                    
                    //Produce new nanobot
                    call NP_CreateProjectile(GetUnitX(udg_NP_Unit[udg_NP_BuffNode[Node]]), GetUnitY(udg_NP_Unit[udg_NP_BuffNode[Node]]), GetUnitFlyHeight(udg_NP_Unit[udg_NP_BuffNode[Node]]), Node)
                    set udg_NP_SwarmStrength[udg_NP_Instance[Node]] = udg_NP_SwarmStrength[udg_NP_Instance[Node]] + 1
                else
                    set udg_NP_CurrentProductionDelay[Node] = udg_NP_CurrentProductionDelay[Node] - NP_TimerSpeed()
                endif
                
            else
                set udg_NP_StageID[Node] = NP_WaitingStageID()
                set udg_NP_SwarmStrength[udg_NP_Instance[Node]] = udg_NP_SwarmStrength[udg_NP_Instance[Node]] - 1
            endif
            
        //Projectiles
        elseif udg_NP_StageID[Node] == NP_ProjectileStageID() then
            //Move projectiles
            set x = GetUnitX(udg_NP_Unit[Node]) + udg_NP_XVel[Node]
            set y = GetUnitY(udg_NP_Unit[Node]) + udg_NP_YVel[Node]
            
            
            set udg_NP_ZVel[Node] = udg_NP_ZVel[Node] - NP_Gravity()
            set udg_NP_CurrentZ[Node] = udg_NP_CurrentZ[Node] + udg_NP_ZVel[Node]
            
            if NP_ValidateLocation(x, y) then
                call SetUnitX(udg_NP_Unit[Node], x)
                call SetUnitY(udg_NP_Unit[Node], y)
            endif
            
            set x2 = udg_NP_CurrentZ[Node] - NP_GetZ(x,y)
            call SetUnitFlyHeight(udg_NP_Unit[Node], x2, 0.)
            
            if x2 < 0 then
                //Crash, remove projectile data
                call DestroyEffect(udg_NP_CurrentEffect[Node])
                call KillUnit(udg_NP_Unit[Node])
                call NP_RecycleNode(Node)
                //Create summon
                set tempNode = NP_CreateNode()
                set udg_NP_Unit[tempNode] = CreateUnit(udg_NP_Player[udg_NP_Instance[Node]], NP_SummonedUnitID(), x, y, GetUnitFacing(udg_NP_Unit[Node]))
                call UnitApplyTimedLife(udg_NP_Unit[tempNode], NP_SummonBuffID(), udg_NP_SummonDuration[udg_NP_Instance[Node]])
                call SetUnitScale(udg_NP_Unit[tempNode], udg_NP_SummonSize[udg_NP_Instance[Node]], 0., 0.)
                call UnitAddAbility(udg_NP_Unit[tempNode], NP_InfectAbilityID())
                call SetUnitAbilityLevel(udg_NP_Unit[tempNode], NP_InfectAbilityID(), udg_NP_SummonLevel[udg_NP_Instance[Node]])
                //Apply bonuses
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonArmour[udg_NP_Instance[Node]], 0)
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonAttackSpeed[udg_NP_Instance[Node]], 1)
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonAttackDamage[udg_NP_Instance[Node]], 2)
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonHealthRegen[udg_NP_Instance[Node]], 6)
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonHealth[udg_NP_Instance[Node]], 8)
                //Set up logic
                set udg_NP_StageID[tempNode] = NP_NanobotStageID()
                set udg_NP_Instance[tempNode] = udg_NP_Instance[Node]
                set udg_NP_Caster[tempNode] = udg_NP_Caster[udg_NP_Instance[Node]]

            endif
            
        //Nanobots produced by the spell
        elseif udg_NP_StageID[Node] == NP_NanobotStageID() then
        
            //Check if it's still alive
            if (IsUnitType(udg_NP_Unit[Node], UNIT_TYPE_DEAD)) then
                set udg_NP_SwarmStrength[udg_NP_Instance[Node]] = udg_NP_SwarmStrength[udg_NP_Instance[Node]] - 1
                call NP_RecycleNode(Node)
            else
                //Apply buff
                call CSS_ClearBonus(udg_NP_Unit[Node], 2)
                call CSS_AddBonus(udg_NP_Unit[Node], udg_NP_SummonAttackDamage[udg_NP_Instance[Node]] + udg_NP_SwarmStrengthBonus[udg_NP_Instance[Node]], 2)
            endif
            
        //Infection buffs
        elseif udg_NP_StageID[Node] == NP_BuffStageID() then
            //Update location of Buffs
            set x = GetUnitX(udg_NP_Caster[Node])
            set y = GetUnitY(udg_NP_Caster[Node])
            set x2 = x + udg_NP_Offset[Node] * Cos(udg_NP_BuffAngle[Node])
            set y2 = y + udg_NP_Offset[Node] * Sin(udg_NP_BuffAngle[Node])
            call SetUnitX(udg_NP_Unit[Node], x2)
            call SetUnitY(udg_NP_Unit[Node], y2)
            call SetUnitFlyHeight(udg_NP_Unit[Node], GetUnitFlyHeight(udg_NP_Caster[Node]) + NP_BuffHeightOffset() + (NP_GetZ(x, y) - NP_GetZ(x2, y2)), 0.)
        //Infection instances that are no longer producing
        elseif udg_NP_SwarmStrength[udg_NP_Instance[Node]] > 0 then
            set udg_NP_SwarmStrengthBonus[Node] = NP_SwarmStrengthAttackBonus(udg_NP_SwarmStrength[Node])
        else
            call NP_DestroyBuff(udg_NP_BuffNode[Node], udg_NP_Unit[Node])
            call NP_RecycleNode(Node)
            
            //If the unit has no other infections, remove the buff
            if NP_GetInfections(udg_NP_Unit[Node]) == 0 then
                call UnitRemoveAbility(udg_NP_Unit[Node], NP_BuffID())
            endif
            
        endif
        
    endloop
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to start an instance of the plague              //
////////////////////////////////////////////////////////////////////
function NP_StartPlague takes nothing returns boolean
    //Set up locals
    local integer Node
    local integer SpellID = GetSpellAbilityId()
    local integer instance
    local integer infections
    local integer ilevel
    local real level
    local unit u
    local unit u2
    local boolean bNew = false
    local boolean bTargetSelf = false
    
    //Check if something was infected
    if SpellID == NP_AbilityID() or SpellID == NP_InfectAbilityID() then
        set u = GetSpellTargetUnit()      
        set u2 = GetTriggerUnit()
                
        //Check if a the unit was infected by a summoned unit or by itself
        if SpellID == NP_InfectAbilityID() then
            set infections = NP_GetInfections(u)
            set instance = NP_GetNode(u2)
            //Check if the unit has been infected from this instance before
            if NP_IsInfected(u, udg_NP_Instance[instance]) or (infections >= NP_InfectionLimit()) then
                //Deal bonus damage when attempting to infect
                call UnitDamageTarget(u2, u, udg_NP_HealthDamage[instance], true, false, NP_AttackType(), NP_DamageType(), NP_WeaponType())
                call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_NP_ManaDamage[instance])
                
                //Instantly create a clone
                if GetRandomReal(0, 1) <= udg_NP_DuplicationChance[udg_NP_Instance[instance]] then
                    call NP_CreateProjectile(GetUnitX(udg_NP_Unit[instance]), GetUnitY(udg_NP_Unit[instance]), GetUnitFlyHeight(udg_NP_Unit[instance]), udg_NP_Instance[instance])
                endif
                
            else
                //Remove the infecting unit
                set bTargetSelf = true
                call RemoveUnit(udg_NP_Unit[instance])
                call NP_RecycleNode(instance)
                set bNew = true
            endif
            
        else
        
            //Prevent targetting of other units
            if not(u == u2) then
                set u = u2
            else
                set bTargetSelf = true
            endif
            
            set infections = NP_GetInfections(u)
            
            if infections < NP_InfectionLimit() then
                set bNew = true
            endif
            
        endif
        
        //Check if a new instance needs to be created
        if (bNew) then
            //Initialise Noe
            set Node = NP_CreateNode()
            
            if udg_NP_PrevNode[Node] == 0 then
                call TimerStart(udg_NP_Timer, NP_TimerSpeed(), true, function NP_Loop)
            endif  
            
            //Set up plague data
            set level = GetUnitAbilityLevel(u2, SpellID)
            set ilevel = R2I(level)
                        
            set udg_NP_Unit[Node] = u
            set udg_NP_Player[Node] = GetTriggerPlayer()
            set udg_NP_BuffNode[Node] = NP_CreateBuff(u, infections + 1, level, udg_NP_Player[Node])
            set udg_NP_TargetSelf[Node] = bTargetSelf
            set udg_NP_TargetX[Node] = GetSpellTargetX()
            set udg_NP_TargetY[Node] = GetSpellTargetY()
            set udg_NP_ProductionDelay[Node] = 1 / NP_ProductionPS(level)
            set udg_NP_CurrentProductionDelay[Node] = udg_NP_ProductionDelay[Node]
            set udg_NP_InfectionDuration[Node] = NP_InfectDuration(level)

            set udg_NP_StageID[Node] = NP_InfectionStageID()
            
            //Pass instance along if the unit was infected by an existing plague
            if SpellID == NP_InfectAbilityID() then
                set udg_NP_Instance[Node] = udg_NP_Instance[instance]
                set udg_NP_Caster[Node] = udg_NP_Caster[instance]
                set udg_NP_SwarmStrength[Node] = 0
            else
                //Set up data only needed by the original instance
                set udg_NP_Instance[Node] = Node
                set udg_NP_Caster[Node] = u
                set udg_NP_SwarmStrength[Node] = 1
                set udg_NP_AOE[Node] = NP_AOE(level)
                set udg_NP_ProjectileSize[Node] = NP_ProjectileSize(level)
                set udg_NP_ProjectileFlightTime[Node] = NP_ProjectileFlightTime(level)
                set udg_NP_InfectionDamageHealth[Node] = NP_InfectDPSHealth(level) * NP_TimerSpeed()
                set udg_NP_InfectionDamageMana[Node] = NP_InfectDPSMana(level) * NP_TimerSpeed()
                set udg_NP_DuplicationChance[Node] = NP_InstantDuplicationChance(level)
                set udg_NP_HealthDamage[Node] = NP_InfectBonusDamageHealth(level)
                set udg_NP_ManaDamage[Node] = NP_InfectBonusDamageMana(level)
                set udg_NP_SummonDuration[Node] = NP_SummonDuration(level)
                set udg_NP_SummonSize[Node] = NP_SummonSize(level)
                set udg_NP_SummonHealth[Node] = NP_SummonHealthBonus(ilevel)
                set udg_NP_SummonHealthRegen[Node] = NP_SummonHealthRegenBonus(ilevel)
                set udg_NP_SummonArmour[Node] = NP_SummonArmourBonus(ilevel)
                set udg_NP_SummonAttackDamage[Node] = NP_SummonAttackDamageBonus(ilevel)
                set udg_NP_SummonAttackSpeed[Node] = NP_SummonAttackSpeedBonus(ilevel)
                set udg_NP_SummonLevel[Node] = ilevel
            endif
            
            //Add buff if this is the first infection
            if infections == 0 then
                call UnitAddAbility(udg_NP_Unit[Node], NP_BuffID())
            endif
            
        endif
        
        set u2 = null
        set u = null
    endif
    
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to enable automatic infections                  //
////////////////////////////////////////////////////////////////////
function NP_AutoInfection takes nothing returns boolean
    local unit u = GetAttacker()
    
    if GetUnitTypeId(u) == NP_SummonedUnitID() then
        call IssueTargetOrderById(u, NP_AttackOrder(), GetTriggerUnit()) 
    endif
    
    return false
endfunction
////////////////////////////////////////////////////////////////////
//  Function used to register the trigger of the ability          //
////////////////////////////////////////////////////////////////////
function InitTrig_Nano_Plague takes nothing returns nothing
    //Set up local variables
    local trigger t = CreateTrigger()

    //Set up trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function NP_StartPlague))
    set udg_NP_ZLoc = Location(0., 0.)
    
    //Create the trigger which automates the infection process if wanted
    if NP_AutoInfect() then
        set t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ATTACKED)
        call TriggerAddCondition(t, Condition(function NP_AutoInfection))
    endif
    
    //Sets up the variables used to make sure a point is within the map area
    set udg_NP_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set udg_NP_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
    set udg_NP_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
    set udg_NP_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL                                                //
////////////////////////////////////////////////////////////////////


Edit: And just because I haven't posted an image of it yet, here's one showing the team colouring of the Buffs
 

Attachments

  • TeamColour Demo.png
    TeamColour Demo.png
    326.2 KB · Views: 61
Last edited:
Level 19
Joined
Jul 2, 2011
Messages
2,162
I don't get it tank commando

where is the summoning in that?

anyway I've decided to scrap my old idea(which I've completed) and instead make a whole new summoning since learning that 25 points go to code.

let's face it people... there is nothing amazing or special about using code, anyone can do it and saying anything other wise is just fanwanking.

the previous concept I had was unique, but I'll create a coded version which does the same thing yet in an overly complicated way.

why? for the points.

I feel like we are choosing atmosphere over functionality. anyway good luck to all those participating.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
I don't get it tank commando

where is the summoning in that?
There are little robots that come out of the box after they hit the ground.

et's face it people... there is nothing amazing or special about using code, anyone can do it and saying anything other wise is just fanwanking.
Is this a joke or are you new to the Zephyr contest? Zephyr has always been about coding. And really... anyone can do it? I have some really high expectations for your entry.

On a side note, thats pretty a insulting comment considering the amount of time and practice most of the contestants have been through to get where they are today. I hope I just misunderstood what you meant.
 
- Anitarf (code)
- Bribe (visuals)
- PurgeandFire (concept)

Does it mean there will be only one review then?
Each judge has one criteria, and will review it for all entries. And all the judge results will be combined into one bigger review, yes.

Anyway, looking at some WIPs here, I think the theme has become a little bit fuzzy. I thought we are supposed to create living unit summoning spell. But now it looks like that launching missiles spell is also acceptable, isn't it? Like a meteor spell, the caster summons the meteor, so it's a summoning spell. I think it makes the theme lil bit less "new". But if it's indeed allowed, I think I have an idea here then. xD mwehehe

But.. but... missile launching spell is also unit summoning spell, the dummy unit is also a unit.
http://www.hiveworkshop.com/forums/2818740-post36.html
http://www.hiveworkshop.com/forums/2819298-post68.html
http://www.hiveworkshop.com/forums/2819306-post71.html

----

If you require a new model for your spell, you can import it, also. :)
(only because I saw some dicussion about models)
 
Level 11
Joined
Jul 25, 2014
Messages
490
WIP #3 (last wip):

I'm close to the end with 845 code lines. Managed to implement
everything that I had imagined from the start. I'll lay out all the
details in the final entry. Meanwhile, here's a gif (it's a level 1
spell, it looks way better as the levels go on, I just don't want to
spoil that yet :D):
giphy.gif
 
Level 19
Joined
Jul 2, 2011
Messages
2,162
WIP #3 (last wip):

I'm close to the end with 845 code lines. Managed to implement
everything that I had imagined from the start. I'll lay out all the
details in the final entry. Meanwhile, here's a gif (it's a level 1
spell, it looks way better as the levels go on, I just don't want to
spoil that yet :D):
giphy.gif

that's pretty cool, it's kinda like a self defence summoning.
 
Entry Post

[TD]Entry Post - Nano Plague[/TD]
Tooltip

Icon
BTNInfernalCannon.png
W - Nano Plague
Releases a nanobot into the caster which consumes them to replicate itself for a few seconds. replicas are thrown out at the target point to seek more things to infect propagating the process

Strength in numbers: The more nanobots alive the stronger each one becomes
Fieldwork: nanobots may instantly replicate when attempting to infect a unit

Level 1 - Nanobot, 5 second infection, 8 second duration, consumes 250 health. Infects Mechanicals
Level 2 - Microbot, 6 second infection, 10 second duration, consumes 450 health. Infects Mechanicals
Level 3 - Minibot, 7 second infection, 12 second duration, consumes 700 health. Infects Mechanicals

Code

JASS:
////////////////////////////////////////////////////////////////////
//                       NANO PLAGUE V1.00                        //
//  Author: Tank-Commander                                        //
//  Purpose: Sacrafice yourself to decimate mechanical forces     //
//                                                                //
//  Requires:                                                     //
//    - Custom Stat System (Created by Doomlord)                  //
//      - SimError (Created by Vexorian)                          //
//      - BoundInt (Created by Magtheridon96)                     //
//      The requirements of the CSS are in the map header         //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  Others mentioned in the requirements                     //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  README:                                                       //
//    Before modifying this spell a few things need to be         //
//    understood and read, this is one of those things, while     //
//    most modification can be considered intuitive, it still     //
//    helps to read through these intstructions, as they will     //
//    inform you about how to configure this spell to your        //
//    desire.                                                     //
//----------------------------------------------------------------//
//  Initial importing: The variable creator trigger can be        //
//  imported first and if you have the correct settings (file,    //
//  preferences, General, automatically create unknown variables  //
//  checked, then when you paste in the variable creator it       //
//  will automatically give you all the variables you need for    //
//  this spell                                                    //
//                                                                //
//  While the remaining object editor based data is not required  //
//  to function (provided they're replaced with equivelents)      //
//  it's recommended that they are also imported, if their data   //
//  value are not the same as listed in the configuration, those  //
//  configurables will need to be changed to work correctly       //
//----------------------------------------------------------------//
//  Changing Valid targets:                                       //
//    What is considered a valid target is controlled by the      //
//    object data for the infection ability used by Nanobots      //
//    (NP_InfectAbilityID)                                        //
//----------------------------------------------------------------//
//                           MISC                                 //
//----------------------------------------------------------------//
//  TimerSpeed: the time in seconds between each iteration of     //
//  the main loop function (default is 0.031250000) it's          //
//  recommended you leave it like that                            //
constant function NP_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  AbilityID: This is the data value of the ability used as the  //
//  base, make sure it is based off channel (to view raw data     //
//  press ctrl + d, pressing again switches back)                 //
constant function NP_AbilityID takes nothing returns integer
    return 'A001'
endfunction
//----------------------------------------------------------------//
//  InfectAbilityID: This is the data value of the ability used   //
//  to infect additional units with the plague                    //
constant function NP_InfectAbilityID takes nothing returns integer
    return 'A002'
endfunction
//----------------------------------------------------------------//
//  DummyID: This is the data value of the unit used to create    //
//  the buffs and projectiles used by the spell                   //
constant function NP_DummyID takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  BuffID: This is the data value of the ability used as a buff  //
//  for infected units that appears below the unit data           //
constant function NP_BuffID takes nothing returns integer
    return 'A000'
endfunction
//----------------------------------------------------------------//
//  SummonedUnitID: This is the data value of the unit summoned   //
//  by the ability (the nanobots)                                 //
constant function NP_SummonedUnitID takes nothing returns integer
    return 'u002'
endfunction
//----------------------------------------------------------------//
//  SummonBuffID: This is the data value of the buff assigned to  //
//  the nanobots to give the timed life text                      //
constant function NP_SummonBuffID takes nothing returns integer
    return 'B001'
endfunction
//----------------------------------------------------------------//
//  AutoInfect: This will cause your Nanobots to automatically    //
//  attempt to infect valid targets                               //
constant function NP_AutoInfect takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  AttackOrder: This is the order ID of the ability used to      //
//  infect enemies - only matters when AutoInfect is true         //
constant function NP_AttackOrder takes nothing returns integer
    return 851983
endfunction
//----------------------------------------------------------------//
//  DummyPlayerID: This is the player that owns all of the dummy  //
//  units as to not interfere with the scoreboard at the end of   //
//  playing a map, 14 Player(14) is the default value             //
constant function NP_DummyPlayerID takes nothing returns player
    return Player(14)
endfunction
//----------------------------------------------------------------//
//  AttachmentPoint: This is the attachment point all effects     //
//  used by the Dummy Unit are placed onto                        //
constant function NP_AttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//                          VISUALS                               //
//----------------------------------------------------------------//
//  BuffModel: This is the model path for the buff placed above   //
//  infected units, remmeber to double slash (\\) the pathname    //
constant function NP_BuffModel takes nothing returns string
    return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactory.mdl"
endfunction
//----------------------------------------------------------------//
//  BuffSpawnModel: This is the effect used on the body of a      //
//  unit when it has been infected                                //
constant function NP_BuffSpawnModel takes nothing returns string
    return "Abilities\\Spells\\Human\\Polymorph\\PolyMorphDoneGround.mdl"
endfunction
//----------------------------------------------------------------//
//  ProjectileModel: This is the model used for projectiles       //
//  containing Nanobots that get thrown out of the unit           //
constant function NP_ProjectileModel takes nothing returns string
    return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactoryMissle.mdl"
endfunction
//----------------------------------------------------------------//
//  SpawnBuffModel: This is the effect/buff used on projectiles   //
//  when they are freshly created (their spawning effect)         //
constant function NP_SpawnBuffModel takes nothing returns string
    return "Objects\\Spawnmodels\\Other\\ToonBoom\\ToonBoom.mdl"
endfunction
//----------------------------------------------------------------//
//  UseTeamColouredBuff: determines if the buffs created above    //
//  infected units use teamcolour (set to false if the buff model //
//  has no team colour to increase efficiency)                    //
constant function NP_UseTeamColouredBuff takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  BuffFacing: This is the facing angle of the buff created      //
//  above infected units                                          //
constant function NP_BuffFacing takes nothing returns real
    return 270.
endfunction
//----------------------------------------------------------------//
//  BuffHeightOffset: This is how high above the model of         //
//  infected units the infected buff is placed                    //
constant function NP_BuffHeightOffset takes nothing returns real
    return 150.
endfunction
//----------------------------------------------------------------//
//  BuffOffset: This is how far away from the centre of an        //
//  infected unit the buff is moved to when there's more than     //
//  one infection buff                                            //
constant function NP_BuffOffset takes nothing returns real
    return 70.
endfunction
//----------------------------------------------------------------//
//  Gravity: This is the strength of the force pulling            //
//  projectiles toward the ground - increasing the value results  //
//  in steeper Arcs                                               //
constant function NP_Gravity takes nothing returns real
    return 1.20
endfunction
//----------------------------------------------------------------//
//  BuffSize: This is the size of the buff created above          //
//  infected units (1 = 100% scaling)                             //
function NP_BuffSize takes real rlevel returns real
    return 0.275 + (0.025 * rlevel)
endfunction
//----------------------------------------------------------------//
//  ProjectileSize: This is the size of the projectiles carrying  //
//  nanobots within                                               //
function NP_ProjectileSize takes real rlevel returns real
    return 0.8 + (0.025 * rlevel)
endfunction
//----------------------------------------------------------------//
//                          MAIN SPELL                            //
//----------------------------------------------------------------//
//  InfectionLimit: This is the maximum amount of infections a    //
//  single unit can have at any given time                        //
constant function NP_InfectionLimit takes nothing returns integer
    return 3
endfunction
//----------------------------------------------------------------//
//  AOE: This is the size of the area that nanobots can land in   //
//  when they are spawned                                         //
function NP_AOE takes real rlevel returns real
    return 250 + (25 * rlevel)
endfunction
//----------------------------------------------------------------//
//  ProjectileFlightTime: This is how long (in seconds) it takes  //
//  for a projectile carrying a Nanobot to reach the target       //
function NP_ProjectileFlightTime takes real rlevel returns real
    return 2 + (-0.2 * rlevel)
endfunction
//----------------------------------------------------------------//
//  InfectDuration: This is how long (in second) a unit will      //
//  produce nanobots while infected                               //
function NP_InfectDuration takes real rlevel returns real
    return 4 + (1 * rlevel)
endfunction
//----------------------------------------------------------------//
//  InfectDPSHealth: This is how much health (per second) the     //
//  Nanobot will drain from an infected unit                      //
function NP_InfectDPSHealth takes real rlevel returns real
    return 25 + (25 * rlevel)
endfunction
//----------------------------------------------------------------//
//  InfectDPSMana: This is how much mana (per second) the         //
//  Nanobot will drain from an infected unit                      //
function NP_InfectDPSMana takes real rlevel returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  InfectBonusDamageHealth: This is the amount of health damage  //
//  that is dealt to a unit when a Nanobot attempts to infect an  //
//  already infected unit (or at its infection limit)             //
function NP_InfectBonusDamageHealth takes real rlevel returns real
    return 10 * rlevel
endfunction
//----------------------------------------------------------------//
//  InfectBonusDamageMana: This is the amount of mana damage      //
//  that is dealt to a unit when a Nanobot attempts to infect an  //
//  already infected unit (or at its infection limit)             //
function NP_InfectBonusDamageMana takes real rlevel returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  InstantDuplicationChance: This is the probability that        //
//  when a Nanobot attempts to infect an already infected unit    //
//  (or at its infection limit) that a new Nanobot will be        //
//  instantly produces (1 = 100%)                                 //
function NP_InstantDuplicationChance takes real rlevel returns real
    return 0.05 * rlevel
endfunction
//----------------------------------------------------------------//
//  ProductionPS: This is the amount of Nanobots produced (per    //
//  second) by a unit infected by the plague                      //
function NP_ProductionPS takes real rlevel returns real
    return 1.50 + (0.25 * rlevel)
endfunction
//----------------------------------------------------------------//
//  SummonDuration: This is the amount of time (in seconds) that  //
//  each Nanobot lasts for after reaching the ground              //
function NP_SummonDuration takes real rlevel returns real
    return 6 + (2 * rlevel)
endfunction
//----------------------------------------------------------------//
//  SummonSize: This is the size of the nanobot (1 = 100% scale)  //
function NP_SummonSize takes real rlevel returns real
    return 0.15 + (0.05 * rlevel)
endfunction
//----------------------------------------------------------------//
//  SummonHealthBonus: This is the amount of extra health the     //
//  Nanobot obtains when spawned                                  //
function NP_SummonHealthBonus takes integer rlevel returns integer
    return -50 + 50 * rlevel
endfunction
//----------------------------------------------------------------//
//  SummonHealthRegenBonus: This is the amount of extra health    //
//  regen the Nanobot obtains when spawned                        //
function NP_SummonHealthRegenBonus takes integer rlevel returns integer
    return 1 * rlevel
endfunction
//----------------------------------------------------------------//
//  SummonArmourBonus: This is the amount of extra armour the     //
//  Nanobot obtains when spawned                                  //
function NP_SummonArmourBonus takes integer rlevel returns integer
    return -1 + (1 * rlevel)
endfunction
//----------------------------------------------------------------//
//  SummonAttackDamageBonus: This is the amount of extra damage   //
//  the Nanobot obtains when spawned                              //
function NP_SummonAttackDamageBonus takes integer rlevel returns integer
    return 5 * rlevel
endfunction
//----------------------------------------------------------------//
//  SummonAttackSpeedBonus: This is the amount of extra attack    //
//  speed the Nanobot obtains when spawned                        //
function NP_SummonAttackSpeedBonus takes integer rlevel returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//  SwarmStrengthAttackBonus: This is the extra damage given to   //
//  the Nanobot for every Nanobot currently alive in the Swarm    //
function NP_SwarmStrengthAttackBonus takes integer SwarmStrength returns integer
    return R2I(0.5 * SwarmStrength)
endfunction
//----------------------------------------------------------------//
//                       DAMAGE SETTINGS                          //
//----------------------------------------------------------------//
//  AttackType: This is the attacktype which is used when         //
//  health is being drained from an infected unit and as the      //
//  bonus infection damage                                        //
constant function NP_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
//  DamageType: This is the damagetype which is used when         //
//  health is being drained from an infected unit and as the      //
//  bonus infection damage                                        //
constant function NP_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
//  WeaponType: This is the weapontype which is used when         //
//  health is being drained from an infected unit and as the      //
//  bonus infection damage                                        //
constant function NP_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//                          READ ONLY                             //
//----------------------------------------------------------------//
//  These Values are read only: Do not attempt to change them     //
constant function NP_InfectionStageID takes nothing returns integer
    return 1
endfunction
//----------------------------------------------------------------//
constant function NP_ProjectileStageID takes nothing returns integer
    return 2
endfunction
//----------------------------------------------------------------//
constant function NP_NanobotStageID takes nothing returns integer
    return 3
endfunction
//----------------------------------------------------------------//
constant function NP_BuffStageID takes nothing returns integer
    return 4
endfunction
//----------------------------------------------------------------//
constant function NP_WaitingStageID takes nothing returns integer
    return 5
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  Function used to get the Z location height at a given point   //
////////////////////////////////////////////////////////////////////
function NP_GetZ takes real x, real y returns real

    call MoveLocation(udg_NP_ZLoc, x, y)
    return GetLocationZ(udg_NP_ZLoc)
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to make sure that the location is within the    //
//  map bounds so that units cannot be moved outside of it and    //
//  get permanently stuck                                         //
////////////////////////////////////////////////////////////////////
function NP_ValidateLocation takes real x, real y returns boolean
   
    //Check if the point is within the map bounds
    return (x < udg_NP_MapMaxX) and (x > udg_NP_MapMinX) and (y < udg_NP_MapMaxY) and (y > udg_NP_MapMinY)

endfunction
////////////////////////////////////////////////////////////////////
//  Function used to remove instances from the linked list once   //
//  they have expired                                             //
////////////////////////////////////////////////////////////////////
function NP_RecycleNode takes integer node returns nothing
    set udg_NP_RecycleNodes[udg_NP_RecyclableNodes] = node
    set udg_NP_RecyclableNodes = udg_NP_RecyclableNodes + 1
    set udg_NP_NextNode[udg_NP_PrevNode[node]] = udg_NP_NextNode[node]
    set udg_NP_PrevNode[udg_NP_NextNode[node]] = udg_NP_PrevNode[node]
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add new nodes to the linked list used by the //
//  system                                                        //
////////////////////////////////////////////////////////////////////
function NP_CreateNode takes nothing returns integer
    local integer node

    if (udg_NP_RecyclableNodes == 0) then
        set udg_NP_NodeNumber = udg_NP_NodeNumber + 1
        set node = udg_NP_NodeNumber
    else
        set udg_NP_RecyclableNodes = udg_NP_RecyclableNodes - 1
        set node = udg_NP_RecycleNodes[udg_NP_RecyclableNodes]
    endif
    
    set udg_NP_NextNode[node] = 0
    set udg_NP_NextNode[udg_NP_PrevNode[0]] = node
    set udg_NP_PrevNode[node] = udg_NP_PrevNode[0]
    set udg_NP_PrevNode[0] = node
    
    return node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to tell if the passed unit is infected with a   //
//  specific instance                                             //
////////////////////////////////////////////////////////////////////
function NP_IsInfected takes unit u, integer instance returns boolean
    local integer node = 0
    
    loop
        set node = udg_NP_NextNode[node]
        exitwhen node == 0 or (udg_NP_Instance[node] == instance and udg_NP_Unit[node] == u)
    endloop
    
    return udg_NP_Instance[node] == instance and udg_NP_Unit[node] == u
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to get the total amount of infections a given   //
//  unit currently has                                            //
////////////////////////////////////////////////////////////////////
function NP_GetInfections takes unit u returns integer
    local integer node = 0
    local integer infections = 0
    
    loop
        set node = udg_NP_NextNode[node]
        exitwhen node == 0
        
        if udg_NP_Unit[node] == u then
            set infections = infections + 1
        endif
        
    endloop
    
    return infections 
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to attribute each infection to the original     //
//  unique cast of the ability, as each unit can be infected      //
//  multiple times, this is only used for getting summoned units  //
////////////////////////////////////////////////////////////////////
function NP_GetNode takes unit u returns integer
    local integer node = 0
    
    loop
        set node = udg_NP_NextNode[node]
        exitwhen node == 0 or udg_NP_Unit[node] == u
    endloop
    
    return node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to move all buffs on an infected unit when a    //
//  buff has either been added or removed from the unit           //
////////////////////////////////////////////////////////////////////
function NP_MoveBuffs takes unit u, integer buffCount returns nothing
    //Set up locals
    local integer node = 0
    local real angle = 0.
    local real increment = (bj_PI * 2) / buffCount
    local integer tempInt = 0
    
    //Locate all buffs belonging to this unit
    loop
        set node = udg_NP_NextNode[node]
        exitwhen tempInt == buffCount
        
        if udg_NP_Caster[node] == u and udg_NP_StageID[node] == NP_BuffStageID() then
            
            //Set up their new position
            if buffCount > 1 then
                set udg_NP_Offset[node] = NP_BuffOffset()
                set angle = angle + increment
                set udg_NP_BuffAngle[node] = angle
            else
                set udg_NP_Offset[node] = 0.
                set udg_NP_BuffAngle[node] = 0.
            endif
            
            set tempInt = tempInt + 1
        endif
            
    endloop
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to remove the infection buff from units and     //
//  subsequently rearrange all others using the MoveBuff function //
////////////////////////////////////////////////////////////////////
function NP_DestroyBuff takes integer node, unit u returns nothing
    local integer infections = NP_GetInfections(u)    
    
    //Remove the buff and recycle it
    call NP_RecycleNode(node)
    call KillUnit(udg_NP_Unit[node])
    call DestroyEffect(udg_NP_CurrentEffect[node])
    
    //Rearrange remaining buffs
    if infections > 1 then
        call NP_MoveBuffs(u, infections - 1)
    endif
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add the infection buff to units and          //
//  subsequently rearrange all others using the MoveBuff function //
////////////////////////////////////////////////////////////////////
function NP_CreateBuff takes unit u, integer buffCount, real rlevel, player pl returns integer
    //set up locals
    local integer node = NP_CreateNode()
    
    //Create new buff
    set udg_NP_Unit[node] = CreateUnit(NP_DummyPlayerID(), NP_DummyID(), GetUnitX(u), GetUnitY(u), NP_BuffFacing())
    set udg_NP_CurrentEffect[node] = AddSpecialEffectTarget(NP_BuffModel(), udg_NP_Unit[node], NP_AttachmentPoint())
    
    //Apply optional teamcolour
    if NP_UseTeamColouredBuff() then
        call SetUnitColor(udg_NP_Unit[node], GetPlayerColor(pl))
    endif
    
    //Set up buff data
    call PauseUnit(udg_NP_Unit[node], true)
    if UnitAddAbility(udg_NP_Unit[node], 'Amrf') and UnitRemoveAbility(udg_NP_Unit[node], 'Amrf') then
    endif
    call SetUnitScale(udg_NP_Unit[node], NP_BuffSize(rlevel), 0., 0.)
    call SetUnitFlyHeight(udg_NP_Unit[node], GetUnitFlyHeight(u) + NP_BuffHeightOffset(), 0.)
    set udg_NP_Caster[node] = u
    set udg_NP_StageID[node] = NP_BuffStageID()
    //Create spawn effect on the affected unit
    call DestroyEffect(AddSpecialEffectTarget(NP_BuffSpawnModel(), u, NP_AttachmentPoint()))
    
    //Move all Buffs to fit around the circle
    call NP_MoveBuffs(u, buffCount + 1)
    
    //Return the newly created buff
    return node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to produce new nanobots either from a virus     //
//  or from the instant duplication effect                        //
////////////////////////////////////////////////////////////////////
function NP_CreateProjectile takes real x, real y, real z, integer node returns nothing
    local integer tempNode = NP_CreateNode()
    local real x2
    local real y2
    local real offset
    local real angle = GetRandomReal(-bj_PI, bj_PI)
    local real tempReal = udg_NP_ProjectileFlightTime[udg_NP_Instance[node]] / NP_TimerSpeed()
    local real dy
    local real dx
    
    //Get target centre
    if udg_NP_TargetSelf[node] then
        set x2 = GetUnitX(udg_NP_Unit[node])
        set y2 = GetUnitY(udg_NP_Unit[node])
    else
        set x2 = udg_NP_TargetX[node]
        set y2 = udg_NP_TargetY[node]
    endif
    
    //Get random location from the target point (Uniform distribution)
    set offset = GetRandomReal(0, udg_NP_AOE[udg_NP_Instance[node]]) +  GetRandomReal(0, udg_NP_AOE[udg_NP_Instance[node]])
    
    if offset > udg_NP_AOE[udg_NP_Instance[node]] then
        set offset = 2 * udg_NP_AOE[udg_NP_Instance[node]] - offset
    endif
    
    set x2 = x2 + offset * Cos(angle)
    set y2 = y2 + offset * Sin(angle)
    
    //Create projectile
    set udg_NP_StageID[tempNode] = NP_ProjectileStageID()
    set udg_NP_Unit[tempNode] = CreateUnit(NP_DummyPlayerID(), NP_DummyID(), x, y, angle * bj_RADTODEG)
    set udg_NP_Instance[tempNode] = udg_NP_Instance[node]
    call SetUnitScale(udg_NP_Unit[tempNode], udg_NP_ProjectileSize[udg_NP_Instance[node]], 0., 0.)
    set udg_NP_CurrentEffect[tempNode] = AddSpecialEffectTarget(NP_ProjectileModel(), udg_NP_Unit[tempNode], NP_AttachmentPoint())
    if UnitAddAbility(udg_NP_Unit[tempNode], 'Amrf') and UnitRemoveAbility(udg_NP_Unit[tempNode], 'Amrf') then
    endif
    call DestroyEffect(AddSpecialEffectTarget(NP_SpawnBuffModel(), udg_NP_Unit[tempNode], NP_AttachmentPoint()))
    call SetUnitFlyHeight(udg_NP_Unit[tempNode], z, 0.)
    set udg_NP_CurrentZ[tempNode] = z + NP_GetZ(x, y)
    call PauseUnit(udg_NP_Unit[tempNode], true)
    
    //Set up movement
    set udg_NP_XVel[tempNode] = (x2 - x) / (udg_NP_ProjectileFlightTime[udg_NP_Instance[node]] / NP_TimerSpeed())
    set udg_NP_YVel[tempNode] = (y2 - y) / (udg_NP_ProjectileFlightTime[udg_NP_Instance[node]] / NP_TimerSpeed())
    set udg_NP_ZVel[tempNode] = ((NP_GetZ(x2, y2) - udg_NP_CurrentZ[tempNode]) + ((NP_Gravity() * (tempReal * tempReal)) / 2)) / tempReal
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to run the main engine of the spell, handles    //
//  creation of new units, buff control and projectile control    //
////////////////////////////////////////////////////////////////////
function NP_Loop takes nothing returns nothing
    //Set up locals
    local integer node = 0
    local integer tempNode = 0
    local real x
    local real y
    local real x2
    local real y2
    
    loop
        set node = udg_NP_NextNode[node]
        exitwhen node == 0
        
        //Infections which are producing nanobots
        if udg_NP_StageID[node] == NP_InfectionStageID() then
            //Check if the infection is over
            if udg_NP_InfectionDuration[node] > 0. and not(IsUnitType(udg_NP_Unit[node], UNIT_TYPE_DEAD)) then
                //Deal infection damage
                set udg_NP_SwarmStrengthBonus[node] = NP_SwarmStrengthAttackBonus(udg_NP_SwarmStrength[node])
                call UnitDamageTarget(udg_NP_Caster[node], udg_NP_Unit[node], udg_NP_InfectionDamageHealth[udg_NP_Instance[node]], true, false, NP_AttackType(), NP_DamageType(), NP_WeaponType())
                call SetUnitState(udg_NP_Unit[node], UNIT_STATE_MANA, GetUnitState(udg_NP_Unit[node], UNIT_STATE_MANA) - udg_NP_InfectionDamageMana[udg_NP_Instance[node]])
            
                set udg_NP_InfectionDuration[node] = udg_NP_InfectionDuration[node] - NP_TimerSpeed()
                
                //Check if it's time to produce a new nanobot
                if udg_NP_CurrentProductionDelay[node] <= 0. then
                    set udg_NP_CurrentProductionDelay[node] = udg_NP_ProductionDelay[udg_NP_Instance[node]]
                    
                    //Produce new nanobot
                    call NP_CreateProjectile(GetUnitX(udg_NP_Unit[udg_NP_BuffNode[node]]), GetUnitY(udg_NP_Unit[udg_NP_BuffNode[node]]), GetUnitFlyHeight(udg_NP_Unit[udg_NP_BuffNode[node]]), node)
                    set udg_NP_SwarmStrength[udg_NP_Instance[node]] = udg_NP_SwarmStrength[udg_NP_Instance[node]] + 1
                else
                    set udg_NP_CurrentProductionDelay[node] = udg_NP_CurrentProductionDelay[node] - NP_TimerSpeed()
                endif
                
            else
                //Change the ID to the waiting period
                set udg_NP_StageID[node] = NP_WaitingStageID()
                set udg_NP_SwarmStrength[udg_NP_Instance[node]] = udg_NP_SwarmStrength[udg_NP_Instance[node]] - 1
            endif
            
        //Projectiles
        elseif udg_NP_StageID[node] == NP_ProjectileStageID() then
            //Move projectiles
            set x = GetUnitX(udg_NP_Unit[node]) + udg_NP_XVel[node]
            set y = GetUnitY(udg_NP_Unit[node]) + udg_NP_YVel[node]
            
            set udg_NP_ZVel[node] = udg_NP_ZVel[node] - NP_Gravity()
            set udg_NP_CurrentZ[node] = udg_NP_CurrentZ[node] + udg_NP_ZVel[node]
            
            if NP_ValidateLocation(x, y) then
                call SetUnitX(udg_NP_Unit[node], x)
                call SetUnitY(udg_NP_Unit[node], y)
            endif
            
            set x2 = udg_NP_CurrentZ[node] - NP_GetZ(x,y)
            call SetUnitFlyHeight(udg_NP_Unit[node], x2, 0.)
            
            if x2 < 0 then
                //Crash, remove projectile data
                call DestroyEffect(udg_NP_CurrentEffect[node])
                call KillUnit(udg_NP_Unit[node])
                call NP_RecycleNode(node)
                //Create summon
                set tempNode = NP_CreateNode()
                set udg_NP_Unit[tempNode] = CreateUnit(udg_NP_Player[udg_NP_Instance[node]], NP_SummonedUnitID(), x, y, GetUnitFacing(udg_NP_Unit[node]))
                call UnitApplyTimedLife(udg_NP_Unit[tempNode], NP_SummonBuffID(), udg_NP_SummonDuration[udg_NP_Instance[node]])
                call SetUnitScale(udg_NP_Unit[tempNode], udg_NP_SummonSize[udg_NP_Instance[node]], 0., 0.)
                call UnitAddAbility(udg_NP_Unit[tempNode], NP_InfectAbilityID())
                call SetUnitAbilityLevel(udg_NP_Unit[tempNode], NP_InfectAbilityID(), udg_NP_SummonLevel[udg_NP_Instance[node]])
                //Apply bonuses to stats
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonArmour[udg_NP_Instance[node]], 0) 
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonAttackSpeed[udg_NP_Instance[node]], 1)
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonAttackDamage[udg_NP_Instance[node]], 2)
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonHealthRegen[udg_NP_Instance[node]], 6)
                call CSS_AddBonus(udg_NP_Unit[tempNode], udg_NP_SummonHealth[udg_NP_Instance[node]], 8)
                //Set up logic
                set udg_NP_StageID[tempNode] = NP_NanobotStageID()
                set udg_NP_Instance[tempNode] = udg_NP_Instance[node]
                set udg_NP_Caster[tempNode] = udg_NP_Caster[udg_NP_Instance[node]]
            endif
            
        //Nanobots produced by the spell
        elseif udg_NP_StageID[node] == NP_NanobotStageID() then
        
            //Check if it's still alive
            if (IsUnitType(udg_NP_Unit[node], UNIT_TYPE_DEAD)) then
                set udg_NP_SwarmStrength[udg_NP_Instance[node]] = udg_NP_SwarmStrength[udg_NP_Instance[node]] - 1
                //Recycling
                call FlushChildHashtable(udg_CSS_Hashtable, GetHandleId(udg_NP_Unit[node]))
                call NP_RecycleNode(node)
            else
                //Apply buff
                call CSS_ClearBonus(udg_NP_Unit[node], 2)
                call CSS_AddBonus(udg_NP_Unit[node], udg_NP_SummonAttackDamage[udg_NP_Instance[node]] + udg_NP_SwarmStrengthBonus[udg_NP_Instance[node]], 2)
            endif
            
        //Infection buffs
        elseif udg_NP_StageID[node] == NP_BuffStageID() then
            //Update location of Buffs
            set x = GetUnitX(udg_NP_Caster[node])
            set y = GetUnitY(udg_NP_Caster[node])
            set x2 = x + udg_NP_Offset[node] * Cos(udg_NP_BuffAngle[node])
            set y2 = y + udg_NP_Offset[node] * Sin(udg_NP_BuffAngle[node])
            call SetUnitX(udg_NP_Unit[node], x2)
            call SetUnitY(udg_NP_Unit[node], y2)
            call SetUnitFlyHeight(udg_NP_Unit[node], GetUnitFlyHeight(udg_NP_Caster[node]) + NP_BuffHeightOffset() + (NP_GetZ(x, y) - NP_GetZ(x2, y2)), 0.)
        //Infection instances that are no longer producing
        elseif udg_NP_SwarmStrength[udg_NP_Instance[node]] > 0 then
            set udg_NP_SwarmStrengthBonus[node] = NP_SwarmStrengthAttackBonus(udg_NP_SwarmStrength[node])
        else
            call NP_DestroyBuff(udg_NP_BuffNode[node], udg_NP_Unit[node])
            call NP_RecycleNode(node)
            
            //If the unit has no other infections, remove the buff
            if NP_GetInfections(udg_NP_Unit[node]) == 0 then
                call UnitRemoveAbility(udg_NP_Unit[node], NP_BuffID())
                
                //Stop the timer if this was the last instance
                if (udg_NP_PrevNode[0] == 0) then
                    call PauseTimer(udg_NP_Timer)
                endif
                
            endif
            
        endif
        
    endloop
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to start an instance of the plague              //
////////////////////////////////////////////////////////////////////
function NP_StartPlague takes nothing returns boolean
    //Set up locals
    local integer node
    local integer spellID = GetSpellAbilityId()
    local integer instance
    local integer infections
    local integer ilevel
    local real rlevel
    local unit u
    local unit u2
    local boolean bNew = false
    local boolean bTargetSelf = false
    
    //Check if something was infected
    if spellID == NP_AbilityID() or spellID == NP_InfectAbilityID() then
        set u = GetSpellTargetUnit()      
        set u2 = GetTriggerUnit()
                
        //Check if a the unit was infected by a summoned unit or by itself
        if spellID == NP_InfectAbilityID() then
            set infections = NP_GetInfections(u)
            set instance = NP_GetNode(u2)
            //Check if the unit has been infected from this instance before
            if GetUnitTypeId(u) == NP_SummonedUnitID() or NP_IsInfected(u, udg_NP_Instance[instance]) or (infections >= NP_InfectionLimit()) then
                //Deal bonus damage when attempting to infect
                call UnitDamageTarget(u2, u, udg_NP_HealthDamage[instance], true, false, NP_AttackType(), NP_DamageType(), NP_WeaponType())
                call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_NP_ManaDamage[instance])
                
                //Instantly create a clone
                if GetRandomReal(0, 1) <= udg_NP_DuplicationChance[udg_NP_Instance[instance]] then
                    call NP_CreateProjectile(GetUnitX(udg_NP_Unit[instance]), GetUnitY(udg_NP_Unit[instance]), GetUnitFlyHeight(udg_NP_Unit[instance]), udg_NP_Instance[instance])
                    set udg_NP_SwarmStrength[udg_NP_Instance[instance]] =  udg_NP_SwarmStrength[udg_NP_Instance[instance]] + 1
                endif
                
            else
                //Remove the infecting unit
                set bTargetSelf = true
                call RemoveUnit(udg_NP_Unit[instance])
                call NP_RecycleNode(instance)
                set bNew = true
            endif
            
        else
        
            //Prevent targetting of other units
            if not(u == u2) then
                set u = u2
            else
                set bTargetSelf = true
            endif
            
            set infections = NP_GetInfections(u)
            
            if infections < NP_InfectionLimit() then
                set bNew = true
            endif
            
        endif
        
        //Check if a new instance needs to be created
        if (bNew) then
            //Initialise Noe
            set node = NP_CreateNode()
            
            if udg_NP_PrevNode[node] == 0 then
                call TimerStart(udg_NP_Timer, NP_TimerSpeed(), true, function NP_Loop)
            endif  
            
            //Set up plague data
            set rlevel = GetUnitAbilityLevel(u2, spellID)
            set ilevel = R2I(rlevel)
                        
            set udg_NP_Unit[node] = u
            set udg_NP_Player[node] = GetTriggerPlayer()
            set udg_NP_StageID[node] = NP_InfectionStageID()
            set udg_NP_BuffNode[node] = NP_CreateBuff(u, infections, rlevel, udg_NP_Player[node])
            set udg_NP_TargetSelf[node] = bTargetSelf
            set udg_NP_TargetX[node] = GetSpellTargetX()
            set udg_NP_TargetY[node] = GetSpellTargetY()
            set udg_NP_ProductionDelay[node] = 1 / NP_ProductionPS(rlevel)
            set udg_NP_CurrentProductionDelay[node] = udg_NP_ProductionDelay[node]
            set udg_NP_InfectionDuration[node] = NP_InfectDuration(rlevel)
            
            //Pass instance along if the unit was infected by an existing plague
            if spellID == NP_InfectAbilityID() then
                set udg_NP_Instance[node] = udg_NP_Instance[instance]
                set udg_NP_Caster[node] = udg_NP_Caster[instance]
                set udg_NP_SwarmStrength[node] = 0
            else
                //Set up data only needed by the original instance
                set udg_NP_Instance[node] = node
                set udg_NP_Caster[node] = u
                set udg_NP_SwarmStrength[node] = 1
                set udg_NP_AOE[node] = NP_AOE(rlevel)
                set udg_NP_ProjectileSize[node] = NP_ProjectileSize(rlevel)
                set udg_NP_ProjectileFlightTime[node] = NP_ProjectileFlightTime(rlevel)
                set udg_NP_InfectionDamageHealth[node] = NP_InfectDPSHealth(rlevel) * NP_TimerSpeed()
                set udg_NP_InfectionDamageMana[node] = NP_InfectDPSMana(rlevel) * NP_TimerSpeed()
                set udg_NP_DuplicationChance[node] = NP_InstantDuplicationChance(rlevel)
                set udg_NP_HealthDamage[node] = NP_InfectBonusDamageHealth(rlevel)
                set udg_NP_ManaDamage[node] = NP_InfectBonusDamageMana(rlevel)
                set udg_NP_SummonDuration[node] = NP_SummonDuration(rlevel)
                set udg_NP_SummonSize[node] = NP_SummonSize(rlevel)
                set udg_NP_SummonHealth[node] = NP_SummonHealthBonus(ilevel)
                set udg_NP_SummonHealthRegen[node] = NP_SummonHealthRegenBonus(ilevel)
                set udg_NP_SummonArmour[node] = NP_SummonArmourBonus(ilevel)
                set udg_NP_SummonAttackDamage[node] = NP_SummonAttackDamageBonus(ilevel)
                set udg_NP_SummonAttackSpeed[node] = NP_SummonAttackSpeedBonus(ilevel)
                set udg_NP_SummonLevel[node] = ilevel
            endif
            
            //Add buff if this is the first infection
            if infections == 0 then
                call UnitAddAbility(udg_NP_Unit[node], NP_BuffID())
            endif
            
        endif
        
        set u2 = null
        set u = null
    endif
    
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to enable automatic infections                  //
////////////////////////////////////////////////////////////////////
function NP_AutoInfection takes nothing returns boolean
    local unit u = GetAttacker()
    
    if GetUnitTypeId(u) == NP_SummonedUnitID() then
        call IssueTargetOrderById(u, NP_AttackOrder(), GetTriggerUnit()) 
    endif
    
    set u = null
    return false
endfunction
////////////////////////////////////////////////////////////////////
//  Function used to register the trigger of the ability          //
////////////////////////////////////////////////////////////////////
function InitTrig_Nano_Plague takes nothing returns nothing
    //Set up local variables
    local trigger t = CreateTrigger()
    
    //Set up trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function NP_StartPlague))
    set udg_NP_ZLoc = Location(0., 0.)
    
    //Create the trigger which automates the infection process if wanted
    if NP_AutoInfect() then
        set t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ATTACKED)
        call TriggerAddCondition(t, Condition(function NP_AutoInfection))
    endif
    
    //Sets up the variables used to make sure a point is within the map area
    set udg_NP_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set udg_NP_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
    set udg_NP_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
    set udg_NP_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL                                                //
////////////////////////////////////////////////////////////////////

Spellset Information

- 912 lines of pure JASS
- 43 Configurables reaching all aspects of the ability
- 1 ability 2 parts, compact and powerful
- Extensive Readme to help you configure the ability
- Doesn't interfere with game scorescreen
- Compatiable with Vanilla WE
- Functions for ground and flying heros
- Arcs are stable on uneven terrain
- Features can be adapted to your liking due to the configuration including complete removal
- Attractive yet functional
- Summon stats are almost entirely controlled through Code leading to only one unit being needed for the summon
- Plagues are team coloured!
- Easy to reach target filtering (done through object data)
- Ignores magic immune enemies correctly
- Variable reuse reduces the amount of variables to import to 40
- Buffs for all status conditions
- Works well as an "ultimate Pocket Factory"
- Swapping out models is easy to create a bug plague or other infestation
- Uses a health cost instead of a Manacost
Tips and Tricks


- Aim for units you can infect, the Nanobots are stronger the more you have of them, Aiming for the right location can be the difference between a quick failure and a decimating plague
- Nanobots automatically infect susceptible units they find themselves attacking
- You can manually control all of the robots, to infect a unit just tell the robot to attack that unit - if it can infect it, it will
- If you target your own unit with the ability Nanobots will spawn on your location regardless of where you move
- Move the caster away after starting the plague; Nanobots will still be thrown to the target point and reach it in the same time as normal
- Replicated units are thrown back to the original casting area, use this to your advantage when casting on yourself
- High HP infected enemies can be used to prolong an infection via use of the instant replication mechanic
- Nanobots can infect allies so bringing your own mechanical units to a fight can be used to make spawning your swarm easier however they will take a lot of damage as a result of the plague and if positioned poorly the swarm may go to waste


- At high levels infected units are done for - run them away from the rest of your army as the Nanobots spawn near their location
- Nanobots have little HP, take them all out with an AOE spell to stop the plague in its tracks
- Nanobots get stronger the more there are of them: Don't let them infect your mechanical units and block their path with other units
- Keep your distance from enemies with mechanicals in their army as they can infect their own units, try to bait the player into using the ability and back off; this will both deal damage to their army without fighting it and waste the nanobots created

Helpful Files

- Variable Creator: Copy and paste into your map, you now have all the variables needed for the spell to run
  • Variable Creator
    • Events
    • Conditions
    • Actions
      • Set NP_AOE[0] = 0.00
      • Set NP_BuffAngle[0] = 0.00
      • Set NP_BuffNode[0] = 0
      • Set NP_Caster[0] = NP_Unit[0]
      • Set NP_CurrentEffect[0] = NP_CurrentEffect[0]
      • Set NP_CurrentProductionDelay[0] = 0.00
      • Set NP_CurrentZ[0] = 0.00
      • Set NP_DuplicationChance[0] = 0.00
      • Set NP_HealthDamage[0] = 0.00
      • Set NP_InfectionDamageHealth[0] = 0.00
      • Set NP_InfectionDamageMana[0] = 0.00
      • Set NP_InfectionDuration[0] = 0.00
      • Set NP_Instance[0] = 0
      • Set NP_ManaDamage[0] = 0.00
      • Set NP_MapMaxX = 0.00
      • Set NP_MapMaxY = 0.00
      • Set NP_MapMinX = 0.00
      • Set NP_MapMinY = 0.00
      • Set NP_NextNode[0] = 0
      • Set NP_NodeNumber = 0
      • Set NP_Offset[0] = 0.00
      • Set NP_Player[0] = NP_Player[0]
      • Set NP_PrevNode[0] = 0
      • Set NP_ProductionDelay[0] = 0.00
      • Set NP_ProjectileFlightTime[0] = 0.00
      • Set NP_ProjectileSize[0] = 0.00
      • Set NP_RecyclableNodes = 0
      • Set NP_RecycleNodes[0] = 0
      • Set NP_StageID[0] = 0
      • Set NP_SummonArmour[0] = 0
      • Set NP_SummonAttackDamage[0] = 0
      • Set NP_SummonAttackSpeed[0] = 0
      • Set NP_SummonDuration[0] = 0.00
      • Set NP_SummonHealth[0] = 0
      • Set NP_SummonHealthRegen[0] = 0
      • Set NP_SummonLevel[0] = 0
      • Set NP_SummonSize[0] = 0.00
      • Set NP_SwarmStrength[0] = 0
      • Set NP_SwarmStrengthBonus[0] = 0
      • Set NP_TargetSelf[0] = False
      • Set NP_TargetX[0] = 0.00
      • Set NP_TargetY[0] = 0.00
      • Set NP_Timer = NP_Timer
      • Set NP_XVel[0] = 0.00
      • Set NP_YVel[0] = 0.00
      • Set NP_ZLoc = NP_ZLoc
      • Set NP_ZVel[0] = 0.00

Images

Tooltip

Levels

Showcases

GIFs


attachment.php

Level 1

Level 2

Level 3

Distribution

Buff

attachment.php
attachment.php
attachment.php
attachment.php
attachment.php

Teamcolouring

Multiple Infection

Mixed Infection

Ongoing Assault

Assault Remains

attachment.php
attachment.php
attachment.php
attachment.php
attachment.php

Basic Cast

Infectious Assault

Follow Caster

Basic%20Cast%20GIF.jpg
Infectious%20Assault%20GIF.jpg
Follow%20Caster%20GIF.jpg

[tr]
 

Attachments

  • Level 1.png
    Level 1.png
    281.3 KB · Views: 1,243
  • Level 2.png
    Level 2.png
    281.7 KB · Views: 1,278
  • Level 3.png
    Level 3.png
    275.6 KB · Views: 1,230
  • Typical Distribution of Nanobots.png
    Typical Distribution of Nanobots.png
    174 KB · Views: 1,233
  • Remains of a successful Plague Strike.png
    Remains of a successful Plague Strike.png
    287.8 KB · Views: 1,163
  • Ongoing Assault.png
    Ongoing Assault.png
    428.3 KB · Views: 1,153
  • Triple Infection.png
    Triple Infection.png
    369.8 KB · Views: 1,191
  • Two Simultaneous Infections.png
    Two Simultaneous Infections.png
    213.8 KB · Views: 1,196
  • Team Coloured Plagues.png
    Team Coloured Plagues.png
    263.3 KB · Views: 1,195
  • Buff.png
    Buff.png
    556.3 KB · Views: 1,177
  • Tooltip.png
    Tooltip.png
    107.1 KB · Views: 1,268
  • Tank-Commander - Nano Plague.w3x
    99.3 KB · Views: 93
Last edited:
Level 11
Joined
Jul 25, 2014
Messages
490
Final entry

Final Entry
Demonic Ritual
By Meatmuffin
Tooltip & Icon
239494-albums8807-picture108121.png

239494-albums8807-picture108120.png

Spell information
Rephrasing the tooltip:
It summons a spirit that circles the caster, that makes
another spirit upon impacting an enemy, leaving the primary spirit inactive
(can't duplicate anymore). After the initial duration is over, all the spirits
evolve into minions at their respective locations.
The minions have unique movement: they move to the caster whenever
they go out of the specified range of the caster. If they are in range,
they choose one random unit in the specified attacking radius and shoot
a missile at it. The missile is not homing, but it does have AoE damage.
After the specified minion duration ends or the caster gets killed, the
spell ends altogether.
Additional information:
- 898 lines of JASS code;
- Made in a few days after the contest has started;
- Detailed documentation and instructions on how to use the spell;
- Easily configurable;
- The code is well structured and is efficient;
- Completely MUI & MPI.
Code & GIF

Code

GIF

JASS:
////////////////////// Demonic Ritual v1.00 by Meatmuffin ///////////////////////
// Conjures a flame spirit that circles the caster. Any contact with an enemy  //
// unit will damge it and duplicate the spirit, going into the opposite        //
// direction. Once the initial duration is over, the spirits evolve into       //
// fiery demons, shooting fireballs at nearby enemies. Each spirit can         //
// duplicate only once.                                                        //
/////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////// How to import? /////////////////////////////////
//
// 1. Copy the base ability and the dummy if you don't have these yet;
// 2. Go to File -> Preferences and tick the box that says "Automatically
// create unknown variables when pasting trigger data";
// 3. Copy the folder "Demonic Ritual" including the Variable Creator;
// 4. Configure the spell to suit your needs;
// 5. Enjoy!
/////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////// Credits ////////////////////////////////////
//
// Dummy.mdx - Vexorian/Anitarf/iNfraNe
//
///////////////////////////////// CONFIGURATION /////////////////////////////////
//
// The configuration is split into four parts:
//
// 1. General configuration - this configuration part is meant for you to
// match the primary spell aspects to your needs. Hardly needs changing.
//
// 2. Spirit configuration - this part is meant for you to configure the aspects
// of the spirits. You can call it "Phase 1" configuration.
//
// 3. Minion configuration - this part is meant for you to configure the aspects
// of the minions. You can call it "Phase 2" configuration.
//
// 4. Misc configuration - you don't really need to touch this. It is meant to
// increase the readability and efficiency inside the spell code itself.
//
///////////////////////////// General Configuration /////////////////////////////
//
// The timer speed. It is recommended that you do not change this, but there are
// other acceptable values, mostly ranging from 0.03 to 0.04.
constant function DR_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//
// The ability ID. This must match the one in your map once you have imported the
// spell. Note that in most cases this will have to be changed.
constant function DR_AbilityID takes nothing returns integer
    return 'A000'
endfunction
//
// The dummy ID. This must match the one in your map once you have imported the
// spell (if you don't have one already).
constant function DR_DummyID takes nothing returns integer
    return 'h000'
endfunction
//
// The player that we will create the dummies for. This doesn't have to match
// the player that is casting the spell since we store that onCast.
constant function DR_DummyPlayer takes nothing returns player
    return Player(14)
endfunction
//
// The summoned unit ID. It will be summoned at the right spell part. Note that
// it will work with almost all units. :)
constant function DR_SummonedID takes nothing returns integer
    return 'n001'
endfunction
//
// The special effect used for when a spirit tries to move out of map bounds.
// It is instantly destroyed.
constant function DR_InvalidSpecialEffect takes nothing returns string
    return "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
endfunction
//
// The above special effect attachment point.
constant function DR_InvalidSEAttachmentPoint takes nothing returns string
    return "chest"
endfunction
//
////////////////////////////// Spirit Configuration /////////////////////////////
//
// The initial duration of the surrounding spirits, before they evolve into
// minions.
constant function DR_SpiritDuration takes nothing returns real
    return 4.00
endfunction
//
// The per level duration of the same as above.
constant function DR_SpiritDurationPerLevel takes nothing returns real
    return 2.50
endfunction
//
// The initial offset of the spirit (the distance between you and the spirits).
constant function DR_SpiritOffset takes nothing returns real
    return 300.00
endfunction
//
// The per level offset of the spirit.
constant function DR_SpiritOffsetPerLevel takes nothing returns real
    return 40.00
endfunction
//
// The initial turn rate of the spirits (how many angles per second does it turn).
constant function DR_SpiritTurnRate takes nothing returns real
    return 135.00 * DR_TimerSpeed()
endfunction
//
// The per level turn rate of the spirits.
constant function DR_SpiritTurnRatePerLevel takes nothing returns real
    return 45.00 * DR_TimerSpeed()
endfunction
//
// The initial damage when the spirits collide with enemies.
constant function DR_SpiritDamage takes nothing returns real
    return 20.00
endfunction
//
// The per level damage of the spirits.
constant function DR_SpiritDamagePerLevel takes nothing returns real
    return 30.00
endfunction
//
// The spirit damage/attack/weapon types.
constant function DR_SpiritDamageType takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC
endfunction
//
constant function DR_SpiritAttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction
//
constant function DR_SpiritWeaponType takes nothing returns weapontype
    return WEAPON_TYPE_WHOKNOWS
endfunction
//
// The initial AoE in which the spirits are searching for targets.
constant function DR_SpiritAoE takes nothing returns real
    return 40.00
endfunction
//
// The AoE per level in which the spirits are searching for targets.
constant function DR_SpiritAoEPerLevel takes nothing returns real
    return 10.00
endfunction
//
// The duplication cooldown.
constant function DR_DuplicationCooldown takes nothing returns real
    return 1.00
endfunction
//
// The damage/duplication special effect. It is attached to the unit the spirit
// collided with.
constant function DR_DuplicationFX takes nothing returns string
    return "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
endfunction
//
// The special effect mentioned above, attachment point.
constant function DR_DuplicationAttachmentPoint takes nothing returns string
    return "overhead"
endfunction
//
// The initial max count of the spirits.
constant function DR_SpiritCount takes nothing returns integer
    return 2
endfunction
//
// The per level max count of the spirits.
constant function DR_SpiritCountPerLevel takes nothing returns integer
    return 4
endfunction
//
// The model of the spirit.
constant function DR_SpiritModel takes nothing returns string
    return "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
endfunction
//
// The attachment point of the model.
constant function DR_SpiritModelAttachmentPoint takes nothing returns string
    return "origin"
endfunction
//
// The scaling value (size) of the spirit. 1.00 is the default size.
constant function DR_SpiritSize takes nothing returns real
    return 1.00
endfunction
//
// The scaling value (size) of the spirit, after it has gone inactive.
constant function DR_InactiveSpiritSize takes nothing returns real
    return 0.50
endfunction
//
//////////////////////////// Minion Configuration //////////////////////////////
//
// The initial duration of the minions, once they have evolved from spirits.
// Note that once this duration ends, the spell will end as well.
constant function DR_MinionDuration takes nothing returns real
    return 8.00
endfunction
//
// The per level duration of the same as above.
constant function DR_MinionDurationPerLevel takes nothing returns real
    return 4.00
endfunction
//
// Minion spawning effect.
constant function DR_SpawnEffect takes nothing returns string
    return "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
endfunction

//
// The initial movement speed of the minions. 522 is max unless you use a system
// which modifies that.
constant function DR_MinionMS takes nothing returns real
    return 200.00
endfunction
//
// The per level movement speed of the minions.
constant function DR_MinionMSPerLevel takes nothing returns real
    return 100.00
endfunction
//
// This period indicates how ofter are the minions being ordered to move.
constant function DR_MovingCooldown takes nothing returns real
    return 1.00
endfunction
//
// The size of the minions. 1.00 is default.
constant function DR_MinionSize takes nothing returns real
    return 0.75
endfunction
//
// The minion size per level.
constant function DR_MinionSizePerLevel takes nothing returns real
    return 0.20
endfunction
//
// The initial radius in which the minions are attacking. If the caster moves out
// of this range, the minions will have to follow him until they reach this radius.
constant function DR_AttackingRadius takes nothing returns real
    return 300.00
endfunction
//
// The per level radius in which the minions are attacking.
constant function DR_AttackingRadiusPerLevel takes nothing returns real
    return 100.00
endfunction
//
// The initial radius in which the minions search for enemies around themselves.
// They choose a random target from a pool of units that are in range of them.
constant function DR_SearchRadius takes nothing returns real
    return 350.00
endfunction
//
// The per level radius in which the minions earch for enemies around themselves.
constant function DR_SearchRadiusPerLevel takes nothing returns real
    return 125.00
endfunction
//
// The initial attack cooldown of the minions.
constant function DR_AttackCooldown takes nothing returns real
    return 2.50
endfunction
//
// The attack cooldown per level (decreasing).
constant function DR_AttackCooldownPerLevel takes nothing returns real
    return 0.50
endfunction
//
// The animation used when launching a missile.
constant function DR_AttackAnimation takes nothing returns string
    return "spell"
endfunction
//
// The minions shoot projectiles in a parabolic curve. This indicates the initial
// speed of the missiles. This does not have the limit of 522.
constant function DR_MissileSpeed takes nothing returns real
    return 400.00 * DR_TimerSpeed()
endfunction
//
// The missile speed per level.
constant function DR_MissileSpeedPerLevel takes nothing returns real
    return 75.00 * DR_TimerSpeed()
endfunction
//
// The height of the parabolic curve.
constant function DR_ParabolaHeight takes nothing returns real
    return 300.00
endfunction
//
// The initial damage when the missile lands.
constant function DR_MissileDamage takes nothing returns real
    return 30.00
endfunction
//
// The missile damage per level.
constant function DR_MissileDamagePerLevel takes nothing returns real
    return 25.00
endfunction
//
// The missile damage/attack/weapon types.
constant function DR_MissileDamageType takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC
endfunction
//
constant function DR_MissileAttackType takes nothing returns attacktype
    return ATTACK_TYPE_MAGIC
endfunction
//
constant function DR_MissileWeaponType takes nothing returns weapontype
    return WEAPON_TYPE_WHOKNOWS
endfunction
//
// The initial missile explosion AoE.
constant function DR_MissileExplosionAoE takes nothing returns real
    return 50.00
endfunction
//
// The missile explosion AoE per level.
constant function DR_MissileExplosionAoEPerLevel takes nothing returns real
    return 10.00
endfunction
//
// The missile model.
constant function DR_MissileModel takes nothing returns string
    return "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
endfunction
//
// The missile model attachment point.
constant function DR_MissileModelAttachmentPoint takes nothing returns string
    return "origin"
endfunction
//
// The missile explosion effect. The current setting is not having one because
// the missile model itself has an appropriate explosion.
constant function DR_MissileExplosionEffect takes nothing returns string
    return ""
endfunction
//
// The initial size of the missiles.
constant function DR_MissileSize takes nothing returns real
    return 0.30
endfunction
//
// The size of the missiles per level.
constant function DR_MissileSizePerLevel takes nothing returns real
    return 0.15
endfunction
//
//////////////////////////// Misc Configuration //////////////////////////////////
//
// The loop control integer values. It plays a big role in the StageID part of the
// spell, which divides the spell into seperate parts. The StageID variable helps
// implementing all the units (or parts, if you will) into a single linked list.
// It makes the linked list more sensitive when regarding maximum spell instances,
// but it will still be pretty hard to reach the max instance count. I believe the
// credit for this "system" goes to Tank-Commander, not sure, but he taught me 
// how to use it.
//
constant function DR_SpiritStageID takes nothing returns integer
    return 1
endfunction
//
constant function DR_MinionStageID takes nothing returns integer
    return 2
endfunction
//
constant function DR_MissileStageID takes nothing returns integer
    return 3
endfunction
//
// The integer buff used when adding 0.01 expiration timers to dummies we want to
// remove.
constant function DR_DyingBuffID takes nothing returns integer
    return 'BTLF'
endfunction
//
//////////////////////////////// END OF CONFIGURATION /////////////////////////////
//
// The code below can be configurable, but it does require some programming
// knowledge.


// Semi-configurable. It filters out the wrong targets when the spirit/missile
// damages.
function DR_TargetFilter takes unit u, player pl returns boolean
    return not(IsUnitType(u, UNIT_TYPE_DEAD)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and IsUnitEnemy(u, pl)
endfunction


// Function used to check if the point we want to create minions or move them in,
// is valid.
function DR_IsPointValid takes real x, real y returns boolean
    return x < udg_DR_MaxX and x > udg_DR_MinX and y < udg_DR_MaxY and y > udg_DR_MinY
endfunction

// Function used to get the location of the points used in the spell. It is meant
// to increase the visuals of the missile parabolic arc.
function DR_GetLocationZ takes real x, real y returns real
    call MoveLocation(udg_DR_Location, x, y)
    return GetLocationZ(udg_DR_Location)
endfunction


// Function used to recycle a no longer needed instance in the linked list.
function DR_Recycle takes integer Node returns nothing

    set udg_DR_RecycleNodes[udg_DR_RecyclableNodes] = Node
    set udg_DR_RecyclableNodes = udg_DR_RecyclableNodes + 1
    set udg_DR_NextNode[udg_DR_PrevNode[Node]] = udg_DR_NextNode[Node]
    set udg_DR_PrevNode[udg_DR_NextNode[Node]] = udg_DR_PrevNode[Node]
    
    if (udg_DR_NextNode[0] == 0) then
        call PauseTimer(udg_DR_Timer)
    endif
    
endfunction


// Function used to create a new instance in the linked list.
function DR_CreateNode takes nothing returns integer
    local integer Node = 0
        
    if (udg_DR_RecyclableNodes == 0) then
        set udg_DR_NodeNumber = udg_DR_NodeNumber + 1
        set Node = udg_DR_NodeNumber
    else
        set udg_DR_RecyclableNodes = udg_DR_RecyclableNodes - 1
        set Node = udg_DR_RecycleNodes[udg_DR_RecyclableNodes]
    endif
    
    set udg_DR_NextNode[Node] = 0
    set udg_DR_NextNode[udg_DR_PrevNode[0]] = Node
    set udg_DR_PrevNode[Node] = udg_DR_PrevNode[0]
    set udg_DR_PrevNode[0] = Node
    
    return Node
    
endfunction


// Main function of the spell, each time it fires, a small part of the spell takes
// place, resulting in a continuous chain of functions.
function DR_Loop takes nothing returns nothing
    // Setting up locals.
    local integer Node = 0
    local integer TempNode
    local real x
    local real y
    local real x2
    local real y2
    local real x3
    local real y3
    local real z
    local real z2
    local unit u
    local real dx
    local real dy
    local real totalD
    local real partialD
    local real angle
    local real offset
    local integer count = 0
    local integer random
    
    loop
    
        // Cycling through each node.
        set Node = udg_DR_NextNode[Node]
        exitwhen Node == 0
        
        // Checking if the spell has ended yet.
        if udg_DR_MinionDuration[Node] > 0.00 and not IsUnitType(udg_DR_Caster[Node], UNIT_TYPE_DEAD) and GetUnitTypeId(udg_DR_Caster[Node]) != 0 then
        
            // Checking if the stage ID is the spirit stage ID.
            if udg_DR_StageID[Node] == DR_SpiritStageID() then
                
                // Moving the spirit.
                set x = GetUnitX(udg_DR_Caster[Node])
                set y = GetUnitY(udg_DR_Caster[Node])
                set x2 = GetUnitX(udg_DR_Spirit[Node])
                set y2 = GetUnitY(udg_DR_Spirit[Node])
                set udg_DR_Angle[Node] = udg_DR_Angle[Node] - udg_DR_SpiritTurnRate[Node]
                set x3 = x + udg_DR_SpiritOffset[Node] * Cos(udg_DR_Angle[Node] * bj_DEGTORAD)
                set y3 = y + udg_DR_SpiritOffset[Node] * Sin(udg_DR_Angle[Node] * bj_DEGTORAD)
                
                // Checking if the point we want to move our spirits to, is valid.
                if DR_IsPointValid(x3, y3) then
                
                    call SetUnitX(udg_DR_Spirit[Node], x3)
                    call SetUnitY(udg_DR_Spirit[Node], y3)
                    call SetUnitFacing(udg_DR_Spirit[Node], Atan2(y3 - y2, x3 - x2) * bj_RADTODEG)
                    
                else
                
                    set udg_DR_Angle[Node] = udg_DR_Angle[Node] + udg_DR_SpiritTurnRate[Node]
                    call DestroyEffect(AddSpecialEffectTarget(DR_InvalidSpecialEffect(), udg_DR_Spirit[Node], DR_InvalidSEAttachmentPoint()))
                    
                endif
                                
                // Enumerating all units in range. The loop exits when all units don't pass the DR_TargetFilter() or
                // it found a valid target.
                if udg_DR_DuplicationCooldown[Node] < 0.00 then
                
                    if udg_DR_TotalCount[Node] < udg_DR_SpiritCount[Node] and udg_DR_EffectBoolean[Node] == true then
                        call GroupEnumUnitsInRange(udg_DR_TempGroup, x3, y3, udg_DR_SpiritAoE[Node], null)
                        loop
                    
                            set u = FirstOfGroup(udg_DR_TempGroup)
                            exitwhen u == null or udg_DR_Count[Node] == 1

                            if DR_TargetFilter(u, udg_DR_Player[Node]) then
                                set udg_DR_Count[Node] = 1
                                call UnitDamageTarget(udg_DR_Caster[Node], u, udg_DR_SpiritDamage[Node], true, false, DR_SpiritAttackType(), DR_SpiritDamageType(), DR_SpiritWeaponType())
                                call DestroyEffect(AddSpecialEffectTarget(DR_DuplicationFX(), u, DR_DuplicationAttachmentPoint()))
                            endif
                    
                            call GroupRemoveUnit(udg_DR_TempGroup, u)
                    
                        endloop
                    endif
                else
                    
                    // Subtracting the duplication cooldown.
                    set udg_DR_DuplicationCooldown[Node] = udg_DR_DuplicationCooldown[Node] - DR_TimerSpeed()
                    
                endif
                
                // Subtracting the total duration of this part of the spell.
                set udg_DR_SpiritDuration[Node] = udg_DR_SpiritDuration[Node] - DR_TimerSpeed()
                
                // Checking if the spirit has found an enemy unit. If yes -> duplicate.
                if udg_DR_Count[Node] == 1 then
                    set udg_DR_Count[Node] = 0
                    set udg_DR_DuplicationCooldown[Node] = DR_DuplicationCooldown()
                    set udg_DR_TotalCount[Node] = udg_DR_TotalCount[Node] + 1
                    set udg_DR_EffectBoolean[Node] = false
                    
                    // Lowering the scaling value of the spirit to make it stand out as inactive.
                    call SetUnitScale(udg_DR_Spirit[Node], DR_InactiveSpiritSize(), 0.00, 0.00)
                    
                    // Adding a new instance to the linked list.
                    set TempNode = DR_CreateNode()
                    
                    // Importing all the necessary data from the previous node.
                    set udg_DR_Count[TempNode]               = udg_DR_Count[Node]
                    set udg_DR_Caster[TempNode]              = udg_DR_Caster[Node]
                    set udg_DR_Player[TempNode]              = udg_DR_Player[Node]
                    set udg_DR_SpiritDuration[TempNode]      = udg_DR_SpiritDuration[Node]
                    set udg_DR_SpiritOffset[TempNode]        = udg_DR_SpiritOffset[Node]
                    set udg_DR_SpiritTurnRate[TempNode]      = udg_DR_SpiritTurnRate[Node] * (-1)
                    set udg_DR_Angle[TempNode]               = udg_DR_Angle[Node]
                    set udg_DR_SpiritCount[TempNode]         = udg_DR_SpiritCount[Node]
                    set udg_DR_TotalCount[TempNode]          = udg_DR_TotalCount[Node]
                    set udg_DR_SpiritAoE[TempNode]           = udg_DR_SpiritAoE[Node]
                    set udg_DR_SpiritDamage[TempNode]        = udg_DR_SpiritDamage[Node]
                    set udg_DR_MinionDuration[TempNode]      = udg_DR_MinionDuration[Node]
                    set udg_DR_MinionMS[TempNode]            = udg_DR_MinionMS[Node]
                    set udg_DR_SearchRadius[TempNode]        = udg_DR_SearchRadius[Node]
                    set udg_DR_AttackingRadius[TempNode]     = udg_DR_AttackingRadius[Node]
                    set udg_DR_AttackCooldown[TempNode]      = udg_DR_AttackCooldown[Node]
                    set udg_DR_MissileDamage[TempNode]       = udg_DR_MissileDamage[Node]
                    set udg_DR_MissileExplosionAoE[TempNode] = udg_DR_MissileExplosionAoE[Node]
                    set udg_DR_MissileSpeed[TempNode]        = udg_DR_MissileSpeed[Node]
                    set udg_DR_DuplicationCooldown[TempNode] = DR_DuplicationCooldown()
                    set udg_DR_EffectBoolean[TempNode]       = true
                    set udg_DR_AttackCounter[TempNode]       = udg_DR_AttackCounter[Node]
                    set udg_DR_MovingCounter[TempNode]       = udg_DR_MovingCounter[Node]
                    set udg_DR_MissileSize[TempNode]         = udg_DR_MissileSize[Node]
                    set udg_DR_MinionSize[TempNode]          = udg_DR_MinionSize[Node]
                    set udg_DR_StageID[TempNode] = DR_SpiritStageID()
                    
                    // Creating the duplicate spirit.
                    set udg_DR_Spirit[TempNode] = CreateUnit(DR_DummyPlayer(), DR_DummyID(), x + udg_DR_SpiritOffset[TempNode] * Cos(udg_DR_Angle[TempNode] * bj_DEGTORAD), y + udg_DR_SpiritOffset[TempNode] * Sin(udg_DR_Angle[TempNode] * bj_DEGTORAD), 0.00)
                    set udg_DR_FX[TempNode] = AddSpecialEffectTarget(DR_SpiritModel(), udg_DR_Spirit[TempNode], DR_SpiritModelAttachmentPoint())
                    call SetUnitFlyHeight(udg_DR_Spirit[TempNode], 50.00, 0.00)
                    call SetUnitScale(udg_DR_Spirit[TempNode], DR_SpiritSize(), 0.00, 0.00)
                    
                endif
                
                // Checking if this part of the spell has ended.
                if udg_DR_SpiritDuration[Node] < 0.00 then
    
                    // Adding a new instance to the linked list.
                    set TempNode = DR_CreateNode()
                    
                    // Importing all the necessary data from the spirit nodes.
                    set udg_DR_Caster[TempNode]              = udg_DR_Caster[Node]
                    set udg_DR_Player[TempNode]              = udg_DR_Player[Node]
                    set udg_DR_MinionDuration[TempNode]      = udg_DR_MinionDuration[Node]
                    set udg_DR_MinionMS[TempNode]            = udg_DR_MinionMS[Node]
                    set udg_DR_SearchRadius[TempNode]        = udg_DR_SearchRadius[Node]
                    set udg_DR_AttackingRadius[TempNode]     = udg_DR_AttackingRadius[Node]
                    set udg_DR_AttackCooldown[TempNode]      = udg_DR_AttackCooldown[Node]
                    set udg_DR_MissileDamage[TempNode]       = udg_DR_MissileDamage[Node]
                    set udg_DR_MissileExplosionAoE[TempNode] = udg_DR_MissileExplosionAoE[Node]
                    set udg_DR_MissileSpeed[TempNode]        = udg_DR_MissileSpeed[Node]
                    set udg_DR_Angle[TempNode]               = udg_DR_Angle[Node]
                    set udg_DR_AttackCounter[TempNode]       = udg_DR_AttackCounter[Node]
                    set udg_DR_MovingCounter[TempNode]       = udg_DR_MovingCounter[Node]
                    set udg_DR_EffectBoolean[TempNode]       = false
                    set udg_DR_MissileSize[TempNode]         = udg_DR_MissileSize[Node]

                    // Creating the minions & setting the stage ID to the minion stage ID.
                    set udg_DR_Minion[TempNode] = CreateUnit(DR_DummyPlayer(), DR_SummonedID(), x2, y2, udg_DR_Angle[Node])
                    call DestroyEffect(AddSpecialEffect(DR_SpawnEffect(), x2, y2))
                    call SetUnitScale(udg_DR_Minion[TempNode], udg_DR_MinionSize[Node], 0.00, 0.00)
                    call SetUnitMoveSpeed(udg_DR_Minion[TempNode], udg_DR_MinionMS[TempNode])
                    set udg_DR_StageID[TempNode] = DR_MinionStageID()
                    
                    // Recycling the spirit nodes.
                    call DestroyEffect(udg_DR_FX[Node])
                    call UnitApplyTimedLife(udg_DR_Spirit[Node], DR_DyingBuffID(), 0.01)
                    call DR_Recycle(Node)
                
                endif
            
            // Checking if the stage ID is the minion stage ID.
            elseif udg_DR_StageID[Node] == DR_MinionStageID() then
                
                // Subtracting minion duration and the attack cooldown.
                set udg_DR_MinionDuration[Node] = udg_DR_MinionDuration[Node] - DR_TimerSpeed()
                set udg_DR_AttackCounter[Node] = udg_DR_AttackCounter[Node] - DR_TimerSpeed()
                
                // Setting up the coordinates and calculating the distance.
                set x = GetUnitX(udg_DR_Minion[Node])
                set y = GetUnitY(udg_DR_Minion[Node])
                set x2 = GetUnitX(udg_DR_Caster[Node])
                set y2 = GetUnitY(udg_DR_Caster[Node])
                set dx = x2 - x
                set dy = y2 - y
                set totalD = SquareRoot(dx*dx + dy*dy)
                
                // Checking if the minion is ready to launch a missile.
                if udg_DR_AttackCounter[Node] < 0.00 and totalD < udg_DR_AttackingRadius[Node] then 
                
                    // Enumerating nearby units and randomizing one of them.
                    call GroupEnumUnitsInRange(udg_DR_TempGroup, x, y, udg_DR_SearchRadius[Node], null)
                    loop
                    
                        set u = FirstOfGroup(udg_DR_TempGroup)
                        exitwhen u == null
                        
                        if DR_TargetFilter(u, udg_DR_Player[Node]) then
                        
                            set count = count + 1
                            call GroupAddUnit(udg_DR_SwapGroup, u)
                            
                        endif
                        
                        call GroupRemoveUnit(udg_DR_TempGroup, u)
                        
                    endloop
                    
                    set random = GetRandomInt(0, count)
                    set count = 0
                    loop
                    
                        set u = FirstOfGroup(udg_DR_SwapGroup)
                        exitwhen u == null
                        set count = count + 1
                        
                        if count == random then
                        
                            set udg_DR_ConstantX[Node] = GetUnitX(u)
                            set udg_DR_ConstantY[Node] = GetUnitY(u)
                            set udg_DR_EffectBoolean[Node] = true
                            
                        endif
                        
                        call GroupRemoveUnit(udg_DR_SwapGroup, u)
                        
                    endloop
                    
                    // Checking if the minion found a valid target to launch the missile at.
                    if udg_DR_EffectBoolean[Node] == true then
                    
                        // Preparing to launch the missile.
                        set angle = GetRandomReal(0.00, 360.00)
                        set udg_DR_Angle[Node] = Atan2(udg_DR_ConstantY[Node] - y, udg_DR_ConstantX[Node] - x)
                        call IssueImmediateOrder(udg_DR_Minion[Node], "stop")
                        call SetUnitAnimation(udg_DR_Minion[Node], DR_AttackAnimation())
                        set udg_DR_AttackCounter[Node] = udg_DR_AttackCooldown[Node]
                        set udg_DR_EffectBoolean[Node] = false
                        
                        // Adding a new instance to the linked list.
                        set TempNode = DR_CreateNode()
                        
                        // Importing all the necessary data from the minion node.
                        set udg_DR_Caster[TempNode]              = udg_DR_Caster[Node]
                        set udg_DR_Player[TempNode]              = udg_DR_Player[Node]
                        set udg_DR_Missile[TempNode]             = udg_DR_Missile[Node]
                        set udg_DR_MinionDuration[TempNode]      = udg_DR_MinionDuration[Node]
                        set udg_DR_Minion[TempNode]              = udg_DR_Minion[Node]
                        set udg_DR_Angle[TempNode]               = udg_DR_Angle[Node]
                        set udg_DR_MissileDamage[TempNode]       = udg_DR_MissileDamage[Node]
                        set udg_DR_MissileExplosionAoE[TempNode] = udg_DR_MissileExplosionAoE[Node]
                        set udg_DR_MissileSpeed[TempNode]        = udg_DR_MissileSpeed[Node]
                        set udg_DR_ConstantX[TempNode]           = udg_DR_ConstantX[Node]
                        set udg_DR_ConstantY[TempNode]           = udg_DR_ConstantY[Node]
                        set udg_DR_MissileSize[TempNode]         = udg_DR_MissileSize[Node]
                        set udg_DR_StageID[TempNode]             = DR_MissileStageID()

                        // Creating a new missile.
                        set udg_DR_Missile[TempNode] = CreateUnit(DR_DummyPlayer(), DR_DummyID(), x, y, udg_DR_Angle[TempNode])
                        set udg_DR_FX[TempNode] = AddSpecialEffectTarget(DR_MissileModel(), udg_DR_Missile[TempNode], DR_MissileModelAttachmentPoint())
                        call SetUnitScale(udg_DR_Missile[TempNode], udg_DR_MissileSize[TempNode], 0.00, 0.00)
                        call SetUnitFacing(udg_DR_Missile[TempNode], udg_DR_Angle[TempNode] * bj_RADTODEG)
                        
                    endif
                
                // Checking if the minion is out of the attacking radius.
                elseif totalD > udg_DR_AttackingRadius[Node] then
                    
                    // Subtracing the moving cooldown.
                    set udg_DR_MovingCounter[Node] = udg_DR_MovingCounter[Node] - DR_TimerSpeed()
                    
                    // Checking if the unit is ready to move.
                    if udg_DR_MovingCounter[Node] < 0.00 then
                    
                        // Ordering the minion to move towards the caster & resetting the moving cooldown.
                        set offset = GetRandomReal(0.00, udg_DR_AttackingRadius[Node])
                        set angle = GetRandomReal(-bj_PI, bj_PI)
                        set x2 = x2 + offset * Cos(angle)
                        set y2 = y2 + offset * Sin(angle)
                        set angle = Atan2(y2 - y, x2 - x)
                        set x3 = x + totalD * Cos(angle)
                        set y3 = y + totalD * Sin(angle)
                        
                        // Checking if the point we want to move our minions to, is valid.
                        if DR_IsPointValid(x3, y3) then
                        
                            call IssuePointOrder(udg_DR_Minion[Node], "move", x3, y3)
                            set udg_DR_MovingCounter[Node] = DR_MovingCooldown()
                            
                        endif
                
                    endif
                
                endif
            
            // Checking if the stage ID is the missile stage ID.
            elseif udg_DR_StageID[Node] == DR_MissileStageID() then
                
                // Setting up the coordinates & calculating the distances.
                set x = GetUnitX(udg_DR_Missile[Node])
                set y = GetUnitY(udg_DR_Missile[Node])
                set x2 = GetUnitX(udg_DR_Minion[Node])
                set y2 = GetUnitY(udg_DR_Minion[Node])
                set dx = x2 - udg_DR_ConstantX[Node]
                set dy = y2 - udg_DR_ConstantY[Node]
                set totalD = SquareRoot(dx*dx + dy*dy)
                set dx = x2 - x
                set dy = y2 - y
                set partialD = SquareRoot(dx*dx + dy*dy)
                set z = DR_GetLocationZ(x2, y2) + GetUnitFlyHeight(udg_DR_Minion[Node])
                set z2 = DR_GetLocationZ(udg_DR_ConstantX[Node], udg_DR_ConstantY[Node])
                
                // Moving the missile based on the configuration speed in a parabolic curve.
                call SetUnitFlyHeight(udg_DR_Missile[Node], 4.00 * DR_ParabolaHeight() / totalD * (totalD - partialD) * (partialD / totalD) + partialD * (z2 - z) / (totalD + 1.00) + z, 0.00)
                call SetUnitX(udg_DR_Missile[Node], x + udg_DR_MissileSpeed[Node] * Cos(udg_DR_Angle[Node]))
                call SetUnitY(udg_DR_Missile[Node], y + udg_DR_MissileSpeed[Node] * Sin(udg_DR_Angle[Node]))
                
                // Checking if the missile has reached the destination.
                if totalD - partialD < 50.00 then
                
                    // Damaging all the units in the specified AoE.
                    call GroupEnumUnitsInRange(udg_DR_TempGroup, x, y, udg_DR_MissileExplosionAoE[Node], null)
                    loop
                    
                        set u = FirstOfGroup(udg_DR_TempGroup)
                        exitwhen u == null
                        
                        if DR_TargetFilter(u, udg_DR_Player[Node]) then
                            call UnitDamageTarget(udg_DR_Caster[Node], u, udg_DR_MissileDamage[Node], true, false, DR_MissileAttackType(), DR_MissileDamageType(), DR_MissileWeaponType())
                        endif
                        
                        call GroupRemoveUnit(udg_DR_TempGroup, u)
                        
                    endloop
                    
                    // Recycling the missile instance.
                    call DestroyEffect(udg_DR_FX[Node])
                    call UnitApplyTimedLife(udg_DR_Missile[Node], DR_DyingBuffID(), 0.01)
                    call DR_Recycle(Node)
                    
                endif
        
            endif
        
        else
        
            // Recycling the minion nodes and the leftover missile nodes.
            call UnitApplyTimedLife(udg_DR_Minion[Node], DR_DyingBuffID(), 0.01)
            if udg_DR_StageID[Node] == DR_MissileStageID() then
                call UnitApplyTimedLife(udg_DR_Missile[Node], DR_DyingBuffID(), 0.01)
                call DestroyEffect(udg_DR_FX[Node])
            endif
            call DR_Recycle(Node)
        
        endif
    
    endloop
    
endfunction


// The On Cast function. It is fired when a unit casts this spell. Creates a new
// instance using the DR_CreateNode function and initializes all the needed variables
// and units.
function DR_onCast takes nothing returns boolean
    // Setting up locals.
    local real x
    local real y
    local integer Node
    local integer level
    
    // Checking if the casted ability was the right one.
    if GetSpellAbilityId() == DR_AbilityID() then
        
        // Creating the new instance.
        set Node = DR_CreateNode()
        
        // Setting up the necessary variables for the spell.
        set udg_DR_Caster[Node] = GetTriggerUnit()
        set udg_DR_Player[Node] = GetTriggerPlayer()
        set x = GetUnitX(udg_DR_Caster[Node])
        set y = GetUnitY(udg_DR_Caster[Node])
        set level = GetUnitAbilityLevel(udg_DR_Caster[Node], DR_AbilityID())
        set udg_DR_DuplicationCooldown[Node] = 0.00
        set udg_DR_TotalCount[Node] = 1
        set udg_DR_StageID[Node] = DR_SpiritStageID()
        set udg_DR_Count[Node] = 0
        set udg_DR_EffectBoolean[Node] = true
        set udg_DR_ConstantX[Node] = 0.00
        set udg_DR_ConstantY[Node] = 0.00
        set udg_DR_MovingCounter[Node] = 0.00
        set udg_DR_AttackCounter[Node] = udg_DR_AttackCooldown[Node]
        
        // Calculating the spirit stats from the configuration.
        set udg_DR_SpiritDuration[Node] = DR_SpiritDuration() + DR_SpiritDurationPerLevel() * level
        set udg_DR_SpiritDamage[Node]   = DR_SpiritDamage()   + DR_SpiritDamagePerLevel()   * level
        set udg_DR_SpiritAoE[Node]      = DR_SpiritAoE()      + DR_SpiritAoEPerLevel()      * level
        set udg_DR_SpiritCount[Node]    = DR_SpiritCount()    + DR_SpiritCountPerLevel()    * level
        set udg_DR_SpiritTurnRate[Node] = DR_SpiritTurnRate() + DR_SpiritTurnRatePerLevel() * level
        set udg_DR_SpiritOffset[Node]   = DR_SpiritOffset()   + DR_SpiritOffsetPerLevel()   * level
        
        // Calculating the minion stats from the configuration.
        set udg_DR_MinionDuration[Node]      = DR_MinionDuration()      + DR_MinionDurationPerLevel()      * level
        set udg_DR_MinionMS[Node]            = DR_MinionMS()            + DR_MinionMSPerLevel()            * level
        set udg_DR_MinionSize[Node]          = DR_MinionSize()          + DR_MinionSizePerLevel()          * level
        set udg_DR_SearchRadius[Node]        = DR_SearchRadius()        + DR_SearchRadiusPerLevel()        * level
        set udg_DR_AttackingRadius[Node]     = DR_AttackingRadius()     + DR_AttackingRadiusPerLevel()     * level
        set udg_DR_AttackCooldown[Node]      = DR_AttackCooldown()      - DR_AttackCooldownPerLevel()      * level
        set udg_DR_MissileDamage[Node]       = DR_MissileDamage()       + DR_MissileDamagePerLevel()       * level
        set udg_DR_MissileExplosionAoE[Node] = DR_MissileExplosionAoE() + DR_MissileExplosionAoEPerLevel() * level
        set udg_DR_MissileSpeed[Node]        = DR_MissileSpeed()        + DR_MissileSpeedPerLevel()        * level
        set udg_DR_MissileSize[Node]         = DR_MissileSize()         + DR_MissileSizePerLevel()         * level
        
        // Creating the first spirit.
        set udg_DR_Angle[Node] = GetRandomReal(0.00, 360.00)
        set udg_DR_Spirit[Node] = CreateUnit(DR_DummyPlayer(), DR_DummyID(), x + udg_DR_SpiritOffset[Node] * Cos(udg_DR_Angle[Node] * bj_DEGTORAD), y + udg_DR_SpiritOffset[Node] * Sin(udg_DR_Angle[Node] * bj_DEGTORAD), 0.00)
        set udg_DR_FX[Node] = AddSpecialEffectTarget(DR_SpiritModel(), udg_DR_Spirit[Node], DR_SpiritModelAttachmentPoint())
        call SetUnitFlyHeight(udg_DR_Spirit[Node], 50.00, 0.00)
        call SetUnitScale(udg_DR_Spirit[Node], DR_SpiritSize(), 0.00, 0.00)
        
        // Checking if this is the first instace.
        if (udg_DR_PrevNode[Node] == 0) then
            call TimerStart(udg_DR_Timer, DR_TimerSpeed(), true, function DR_Loop)
        endif
        
    endif
    
    return false
    
endfunction


// The initialization function
function InitTrig_Demonic_Ritual takes nothing returns nothing

    local trigger DR = CreateTrigger()
    call TriggerAddCondition(DR, Condition(function DR_onCast))
    call TriggerRegisterAnyUnitEventBJ(DR, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    
    // Setting up the location variable to check for the terrain height.
    set udg_DR_Location = Location(0.00, 0.00)
    
    // Setting up the min/max coordinates of the map area.
    set udg_DR_MinX = GetRectMinX(bj_mapInitialPlayableArea)
    set udg_DR_MaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set udg_DR_MinY = GetRectMinY(bj_mapInitialPlayableArea)
    set udg_DR_MaxY = GetRectMaxY(bj_mapInitialPlayableArea)
    
endfunction
giphy.gif
Attached is the demo map with a few enemy units dropped so you can test it.
Leave feedback if you want to.
Good luck to other participants, can't wait to see their entries!
 

Attachments

  • Meatmuffin + Demonic Ritual.w3x
    53.6 KB · Views: 100
Last edited:

Ardenian

A

Ardenian

Well, if you choose to have in mind that the "player selects unit" event is kinda buggy if I recall.

I think if you hold in left click and select all units with the rectangle-tool (or whatever it's called) the event will fire for all units in that rectangle at once.

Oh, good to know, thank you!
I am thinking about changing it then, maybe with attacking a dummy unit or
else I might use trackables.
 
Level 13
Joined
Jun 20, 2014
Messages
479
hi guys, ive decided to change my concept a bit and turns out i screwed up myself. the concept is easy but it takes too long to code especially if procrastinating haha. hows everyone?

Edit: omg i cant resist it. i need you to see it.

Call of the Damned 2.png
 
Last edited:

Ardenian

A

Ardenian

Trash code WIP

Already forgot what I did, maybe I will just rush the code creation once,
taking a few hours for it and finish it before I forget everything again.


JASS:
globals
    integer udg_DE_StatUnitIndex
    integer udg_DE_StatUnitRecycleIndex
    integer udg_DE_CasterIndex
    integer array udg_DE_StatUnitCount
    real array udg_DE_StatUnitFacing 
    real array udg_DE_StatUnitRange 
    unit array udg_DE_StatUnit
    unit array udg_DE_CasterUnit
    unittype array udg_DE_StatUnitType
    ability udg_DE_Ability
    
    trigger gg_trg_OnCast
    trigger gg_trg_Update_DE
endglobals

//===========================================================================

function CreateStatUnit takes real x, real y, player owner, integer i, real alpha returns nothing
    // create the position
    set x = x + Cos(alpha) * udg_DE_StatUnitRange[i]
    set y = y + Sin(alpha) * udg_DE_StatUnitRange[i]
    // if a recycled unit is available, get it, else create new one
    set udg_DE_StatUnitIndex = udg_DE_StatUnitIndex + 1
    if udg_DE_StatUnitRecycleIndex != 0 then // a recycled unit is available
        set udg_DE_StatUnit[udg_DE_StatUnitIndex] = udg_DE_StatUnit[udg_DE_StatUnitRecycleIndex]
        set udg_DE_StatUnitRecycleIndex = udg_DE_StatUnitRecycleIndex - 1
        call SetUnitX(udg_DE_StatUnit[udg_DE_StatUnitIndex], x)
        call SetUnitY(udg_DE_StatUnit[udg_DE_StatUnitIndex], y)
        call ShowUnit(udg_DE_StatUnit[udg_DE_StatUnitIndex], true)
    else // no recycled unit is available, create a new one
        set udg_DE_StatUnit[udg_DE_StatUnitIndex] = CreateUnit(owner,udg_DE_StatUnitType[i],x,y,udg_DE_StatUnitFacing[i]) 
    endif
endfunction

// This trigger runs when a unit casts the spell Demonic Empowering
function Trig_OnCast_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == udg_DE_Ability ) ) then
        return false
    endif
    return true
endfunction

function Trig_OnCast_Actions takes nothing returns nothing
    local real x = 0.00
    local real y = 0.00
    local real alpha = 0.00
    local integer abilevel = 0
    local integer i = 0
    local player owner = GetOwningPlayer(GetSpellAbilityUnit())
    
    // index caster
    set udg_DE_CasterIndex = udg_DE_CasterIndex + 1
    set udg_DE_CasterUnit[udg_DE_CasterIndex] = GetSpellAbilityUnit()
       
    // create stat units
    set x = GetUnitX(udg_DE_CasterUnit[udg_DE_CasterIndex])
    set y = GetUnitY(udg_DE_CasterUnit[udg_DE_CasterIndex])
    set abilevel = GetUnitAbilityLevel(udg_DE_CasterUnit[udg_DE_CasterIndex],udg_DE_Ability)
    loop
    exitwhen i > udg_DE_StatUnitCount[abilevel]
        set alpha = alpha + 360/udg_DE_StatUnitCount[abilevel]
        call CreateStatUnit(x,y,owner,i,alpha)
        set i = i + 1
    endloop
    
    //create special effect dummies
    
endfunction

function InitTrig_OnCast takes nothing returns nothing
    set gg_trg_OnCast = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_OnCast, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_OnCast, Condition( function Trig_OnCast_Conditions ) )
    call TriggerAddAction( gg_trg_OnCast, function Trig_OnCast_Actions )
endfunction

//===========================================================================

// This trigger updates the stat unit positions and the special effects
function Trig_Update_DE_Actions takes nothing returns nothing
    local integer i = 1
    loop
    exitwhen i > udg_DE_CasterIndex
        if GetUnitState(udg_DE_CasterUnit[i], UNIT_STATE_LIFE) > 0 then // (!) potential problem
        
        else // deindex caster and stat units
            //loop
//            exitwhen
//            
//            endloop
            set udg_DE_CasterUnit[i] = udg_DE_CasterUnit[udg_DE_CasterIndex]
            set udg_DE_CasterIndex = udg_DE_CasterIndex - 1 
        endif
        set i = i + 1
    endloop    
endfunction

function InitTrig_NewTrigger takes nothing returns nothing
    set gg_trg_Update_DE = CreateTrigger()
    call TriggerRegisterTimerEventPeriodic(gg_trg_Update_DE, 0.03125 ) 
    call TriggerAddAction(gg_trg_Update_DE, function Trig_Update_DE_Actions)
endfunction


About that multiple selection problem, I should be able to workaround it with checking
how many units a player has selected when the event fires.
 
Level 19
Joined
Jul 2, 2011
Messages
2,162
hi guys, ive decided to change my concept a bit and turns out i screwed up myself. the concept is easy but it takes too long to code especially if procrastinating haha. hows everyone?

Edit: omg i cant resist it. i need you to see it.

View attachment 154335

now that is pretty amazing

maybe I'll try and compete. my summoning is very simular. actually as a concept both of our ideas are pretty unoriginal but so what at least they look good
 
Level 22
Joined
Aug 27, 2013
Messages
3,973
hi guys, ive decided to change my concept a bit and turns out i screwed up myself. the concept is easy but it takes too long to code especially if procrastinating haha. hows everyone?

Edit: omg i cant resist it. i need you to see it.

View attachment 154335
I was planning to change my concept from nature themed spell to undead themed spell but it seems the effects I had in mind were similar to yours and you've beaten me to it because damn, it looks unholy shit!

OMG
so many of you guys are submitting their entries already and I didn't even start yet.
same here and the entries are amazing.
 
I've compiled a Table for keeping track of the contest - I'll maintain it as the contest progresses
Entry links restored - WIP links still broken from the transfer - will fix them shortly

AlmiaCompleted entry1 2 3 4 5 6Summon Devourer
ArdenianDropped out1 2None
ChaosyCompleted entry1 2 3 4Alchemist's Creation
crabas saktiCompleted entry1 2 3Summon Ectoplasm
Daffa the MageWorking on entry1None
DD_legionTNCompleted entry1 2 3Elemental Chaos
Emm-A-Completed entry1Chicken Rage
EmpireanCompleted entry1 2Call of the Damned
FluxCompleted entry1Aerial Aid
GhostHunter123Working on entry1None
IcemanBoHas a conceptNoneNone
KILLCIDECompleted entry1 2Ice Anomaly
Kyrbri0Completed entry1Mother's Love
Loner-MagixxarCompleted entry1 2 3Stone Integrity
Master TrainerDropped out1None
MeatmuffinCompleted entry1 2 3 4Demonic Ritual
OfelWorking on entry1 2None
RheikoCompleted entry1 2Hellfire Orb
RufusWorking on entry1None
Shar DundredHas a conceptNoneNone
Tank-CommanderCompleted entry1 2 3 4 5Nano Plague
WarseekerHas a conceptNoneNone
xxdingo93xxCompleted entry1 2 3Void Undertow
ZerGreenOneWorking on entry1None
[TD]Competitor[/TD][TD]Development Stage[/TD][TD]WIPs[/TD][TD]Entry[/TD]
 
Last edited:

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,183
Final entry.

This has to be one of the ugliest pieces of code I've ever written.
I am pleased most other aspects though.





Alchemist's Creation
Target Unit
icons_18431_btn.jpg
Putting certain combination of items into the Alchemist's Cauldron (building) will craft a minion to serve you.
Felweed + Mana Potion = FelHound
Ring or Protection + Frost Orb = Ice Reveant
etc

JASS:
/**************************************************************************************************
*                      Alchemist's Creation
*
*	Author:
*		Chaosy
*
*   Requires:
*       Linked List
*    
*	Optional
*		FloatingTextArc
*       TimedUnitScale
*       AlchCraft
*
*	Importing Instructions
*		Copy the Alchcore library into your map.
*		Copy the cauldron unit into your map.
*		(optional) copy the triggers from the "Optional" folder into your map.	
*
*	Author's note(s):
*		I've decided to split this into two parts where "AlchCore" contains the core content
*		as the name implies. "AlchCraft" is an addon which adds additional features such as fail
*		rate, a chance of summoning two units and so on.
*		The reason for this was that I did not want to force a lot of features that perhaps wont be
*		needed.
*		I've also chosen most libraries to be optional once more because I don't want to force
*		their usage, they will bring funtionality and/or visuals but are not required for the
*		system to work properly.
*
*	Config:
*
*		The id of the unit with the cauldron model.
*			public constant integer cauldron = 'H000';
*
*		The id of the ability used for crafting.
*			public constant integer craftAbility = 'H000';
*
*		Chance of the cauldron getting destroyed, note that it's 25% if the craft fails.
*		Which means, 25 of the fail rate, which is also 25% by default. So 25% out of 25%.
*		This variable is only needed if you intend to import AlchCraft into your map.
*			real destructionRate    = 25; //destroys the cauldron
*
*		Chance of the craft failing, resulting in no summoned unit.
*		This variable is only needed if you intend to import AlchCraft into your map.
*			real failRate           = 25;
*			
*		Chance of summoning two units instead of one upon a successful craft.
*		This variable is only needed if you intend to import AlchCraft into your map.
*			real dublicateChance    = 25;
*			
*		
*		The lifespan of a summoned unit, setting this to 0 will make the unit permanent.
*			real timedLife          = 0; // 0 = permanent (no timer)
*			
*		When a unit is created the size is really small at first, 0.5 = 50% of the original size.
*		This variable is only needed if you intend to add TimedUnitScale and it's requirements.
*			real minStartScale      = 0.1;
*			
*		When a unit it gets upsized to it's normal scale by default.
*		This can be changed though. 1 = 100% (normal unit size)
*		This variable is only needed if you intend to add TimedUnitScale and it's requirements.
*			real minEndScale        = 1;
*			
*		The time it takes for the unit scaling.
*		This variable is only needed if you intend to add TimedUnitScale and it's requirements.
*			real scaleDuration      = 1;
*			
*		The effect used when a unit is summoned.
*			string spawnEffect      = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl";
*			
*		The effect used when the summoning fails.
*			string failEffect       = "Units\\NightElf\\Wisp\\WispExplode.mdl";
*			
*		A method can be called when a unit is summoned, this allows for additional features to
*		be added if the user so wish. This is left empty by default but can be set to:
*		Struct.method which will then be called by the system automatically. This is unique to
*		each unit. Check the spell demo for an example of this.
*			craftFunc callback;
***************************************************************************************************
*/

globals
	constant string itemEffect       = "Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl"
	constant string green            = "|c0096FF96"
	constant string yellow           = "|c00FFFF00"
	constant string red              = "|c00FF0000"
	constant string blue             = "|c007EBFF1"
	
	constant string end              = "|r"

endglobals

//! zinc
	library AlchCore requires LinkedList, optional FloatingTextArc, optional TimedUnitScale, optional AlchCraft
	{
	
		type craftFunc extends function(unit, integer);
		type crafterFunc extends function(unit, unit, integer);
		public List recipes;
		constant integer cauldron        = 'H000';
		public constant integer craftAbility    = 'A000';
				
		public function PrepareCraft(unit u, unit crafter)
		{
			integer i = 0;
			Link x    = recipes.first;
			boolean detect = false;
			
			while(i < recipes.size)
			{
				if(ItemRecipe(x.data).requiredLevel <= GetUnitAbilityLevel(crafter, craftAbility)
				&& ItemRecipe(x.data).Craft(u, crafter) == true)
				{
					detect = true;
				}
				x = x.next;
				i = i + 1;
			}
			
			if(!detect)
			{
				static if(LIBRARY_FloatingTextArc)
				{
					ArcingTextTag.create(yellow + "That combination of items is invalid." + end, u);
				}
			}
		}
		
		function CraftInterface(unit u, integer i, craftFunc c)
		{
			c.evaluate(u, i);
		}
		
		function CrafterInterface(unit u, unit crafter, integer i, crafterFunc c)
		{
			c.evaluate(u, crafter, i);
		}
		
		function CastActions() -> boolean
		{
			unit u = GetSpellTargetUnit();
			if(GetSpellAbilityId() == craftAbility && GetUnitTypeId(u) == cauldron)
			{
				PrepareCraft(u, GetTriggerUnit());
			}
			else
			{
				static if(LIBRARY_FloatingTextArc)
				{
					ArcingTextTag.create(blue + "Invalid target, target a cauldron." + end, u);
				}
			}
			u = null;
			return false;
		}
		
		function PickActions()
		{
			unit u = GetManipulatingUnit();
			if(GetUnitTypeId(u) == cauldron)
			{
				DestroyEffect(AddSpecialEffect(itemEffect, GetUnitX(u), GetUnitY(u)));
				static if(LIBRARY_FloatingTextArc)
				{
					ArcingTextTag.create(blue + "Added: "
					+ yellow + GetItemName(GetManipulatedItem()) + end + end, u);
				}
				SetSoundPosition(gg_snd_brew, GetUnitX(u), GetUnitY(u), 0);
				SetSoundVolume(gg_snd_brew, PercentToInt(50, 127));
				StartSound(gg_snd_brew);
			}
			u = null;
		}
		
		function onInit()
		{
			trigger t = CreateTrigger();
			TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM);
			TriggerAddAction(t, function PickActions);
			
			t = CreateTrigger();
			TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
			TriggerAddCondition(t, Condition(function CastActions));
			
			recipes = List.create();
			t = null;
		}
		
		public struct ItemRecipe
		{
			optional module CraftVariables;
			unit last;
			List parts;
			integer summon;
			real minStartScale      = 0.1;
			real minEndScale        = 1;
			real scaleDuration      = 1;
			real timedLife          = 0; // 0 = permanent (no timer)
			integer requiredLevel   = 1;
			string spawnEffect      = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl";
			craftFunc callback;
			public method Craft(unit u, unit crafter) -> boolean
			{
				integer i = 0, j = 1;
				boolean check = true, found = false;
				boolean taken [];
				real x, y;
				Link l = parts.first;
				while(i < parts.size)
				{
					while(j < 7)
					{
						if(!taken[j] && GetItemTypeId(UnitItemInSlot(u, j - 1)) == l.data)
						{
							taken[j] = true;
							found    = true;
							break;
						}
						j += 1;
					}
					
					j = 1;
					
					if(!found)
					{
						check = false;
						break;
					}
					
					found = false;
					l = l.next;
					i += 1;
				}
				
				if(check)
				{
					x = GetUnitX(u);
					y = GetUnitY(u);
					static if(LIBRARY_AlchCraft)
					{
						CraftInterface(u, this, addonOnCraft);
					}
					else
					{
						last = CreateUnit(GetOwningPlayer(u), summon, x, y, 0);
						static if(LIBRARY_TimedUnitScale)
						{
							UnitSetScale(last, minStartScale, 0);
							UnitSetScale(last, minEndScale, scaleDuration);
						}
						if(this.timedLife > 0)
						{
							UnitApplyTimedLife(last, 'BTLF', timedLife);
						}
						static if(LIBRARY_FloatingTextArc)
						{
							ArcingTextTag.create(blue + "Success! You created: "
							+ yellow + GetUnitName(last) + end + end, u);
						}
					}
					static if(LIBRARY_Modification)
					{
						CrafterInterface(u, crafter, this, callback);
					}
					l = this.parts.first;
					i = 0;
					while(i < this.parts.size)
					{
						RemoveItem(GetItemOfTypeFromUnitBJ(u, l.data));
						l = l.next;
						i += 1;
					}
				}
				return check;
			}
			
			method add(integer part)
			{
				Link.create(parts, part);
			}
			
			static method create(integer result) -> thistype
			{
				thistype this = thistype.allocate();
				summon        = result;
				parts         = List.create();
				Link.create(recipes, this);
				return this;
			}
		}
	}
//! endzinc
JASS:
//! zinc
	library AlchCraft requires FloatingTextArc, optional TimedUnitScale, optional FloatingTextArc
	{
		type craftFunc extends function(unit, unit, integer);
		public module CraftVariables
		{
			real destructionRate    = 25; //destroys the cauldron
			real failRate           = 25;
			real dublicateChance    = 25;
			string failEffect       = "Units\\NightElf\\Wisp\\WispExplode.mdl";
			craftFunc addonOnCraft  = AdvancedCraft.onCraft;
		}
		
		public struct AdvancedCraft
		{
			static unit first;
			static unit second;
			public static method onCraft(unit u, integer i)
			{
				ItemRecipe this = ItemRecipe(i);
				real luck, x, y;
				integer j = 0;
				Link l;
				luck   = GetRandomReal(1,100);
				x      = GetUnitX(u);
				y      = GetUnitY(u);
				if(luck > this.failRate)
				{
					first = CreateUnit(GetOwningPlayer(u), this.summon, x, y, 0);
					static if(LIBRARY_TimedUnitScale)
					{
						UnitSetScale(first, this.minStartScale, 0);
						UnitSetScale(first, this.minEndScale, this.scaleDuration);
					}
					if(this.timedLife > 0)
					{
						UnitApplyTimedLife(first, 'BTLF', this.timedLife);
					}
				
					luck = GetRandomReal(1,100);
					DestroyEffect(AddSpecialEffect(this.spawnEffect, x, y));
					if(luck  < this.dublicateChance)
					{
						second = CreateUnit(GetOwningPlayer(u), this.summon, x, y, 0);
						static if(LIBRARY_TimedUnitScale)
						{
							UnitSetScale(second,this. minStartScale, 0);
							UnitSetScale(second, this.minEndScale  , this.scaleDuration);
						}
						static if(LIBRARY_FloatingTextArc)
						{
							ArcingTextTag.create(green   + "Perfect brew! 2x: "
							+ yellow + GetUnitName(second) + end + end, u);
						}
						if(this.timedLife > 0)
						{
							UnitApplyTimedLife(second, 'BTLF', this.timedLife);
						}
					}
					else
					{
						static if(LIBRARY_FloatingTextArc)
						{
							ArcingTextTag.create(blue + "Success! You created: "
							+ yellow + GetUnitName(first) + end + end, u);
						}
					}
				}
				else
				{
					luck = GetRandomReal(1,100);
					if(luck < this.destructionRate)
					{
						UnitApplyTimedLife(u, 'BTLF', 0.01);
						static if(LIBRARY_FloatingTextArc)
						{
							ArcingTextTag.create(red + "Accident! Kaboom~" + end, u);
						}
					}
					else
					{
						DestroyEffect(AddSpecialEffect(this.failEffect, x, y));
						static if(LIBRARY_FloatingTextArc)
						{
							ArcingTextTag.create(red + "Failed!" + end, u);
						}
					}
				}
			}
		}
	}
//! endzinc
 

Attachments

  • Alchemy.w3x
    61.4 KB · Views: 97
Last edited:
Level 13
Joined
Jun 20, 2014
Messages
479
WIP Post #2

Hey, heres a prototype of my spell that ive been working on yesterday. its experimental and i know there is no way it will work in this approach. i just wanna document it by uploading it in this thread before i scrap it. By the way, i would like to define my slight change of concept.

Concept:
The caster creates a summoning circle in an attempt to summon beings from another realm. Acolytes may aid the caster by stepping on one of the summoning points. As a result, the summoning time is reduced and more creatures are summoned. The reduction time and additional creatures is equal to the number of acolytes helping the caster. Each level the number of acolytes is increased.

Here are my experimental triggers

  • Call of the Damned Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- --------
      • -------- ABILITY --------
      • -------- base ability --------
      • Set CD_Ability = Call of the Damned
      • -------- order of the ability --------
      • Set CD_Order = starfall
      • -------- ----------------------------------------- --------
      • -------- --------
      • -------- SUMMONED UNITS --------
      • -------- summoned unit type --------
      • Set CD_SummonedUnit[1] = Doom Guard
      • Set CD_SummonedUnit[2] = Doom Guard
      • Set CD_SummonedUnit[3] = Doom Guard
      • Set CD_SummonedUnit[4] = Doom Guard
      • -------- number of summoned unit --------
      • Set CD_SummonedUnitCount[1] = 1
      • Set CD_SummonedUnitCount[2] = 1
      • Set CD_SummonedUnitCount[3] = 1
      • Set CD_SummonedUnitCount[4] = 1
      • -------- additional summoned unit --------
      • Set CD_BonusUnit[1] = Fel Beast
      • Set CD_BonusUnit[2] = Fel Beast
      • Set CD_BonusUnit[3] = Fel Beast
      • Set CD_BonusUnit[4] = Fel Beast
      • -------- additional unit count per helper --------
      • Set CD_BonusUnitCount[1] = 1
      • Set CD_BonusUnitCount[2] = 1
      • Set CD_BonusUnitCount[3] = 1
      • Set CD_BonusUnitCount[4] = 1
      • -------- ----------------------------------------- --------
      • -------- --------
      • -------- CASTING --------
      • -------- time required to complete the summoning --------
      • Set CD_CastingTime[1] = 30.00
      • Set CD_CastingTime[2] = 30.00
      • Set CD_CastingTime[3] = 30.00
      • Set CD_CastingTime[4] = 30.00
      • -------- casting time reduced by each helper --------
      • Set CD_CastingTimeReduction[1] = 2.00
      • Set CD_CastingTimeReduction[2] = 2.00
      • Set CD_CastingTimeReduction[3] = 2.00
      • Set CD_CastingTimeReduction[4] = 2.00
      • -------- number of helpers --------
      • Set CD_Helpers[1] = 2
      • Set CD_Helpers[2] = 3
      • Set CD_Helpers[3] = 4
      • Set CD_Helpers[4] = 5
      • -------- ----------------------------------------- --------
      • -------- --------
      • -------- AREA OF EFFECT --------
      • -------- summoning circle area of effect --------
      • Set CD_AoE[1] = 500.00
      • Set CD_AoE[2] = 500.00
      • Set CD_AoE[3] = 500.00
      • Set CD_AoE[4] = 500.00
      • -------- the size of the help detection --------
      • Set CD_HelpAoE[1] = 100.00
      • Set CD_HelpAoE[2] = 100.00
      • Set CD_HelpAoE[3] = 100.00
      • Set CD_HelpAoE[4] = 100.00
      • -------- the landing spot offset of the additional units relative to the helper towards the center --------
      • Set CD_BonusOffset[1] = 250.00
      • Set CD_BonusOffset[2] = 250.00
      • Set CD_BonusOffset[3] = 250.00
      • Set CD_BonusOffset[4] = 250.00
      • -------- size adjustment of the summoning circle --------
      • Set CD_CircleSize[1] = 300.00
      • Set CD_CircleSize[2] = 300.00
      • Set CD_CircleSize[3] = 300.00
      • Set CD_CircleSize[4] = 300.00
      • -------- --------
      • -------- AESTHETICS --------
      • -------- SUMMONING CIRCLE --------
      • -------- circle transparency --------
      • Set CD_CircleTransparency = 1.00
      • -------- model used by the summoning circle --------
      • Set CD_CenterSfx = SharedModels\UBirth.mdl
      • -------- model used when the units are successfully summoned --------
      • Set CD_SpawnSfx = Abilities\Spells\Undead\AnimateDead\AnimateDeadTarget.mdl
      • -------- special effect model in the center of the summoning cicle for aesthetics --------
      • Set CD_CircleGroundSfx = Abilities\Spells\Undead\Unsummon\UnsummonTarget.mdl
      • -------- HELPERS --------
      • -------- model for the help area indicator --------
      • Set CD_HelpSfx = Abilities\Spells\Undead\Darksummoning\DarkSummonTarget.mdl
      • -------- model indication that the help spot has been occupied --------
      • Set CD_HelpedSfx = Abilities\Spells\Items\TomeOfRetraining\TomeOfRetrainingCaster.mdl
      • -------- helper's animation when helping --------
      • Set CD_GhostAnim = work
      • -------- animation call interval --------
      • Set CD_GhostInterval = 2.26
      • -------- ----------------------------------------- --------
      • -------- --------
      • -------- SOUL --------
      • -------- soul height --------
      • Set CD_SoulMaxHeight = 500.00
      • -------- soul rotation for aesthetics --------
      • Set CD_Clockwise = True
      • -------- model used by the souls --------
      • Set CD_SoulSfx = Abilities\Weapons\ZigguratMissile\ZigguratMissile.mdl
      • -------- turn rate of the souls --------
      • Set CD_TurnRate = 3.00
      • -------- time before the souls reaches the center/edge --------
      • Set CD_SoulDelay = 1.00
      • -------- rate at which the soul gain/lose altitude --------
      • Set CD_SoulHeightSpeed = 5.00
      • -------- ----------------------------------------- --------
      • -------- --------
      • -------- DUMMY UNITS --------
      • -------- unit type of the helper --------
      • Set CD_HelperType = Acolyte
      • -------- alternate unit substitute to the helper --------
      • Set CD_HelperGhost = Ghost
      • -------- dummy unit type on where the model will be attached to --------
      • Set CD_CircleDummyType = Dummy



  • Call of the Damned Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Call of the Damned
    • Actions
      • -------- --------
      • Set CD_Index = (CD_Index + 1)
      • -------- --------
      • Set CD_Caster[CD_Index] = (Triggering unit)
      • Set CD_Player[CD_Index] = (Triggering player)
      • Set CD_Level[CD_Index] = (Level of CD_Ability for CD_Caster[CD_Index])
      • -------- --------
      • Set CD_Point1 = (Position of CD_Caster[CD_Index])
      • Set CD_Point2 = (CD_Point1 offset by CD_AoE[CD_Level[CD_Index]] towards (Facing of CD_Caster[CD_Index]) degrees)
      • -------- --------
      • Set CD_Angle[CD_Index] = (Angle from CD_Point2 to CD_Point1)
      • -------- --------
      • -------- soul --------
      • -------- --------
      • Unit - Create 1 CD_CircleDummyType for CD_Player[CD_Index] at CD_Point1 facing CD_Angle[CD_Index] degrees
      • Set CD_Soul[CD_Index] = (Last created unit)
      • Unit - Add Storm Crow Form to CD_Soul[CD_Index]
      • Unit - Remove Storm Crow Form from CD_Soul[CD_Index]
      • Special Effect - Create a special effect attached to the origin of CD_Soul[CD_Index] using CD_SoulSfx
      • Set CD_SoulEffect[CD_Index] = (Last created special effect)
      • Special Effect - Create a special effect at CD_Point1 using CD_HelpSfx
      • Set CD_HelpEffect[CD_Index] = (Last created special effect)
      • -------- --------
      • -------- circle --------
      • -------- --------
      • Unit - Create 1 CD_CircleDummyType for CD_Player[CD_Index] at CD_Point2 facing CD_Angle[CD_Index] degrees
      • Set CD_Circle[CD_Index] = (Last created unit)
      • Animation - Change CD_Circle[CD_Index]'s size to (CD_CircleSize[CD_Level[CD_Index]]%, 100.00%, 100.00%) of its original size
      • Animation - Change CD_Circle[CD_Index]'s vertex coloring to (100.00%, 100.00%, 100.00%) with CD_CircleTransparency% transparency
      • Special Effect - Create a special effect attached to the origin of CD_Circle[CD_Index] using CD_CenterSfx
      • Set CD_CircleEffect1[CD_Index] = (Last created special effect)
      • Special Effect - Create a special effect at CD_Point2 using CD_CircleGroundSfx
      • Set CD_CircleEffect2[CD_Index] = (Last created special effect)
      • -------- --------
      • -------- clean up --------
      • -------- --------
      • Custom script: call RemoveLocation(udg_CD_Point1)
      • Custom script: call RemoveLocation(udg_CD_Point2)
      • -------- --------
      • -------- logic --------
      • -------- --------
      • Set CD_ActionId[CD_Index] = 0
      • Set CD_TimeLeft[CD_Index] = (CD_TimeLeft[CD_Index] + CD_CastingTime[CD_Level[CD_Index]])
      • Set CD_AngleAdjustment[CD_Index] = (360.00 / ((Real(CD_Helpers[CD_Level[CD_Index]])) + 1.00))
      • Set CD_Complete[CD_Index] = False
      • Set CD_SoulAngle[CD_Index] = CD_Angle[CD_Index]
      • Set CD_SoulDistance[CD_Index] = CD_AoE[CD_Level[CD_Index]]
      • Set CD_SoulSpeed[CD_Index] = (CD_AoE[CD_Level[CD_Index]] / (CD_SoulDelay / 0.03))
      • Set CD_SoulCurrentHeight[CD_Index] = 0.00
      • Set CD_SoulIn[CD_Index] = True
      • Set CD_SoulUp[CD_Index] = True
      • -------- --------
      • -------- run --------
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CD_Index Equal to 1
        • Then - Actions
          • Trigger - Turn on Call of the Damned Effect <gen>
        • Else - Actions



  • Call of the Damned Effect
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • For each (Integer CD_Looper1) from 1 to CD_Index, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Current order of CD_Caster[CD_Looper1]) Equal to (Order(CD_Order))
            • Then - Actions
              • -------- --------
              • -------- soul animation --------
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • CD_Clockwise Equal to True
                • Then - Actions
                  • Set CD_SoulAngle[CD_Looper1] = (CD_SoulAngle[CD_Looper1] - CD_TurnRate)
                • Else - Actions
                  • Set CD_SoulAngle[CD_Looper1] = (CD_SoulAngle[CD_Looper1] + CD_TurnRate)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • CD_SoulIn[CD_Looper1] Equal to True
                • Then - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • CD_SoulDistance[CD_Looper1] Greater than 0.00
                    • Then - Actions
                      • Set CD_SoulDistance[CD_Looper1] = (CD_SoulDistance[CD_Looper1] - CD_SoulSpeed[CD_Looper1])
                    • Else - Actions
                      • Set CD_SoulIn[CD_Looper1] = False
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • CD_SoulDistance[CD_Looper1] Less than CD_AoE[CD_Level[CD_Looper1]]
                    • Then - Actions
                      • Set CD_SoulDistance[CD_Looper1] = (CD_SoulDistance[CD_Looper1] + CD_SoulSpeed[CD_Looper1])
                    • Else - Actions
                      • Set CD_SoulIn[CD_Looper1] = True
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • CD_SoulUp[CD_Looper1] Equal to True
                • Then - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • CD_SoulCurrentHeight[CD_Looper1] Less than CD_SoulMaxHeight
                    • Then - Actions
                      • Set CD_SoulCurrentHeight[CD_Looper1] = (CD_SoulCurrentHeight[CD_Looper1] + CD_SoulHeightSpeed)
                    • Else - Actions
                      • Set CD_SoulUp[CD_Looper1] = False
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • CD_SoulCurrentHeight[CD_Looper1] Greater than 0.00
                    • Then - Actions
                      • Set CD_SoulCurrentHeight[CD_Looper1] = (CD_SoulCurrentHeight[CD_Looper1] - CD_SoulHeightSpeed)
                    • Else - Actions
                      • Set CD_SoulUp[CD_Looper1] = True
              • Set CD_Point1 = (Position of CD_Circle[CD_Looper1])
              • Set CD_Point2 = (CD_Point1 offset by CD_SoulDistance[CD_Looper1] towards CD_SoulAngle[CD_Looper1] degrees)
              • Animation - Change CD_Soul[CD_Looper1] flying height to CD_SoulCurrentHeight[CD_Looper1] at 0.00
              • Custom script: call SetUnitY(udg_CD_Soul[udg_CD_Looper1], GetLocationY(udg_CD_Point2))
              • Custom script: call SetUnitX(udg_CD_Soul[udg_CD_Looper1], GetLocationX(udg_CD_Point2))
              • Custom script: call RemoveLocation(udg_CD_Point1)
              • Custom script: call RemoveLocation(udg_CD_Point2)
              • -------- --------
              • -------- casting time --------
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • CD_TimeLeft[CD_Looper1] Greater than 0.00
                • Then - Actions
                  • Set CD_TimeLeft[CD_Looper1] = (CD_TimeLeft[CD_Looper1] - 0.03)
                • Else - Actions
                  • Set CD_Complete[CD_Looper1] = False
                  • Unit - Order CD_Caster[CD_Looper1] to Stop
              • -------- --------
              • -------- create the helper's spot --------
              • -------- action id = 0 --------
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • CD_ActionId[CD_Looper1] Equal to 0
                • Then - Actions
                  • Set CD_TempAngle = CD_Angle[CD_Looper1]
                  • For each (Integer CD_Looper2) from 1 to CD_Helpers[CD_Level[CD_Looper1]], do (Actions)
                    • Loop - Actions
                      • -------- --------
                      • Set CD_Index = (CD_Index + 1)
                      • -------- --------
                      • Set CD_Caster[CD_Index] = CD_Caster[CD_Looper1]
                      • Set CD_Player[CD_Index] = CD_Player[CD_Looper1]
                      • Set CD_Level[CD_Index] = CD_Level[CD_Looper1]
                      • -------- --------
                      • Set CD_TempAngle = (CD_TempAngle + CD_AngleAdjustment[CD_Looper1])
                      • Set CD_Angle[CD_Index] = CD_TempAngle
                      • -------- --------
                      • Set CD_Point1 = (Position of CD_Circle[CD_Looper1])
                      • Set CD_Point2 = (CD_Point1 offset by CD_AoE[CD_Level[CD_Index]] towards CD_Angle[CD_Index] degrees)
                      • -------- --------
                      • -------- circle --------
                      • -------- --------
                      • Unit - Create 1 CD_CircleDummyType for CD_Player[CD_Index] at CD_Point2 facing CD_Angle[CD_Index] degrees
                      • Set CD_HelpAreaDummy[CD_Index] = (Last created unit)
                      • Special Effect - Create a special effect at CD_Point2 using CD_HelpSfx
                      • Set CD_HelpEffect[CD_Index] = (Last created special effect)
                      • -------- --------
                      • -------- clean up --------
                      • -------- --------
                      • Custom script: call RemoveLocation(udg_CD_Point1)
                      • Custom script: call RemoveLocation(udg_CD_Point2)
                      • -------- --------
                      • -------- logic --------
                      • -------- --------
                      • Set CD_ActionId[CD_Index] = 2
                      • Set CD_TimeLeft[CD_Index] = (CD_TimeLeft[CD_Index] + CD_CastingTime[CD_Level[CD_Index]])
                      • Set CD_Occupied[CD_Index] = False
                  • Set CD_ActionId[CD_Looper1] = 1
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • CD_ActionId[CD_Looper1] Equal to 2
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • CD_Occupied[CD_Looper1] Equal to False
                        • Then - Actions
                          • Set CD_Point1 = (Position of CD_HelpAreaDummy[CD_Looper1])
                          • Set CD_HelperGroup = (Units within CD_HelpAoE[CD_Level[CD_Looper1]] of CD_Point1)
                          • Unit Group - Pick every unit in CD_HelperGroup and do (Actions)
                            • Loop - Actions
                              • Set CD_Picked = (Picked unit)
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • (Unit-type of CD_Picked) Equal to CD_HelperType
                                  • (CD_Picked is alive) Equal to True
                                  • (Owner of CD_Picked) Equal to CD_Player[CD_Looper1]
                                • Then - Actions
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • CD_Occupied[CD_Looper1] Equal to False
                                    • Then - Actions
                                      • Set CD_Occupied[CD_Looper1] = True
                                      • Set CD_HiddenHelper[CD_Looper1] = CD_Picked
                                      • Unit - Hide CD_HiddenHelper[CD_Looper1]
                                      • Unit - Create 1 CD_HelperGhost for CD_Player[CD_Looper1] at CD_Point1 facing (CD_Angle[CD_Looper1] + 180.00) degrees
                                      • Set CD_Ghost[CD_Looper1] = (Last created unit)
                                      • Unit - Create 1 CD_CircleDummyType for CD_Player[CD_Looper1] at CD_Point1 facing CD_Angle[CD_Looper1] degrees
                                      • Set CD_Soul[CD_Looper1] = (Last created unit)
                                      • Unit - Add Storm Crow Form to CD_Soul[CD_Looper1]
                                      • Unit - Remove Storm Crow Form from CD_Soul[CD_Looper1]
                                      • Special Effect - Create a special effect attached to the origin of CD_Soul[CD_Looper1] using CD_SoulSfx
                                      • Set CD_SoulEffect[CD_Looper1] = (Last created special effect)
                                      • Set CD_SoulAngle[CD_Looper1] = CD_Angle[CD_Looper1]
                                      • Set CD_SoulDistance[CD_Looper1] = CD_AoE[CD_Level[CD_Looper1]]
                                      • Set CD_SoulSpeed[CD_Looper1] = (CD_AoE[CD_Level[CD_Looper1]] / (CD_SoulDelay / 0.03))
                                      • Set CD_SoulCurrentHeight[CD_Looper1] = 0.00
                                      • Set CD_SoulIn[CD_Looper1] = True
                                      • Set CD_SoulUp[CD_Looper1] = True
                                    • Else - Actions
                                      • -------- i only need one and i do not know how to exit loop --------
                                • Else - Actions
                                  • Set CD_Picked = No unit
                          • Custom script: call RemoveLocation(udg_CD_Point1)
                        • Else - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • CD_GhostLeft[CD_Looper1] Greater than 0.00
                            • Then - Actions
                              • Set CD_GhostLeft[CD_Looper1] = (CD_GhostLeft[CD_Looper1] - 0.03)
                            • Else - Actions
                              • Set CD_Point1 = (Position of CD_HelpAreaDummy[CD_Looper1])
                              • Set CD_GhostLeft[CD_Looper1] = CD_GhostInterval
                              • Animation - Play CD_Ghost[CD_Looper1]'s CD_GhostAnim animation
                              • Special Effect - Create a special effect at CD_Point1 using CD_HelpedSfx
                              • Special Effect - Destroy (Last created special effect)
                              • Custom script: call RemoveLocation(udg_CD_Point1)
                    • Else - Actions
            • Else - Actions


i have also attached the map
 

Attachments

  • Call of the Damned.w3x
    35 KB · Views: 65
Level 4
Joined
Apr 28, 2016
Messages
33
Hi guys, i'm new here...
this is my first time to entry a contest, i have a few question about the contest:
- I have the war3 world editor ver 1.2.2, is that different from the vanilla world editor?
- If it's different, can my map from lower version loaded to upper version?
- Can i only use Trigger Command for the spell?
Thanks before, pleased to meet you all =)

This is my summoning spell, hope it will worthy and works!
154384d1462182418-zephyr-contest-14-unique-summoning-crabas_sakti-summon-ectoplasm-summoning-unit.jpg
 

Attachments

  • crabas_sakti+summon ectoplasm+summoning unit.jpg
    crabas_sakti+summon ectoplasm+summoning unit.jpg
    167.5 KB · Views: 210
Level 19
Joined
Jul 2, 2011
Messages
2,162
Hi guys, i'm new here...
this is my first time to entry a contest, i have a few question about the contest:
- I have the war3 world editor ver 1.2.2, is that different from the vanilla world editor?
- If it's different, can my map from lower version loaded to upper version?
- Can i only use Trigger Command for the spell?
Thanks before, pleased to meet you all =)

This is my summoning spell, hope it will worthy and works!
154384d1462182418-zephyr-contest-14-unique-summoning-crabas_sakti-summon-ectoplasm-summoning-unit.jpg

that's a pretty cool ability. did you use the mirror image spell to replicate that ability and the enemy?

or did you uses just the standard create unit of type?
 
If you patch warcraft III to the latest version you should have World Editor V1.27 (6059) you can check this in the editor via help -> about warcraft III world editor

- The vanilla editor is the one that comes with warcraft (the one automatically installed alongside Warcraft III)
- You can use object editor components however the entry shall be judged on Code primarily and thus necessarily uses the Trigger Editor
 

Deleted member 238226

D

Deleted member 238226

i heard vitamin can increase the production of dopamine which can decrease the intention of procrastination. just saying ^^
 
Level 4
Joined
Apr 28, 2016
Messages
33
@TheLordOfChaos201: Maybe I will use destroy and create unit for SU variable = summoning/casts spell unit. It will be hard for me since i'm still newbie > <

@Tank-Commander: Where can i get the world editor patch for v.1.27? How to convert trigger into code?
 
Well, time to strike then.

  • DA Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Dark Altar ability --------
      • Set DA_Ability = Acid Bomb
      • -------- Altar unit --------
      • Set DA_Altar[1] = No unit-type
      • -------- Summoned unit --------
      • Set DA_Summon[1] = Ghoul
      • Set DA_Summon[2] = Abomination
      • -------- Altar damage per gap --------
      • Set DA_Damage[1] = 15.00
      • Set DA_Damage[2] = 20.00
      • -------- Altar energy gain --------
      • Set DA_EnergyGain[1] = 10.00
      • Set DA_EnergyGain[2] = 15.00
      • -------- Altar damage time gap --------
      • Set DA_Gap[1] = 2.00
      • Set DA_Gap[2] = 1.75
      • -------- Altar energy spent per summon --------
      • Set DA_EnergyReq[1] = 150.00
      • Set DA_EnergyReq[2] = 400.00
      • -------- Altar absorbing SFX --------
      • Set DA_SFXAbsorb = <Empty String>
      • -------- Unit being absorbed SFX --------
      • Set DA_SFXTarget = <Empty String>
      • -------- Altar summoning SFX --------
      • Set DA_SFXSummon = <Empty String>
      • -------- Altar lightning SFX --------
      • Set DA_AbsorbLightning = Drain Life
That's just some configuration to get a start of my ability. I'm planning more as I progress.
Core code not yet.
Is Unit Indexer allowed for this event? Same goes for Damage Detection Systems.
 
Level 17
Joined
Sep 8, 2007
Messages
994
I still don't know a name for my spell and I'm not 100% sure with the concept yet.
However, I'm working on components which I'm definitely going to use!
I can post a first WIP already, here ya go:

JASS:
scope ZephyrChallange
    globals
    // ========= SOUL LIFETIME ========= 
        
        //Default: 'n001'
        private constant integer    SOUL_ID         = 'n001' //ID of the dummy unit representing the soul
        //Default: 6
        private constant integer    SOUL_AMT        = 6 //amount of souls 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     = 75. //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     = 75. //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
    endglobals
    
    private struct SoulLifetime
    
        unit u  //affected unit
        unit array souls[SOUL_AMT]
        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
        integer remTick //remaining ticks
        timer t
        static real radDistance //radian-distance between each soul
        static real cosS//cos-value for the slope plane
        static real sinS//sin ---""---
        
        public method onTimeout takes nothing returns nothing
            //- MORE TO COME HERE -
            call this.destroy()
        endmethod
        
        public static method onTick takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            local integer i = 0
            
            // Positioning the souls behind the unit
            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,0)
                set i = i+1
            endloop
            
            //Shrinking souls and checking timeout-conditions
            if data.remTick < 0 then
                call UnitApplyTimedLife(data.souls[data.current], 'BTLF', 0.5)
                set data.current = data.current - 1
                if data.current < 0 then
                    call data.onTimeout()
                endif
                set data.soulSize = SOUL_MAX_SCALE
                set data.remTick = data.tickAmt
            else
                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.cosS = Cos(slope)
            set data.sinS = Sin(slope)
            set data.u = target
            set data.t = NewTimer()
            set data.current = (SOUL_AMT-1)
            set data.tickAmt = R2I((time/TICK)/SOUL_AMT)
            set data.remTick = data.tickAmt
            set data.shrinkStep = (SOUL_MAX_SCALE - SOUL_MIN_SCALE) / data.tickAmt
            set data.soulSize = SOUL_MAX_SCALE
            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 SetUnitFlyHeight(data.souls[i], SOUL_HEIGHT + z, 0.)
                set i = i+1
            endloop
            
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, TICK, true, function SoulLifetime.onTick)
            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            local integer i = 0
            call ReleaseTimer(this.t)
            set this.u = null
            loop
                exitwhen i >= SOUL_AMT
                set this.souls[i] = null
                set i = i+1
            endloop
        endmethod
        
        private static method onInit takes nothing returns nothing
            local real slope = SOUL_SLOPE * bj_PI/180.
            set cosS = Cos(slope)
            set sinS = Sin(slope)
            set radDistance = bj_PI/(SOUL_AMT-1)
        endmethod
    endstruct
endscope
 
Level 17
Joined
Sep 8, 2007
Messages
994
I'm in the flow right now. Even though this is a smaller piece, I might as well use it as another WIP.

JASS:
scope ZephyrChallange
    globals

        //======== 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: 50.
        private constant real       VOIDORB_HEIGHT  = 50. //orb's height
        //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 based speed every TICK
        //Default: 2.
        private constant real       VOIDORB_DELAY   = 2. // delay until the orb starts moving towards the caster
        //Default 60.
        private constant real       VOIDORB_COL     = 60. //collision range between caster and orb
    endglobals
    
    private function A2PXY takes real x,real y,real xt,real yt returns real
        return ModuloReal(bj_RADTODEG*Atan2(yt-y,xt-x),360.)
    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 struct VoidOrb
        unit caster 
        unit orb
        real x
        real y
        real size
        real speed
        effect spx
        timer t
        
        private method onCollision takes nothing returns nothing
            call this.destroy()
        endmethod
        
        private static method onTick takes nothing returns nothing
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            local real angle = A2PXY(GetUnitX(data.orb), GetUnitY(data.orb), GetUnitX(data.caster), GetUnitY(data.caster))
            local real distance = D2PXY(GetUnitX(data.orb), GetUnitY(data.orb), GetUnitX(data.caster), GetUnitY(data.caster))
            local real x
            local real y
            
            if (distance > VOIDORB_COL) then
                set data.speed = data.speed * VOIDORB_ACC
                set x = GetUnitX(data.orb) + data.speed * Cos(angle * bj_DEGTORAD)
                set y = GetUnitY(data.orb) + data.speed * Sin(angle * bj_DEGTORAD)
                call SetUnitX(data.orb, x)
                call SetUnitY(data.orb, y) 
                
                // - MORE TO COME HERE -
            else
                call data.onCollision()
            endif
        endmethod
        
        public static method onStart takes nothing returns nothing 
            local thistype data = thistype(GetTimerData(GetExpiredTimer()))
            call TimerStart(data.t, TICK, true, function thistype.onTick)
        endmethod
        
        public static method create takes unit caster, real x, real y returns thistype
            local thistype data = thistype.allocate()
            
            set data.caster = caster
            set data.orb = CreateUnit(GetOwningPlayer(caster), DUMMYID, x, y, 0.)
            set data.x = x
            set data.y = y
            set data.speed = VOIDORB_SPEED
            set data.spx = AddSpecialEffectTarget(VOIDORB_MODEL, data.orb, "origin")
            call SetUnitScale(data.orb, VOIDORB_SIZE,VOIDORB_SIZE,VOIDORB_SIZE)
            call SetUnitFlyHeight(data.orb, VOIDORB_HEIGHT, 0.)
            
            set data.t = NewTimer()
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, VOIDORB_DELAY, false, function thistype.onStart)
            
            return data
        endmethod
        
        private method onDestroy takes nothing returns nothing
            call UnitApplyTimedLife(this.orb, 'BTLK', 0.5)
            call DestroyEffect(this.spx)
            set this.spx = null
            set this.orb = null
            set this.caster = null
            call ReleaseTimer(this.t)
        endmethod
    endstruct
endscope

Btw - sry for double post, but this fits besser as a separate post for another WIP, IMO.
 
Level 13
Joined
Mar 29, 2012
Messages
530
I'm in.
A week late, but I have my own concept:

Spell Name: Ancient of Power
Target Type: Point target (Channeling)
Description:
Hero sends mana (in a form of orb) to targeted location. The mana causes a circle of nature where all enemy units will be hold inside while their life points being absorbed. In the mean time, there will be an ancient being summoned at the center of the circle. If the absorbed life points reaches certain threshold, an ancient tree will appear along with a big explosion of life points which will heal nearby friendly units and damage undead units.
The ancient might have special powers.​

Update:

Description:
The Hero turns his mana into an orb and throws it to targeted location. While the orb traveling in a line, it drains mana of nearby enemy units. Upon reaching the target, it'll begin summoning an ancient by creating a circle of nature that also hold all enemy ground units inside the circle. The summoning requires certain amount of power which is gathered by absorbing nearby enemy units' life and mana. When the amount of power has reached, an explosion will occur that causes damage to nearby enemy units and heal friendly units. Overloaded powers can also turn into a Treant or reanimate dead Treants. 20% of the absorbed power is returned to the caster, thus increasing caster's life and mana.​
 
Last edited:
Level 17
Joined
Sep 8, 2007
Messages
994
I'm in.
A week late, but I have my own concept:

Spell Name: Ancient of Power
Target Type: Point target (Channeling)
Description:
Hero sends mana (in a form of orb) to targeted location. The mana causes a circle of nature where all enemy units will be hold inside while their life points being absorbed. In the mean time, there will be an ancient being summoned at the center of the circle. If the absorbed life points reaches certain threshold, an ancient tree will appear along with a big explosion of life points which will heal nearby friendly units and damage undead units.
The ancient might have special powers.​

Gonna work on entry soon.

My concept is similar. I think it's the most common idea people come up with when it comes to "unique" summoning...
 
Status
Not open for further replies.
Top