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

Nano Plague V1.00

  • Like
Reactions: JesusHipster
[TD]Zephyr Contest #14 Winning entry - 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
    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 ilevel = GetUnitAbilityLevel(u2, spellID)
            set rlevel = ilevel

            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][/tr]
Contents

Nano Plague V1.00 (Map)

Top