Chaosy
Tutorial Reviewer
- Joined
- Jun 9, 2011
- Messages
- 13,239
Pretty sure that's required.
////////////////////// Demonic Ritual v1.00 by Meatmuffin ///////////////////////
// Conjures a flame spirit that circles the caster. Any contact with an enemy //
// unit will damage 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. //
/////////////////////////////////////////////////////////////////////////////////
//
///////////////////////////// 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!
/////////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////// 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.31250000
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_AbiliyID 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
//
////////////////////////////// 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 200.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 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
//
//////////////////////////// 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 4.00
endfunction
//
// The per level duration of the same as above.
constant function DR_MinionDurationPerLevel takes nothing returns real
return 4.00
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
//
// 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 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 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 size of the missile.
constant function DR_MissileSize takes nothing returns real
return 0.40
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
//
//////////////////////////////// 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) == true) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE) == true) and IsUnitEnemy(u, pl) == true
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
return GetLocationZ(udg_DR_Location)
endfunction
// Function used to create a new instance in the linked list.
function DR_CreateNode takes nothing returns integer
local integer Node
return Node
endfunction
// Function used to recycle a no longer needed instance in the linked list.
function DR_Recycle takes integer Node returns nothing
endfunction
// Main function of the spell, each time it fires, a small part of the spell takes
// place, resulting in a continuous loop of functions.
function DR_Loop takes nothing returns nothing
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
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)
set udg_DR_Location = Location(0.00, 0.00)
endfunction
You need a WIP before the final submission, keep that in mind.
@Tank-Commander: For vanilla JASS, I would recommend DoomLord's system:
http://www.hiveworkshop.com/forums/spells-569/jass-custom-stat-system-css-v1-5g-229885/
Otherwise, BonusMod.
Is this a general permission to use these two systems ?
I am going to use one of these.
It can't possibly be more work than it was making it from scratch, and like you said:I shouldn't have made it in my map. I had no idea how much work it would be to transfer the ability over to a blank Map
You still have a whole month. I'm sure you can find the time to move the spell to a new map.it was less than an hour to make ^^
As long as it is approved, yes.
Meanwhile, BonusMod is approved, tho graveyarded from some reason
this let's see
it's 21 spells to transfer and a few trigs
that will take a whole to transfer but your right not very long
but na I'm happy with just having a cool spell. no need to enter it into a contest
library ContractOfSuffering/*
*************************************************************************************
Contract of Suffering
Signs a contract to call on your embodiment of suffering that escorts you in
battle.
Signs a contract. If the caster have received a certain amount of damage
in a given duration, it summons the embodiment. The Embodiment requires 20/25/30
attacks to be killed, and has a timed life duration of 40/50/60 seconds.
and has 3 abilities that it gains per level:
Level 1 - Shadow Assault
The Embodiment strikes a nearby random unit within 800 range, dealing 50/100/150
damage to it's target and 15/30/45 damage to the units the embodiment passed
through.
Attack cooldown : 2.5/2/1.5
Level 2 - Veil of Ordeal
*************************************************************************************
*/ requires /*
*/ MissileRecycler /*
*/ SpellEffectEvent /*
*/ GetUnitCollision /*
*/ MapBounds /*
*/ TimerUtils /*
*/ StunSystem /*
*/ RealEvent /*
*/ Table /*
*/ optional OrderEvent /*
*/ optional ZLibrary/*
*************************************************************************************/
globals
/*
* Timeout of the all timers in this spell
*/
private constant real TIMEOUT = 0.03125
/*
* Ability id of the Contract of Suffering
*/
private constant integer ABIL_ID = 0
/*
* All summoned units with respect to their levels
*/
private constant integer EMB_LEVEL_1_UNIT = 'cos1'
private constant integer EMB_LEVEL_2_UNIT = 'cos2'
private constant integer EMB_LEVEL_3_UNIT = 'cos3'
/*
* Damage required to summon the embodiment
* Note: The require amount is not necessarily
* the exact amount, instead it is the
* the total amount of damage received
* on the spell's duration
*/
private constant real REQUIRED_DAMAGE = 600
private constant real REQUIRED_DAMAGE_PER_LVL = -100
/*
* Spell duration
*/
private constant real SPELL_DURATION = 10
private constant real SPELL_DURATION_PER_LVL = 0
/**************************************
*
* Embodiment section
*
**************************************/
/*
* Movespeed of the embodiment
*/
private constant real MOVESPEED = 600
private constant real MOVESPEED_PER_LVL = 0
/*
* Timer period whether the embodiment is idle
*/
private constant real EMBODIMENT_IDLE_PERIOD = 1.0
/*
* Max range for the embodiment from it's owner
*/
private constant real ESCORT_RANGE = 800
/*
* Period to order the embodiment to wander around it's owner
*/
private constant real ESCORT_WANDER_PERIOD = 0.25
/*
* Does the embodiment return to it's owner if it is
* outside the wander range?
* Note: Returns after the order has been fulfilled OR
* if it is cancelled
*/
private constant boolean RETURN_IF_OUTRANGE = false
/*
* Embodiment Timed Life
*/
private constant real TIMED_LIFE = 30
private constant real TIMED_LIFE_PER_LVL = 10
/**************************************
*
* Level 1 : Shadow Assault
*
**************************************/
/*
* Level requirement
*/
private constant integer ATTACK_LEVEL_REQ = 1
/*
* Order id of Shadow Assault
* Note: the player can also cast this
* using this order
*/
private constant integer ATTACK_ORDER_ID = 851983 // "attack"
/*
* Range to acquire targets
*/
private constant real ACQUISITION_RANGE = 800
/*
* If true, it uses the owner's position to search targets
* instead of using the embodiment's position
*/
private constant boolean USE_OWNER_POSITION = false
/*
* Attack cooldown of the embodiment
*/
private constant real ATTACK_COOLDOWN = 2
private constant real ATTACK_COOLDOWN_PER_LVL = -0.25
/*
* Range of the attack
* Note: the actual range is :
* ATTACK_RANGE + target collision size + embodiment collision size
*/
private constant real ATTACK_RANGE = 0
/*
* Attack damage of the embodiment
*/
private constant real ATTACK_DAMAGE = 25
private constant real ATTACK_DAMAGE_PER_LVL = 25
private constant attacktype ATK_DMG_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype ATK_DMG_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Does the attack cooldown start if the embodiment has
* returned to it's owner?
* Note: if false, the attack cooldown starts after
* the attack has landed
*/
private constant boolean START_ATK_COOLDOWN_IF_RETURNED = true
/*
* Does the embodiment cancel it's attack if it is
* outside the escort range?
* Note: This is useless if CANCEL_ORDERS_IF_OUTRANGE
* is false.
*/
private constant boolean CANCEL_ATK_IF_OUTRANGE = false
/*
* Distance check of the difference of it's target's previous
* position to it's current position.
* If the difference is larger than ENEMY_ESCAPE_DISTANCE,
* the embodiment cancels it's attack
*/
private constant real ENEMY_ESCAPE_DISTANCE = 1250
/*
* Does the attack cooldown start if it escapes?
*/
private constant boolean START_ATK_COOLDOWN_IF_ESCAPED = false
/*
* Pass-through damage
*/
private constant real PASS_DAMAGE = 10
private constant real PASS_DAMAGE_PER_LVL = 10
private constant real PASS_DAMAGE_RANGE = 64
/**************************************
*
* Level 2 : Veil of Ordeal
*
**************************************/
/*
* Veil of Ordeal Level Requirement
*/
private constant integer VEIL_LEVEL_REQ = 2
/*
* Required receive damage to feed the embodiment
*/
private constant real REQ_DAMAGE = 100
private constant real REQ_DAMAGE_PER_LVL = 0
/*
* Time to wait for the total damage
*/
private constant real REQ_DMG_WAIT_TIME = 2
private constant real REQ_DMG_WAIT_TIME_PER_LVL = 0
/*
* Amount of timed life increased to the embodiment
* when fed.
*/
private constant real TIMED_LIFE_INC = 0
private constant real TIMED_LIFE_INC_PER_LVL = 1
/**************************************
*
* Level 3 : Roar of Pain
*
**************************************/
private constant string EMB_MODEL = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl"
private constant real EMB_SCALE = 1.5
private constant integer EMB_RED = 255
private constant integer EMB_GREEN = 255
private constant integer EMB_BLUE = 255
private constant integer EMB_ALPHA = 240
private constant integer EMB_SEGMENT_LENGTH = 8
private constant string EMB_SEGMENT_MODEL = EMB_MODEL
private constant real MIN_SCALE = 0.25
private constant integer EMB_SEGMENT_RED = 255
private constant integer EMB_SEGMENT_GREEN = 255
private constant integer EMB_SEGMENT_BLUE = 255
private constant integer EMB_SEGMENT_ALPHA = 240
private constant real EMB_SEGMENT_SEPERATION_DIST = 16
endglobals
private function ShadowAssaultValidTarget takes unit saTarget, player embOwner returns boolean
/*
* Just put "//" before each statement to cancel those conditions
*/
return UnitAlive (saTarget) and /*
*/ IsUnitEnemy (saTarget, embOwner) and /*
*/ IsUnitVisible(saTarget, embOwner) and /*
*/ not IsUnitType (saTarget, UNIT_TYPE_SLEEPING) and /*
*/ not IsUnitType (saTarget, UNIT_TYPE_ETHEREAL) and /*
*/ not IsUnitType (saTarget, UNIT_TYPE_STRUCTURE)
endfunction
private function RoarOfPainValidTarget takes unit ropTarget, player embOwner returns boolean
return UnitAlive (ropTarget) and /*
*/ IsUnitEnemy (ropTarget, embOwner) and /*
*/ IsUnitVisible(ropTarget, embOwner) and /*
*/ not IsUnitType (ropTarget, UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ not IsUnitType (ropTarget, UNIT_TYPE_STRUCTURE)
endfunction
/**************************************
*
* For getting the location surface z
*
**************************************/
static if (not LIBRARY_ZLibrary) then
globals
private constant location p = Location(0.0, 0.0)
endglobals
function GetSurfaceZ takes real x, real y returns real
call MoveLocation(p, x, y)
return GetLocationZ(p)
endfunction
endif
/**************************************
*
* List methods (Modules don't work
* as intended)
*
**************************************/
//! textmacro COS_LIST
private static thistype array prev
private static thistype array next
/*
* Insert node method
*/
private method insert takes nothing returns nothing
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
endmethod
/*
* Remove node method
*/
private method remove takes nothing returns nothing
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
endmethod
/*
* Check if list is empty
*/
static method operator empty takes nothing returns boolean
return next[0] == 0
endmethod
/*
* Check if list has only a single instance
*/
method operator single takes nothing returns boolean
return prev[this] == this
endmethod
//! endtextmacro
/**************************************
*
* Struct for segments to automatically
* adjust their facing.
*
**************************************/
private struct SegmentFace
/*
* u = the main unit
* nextU = the unit it's facing angle is based
* prevU = for destroying purposes
*/
readonly unit u
readonly unit nextU
readonly unit prevU
/*
* holds the object instance of the units
*/
private static Table segList
/*
* implement the list
*/
//! runtextmacro COS_LIST()
private static constant timer t = CreateTimer()
method destroy takes nothing returns nothing
/*
* remove node
*/
call remove()
/*
* if list is empty, pause timer
*/
if empty then
call PauseTimer(t)
endif
/*
* remove all data
*/
set segList[GetHandleId(u)] = 0
set u = null
set nextU = null
set prevU = null
endmethod
static method destroySegment takes unit du returns nothing
/*
* this method is used for chain destruction
* e.g. unit A is destroyed, destroy prev unit B
*/
call thistype(segList[GetHandleId(du)]).destroy()
endmethod
private static method period takes nothing returns nothing
local thistype this = next[0]
local real ang = 0
/*
* Iterate over instances
*/
loop
exitwhen 0 == this
/*
* get the next unit's facing
*/
set ang = GetUnitFacing(nextU)
/*
* Set the unit's facing towards the facing
* of the next unit.
*/
call SetUnitFacing(u, ang)
/*
* Negate the given angle by rotating it
* 180 degrees
*/
set ang = ang*bj_DEGTORAD + bj_PI
/*
* Move the unit to it's seperation distance
*/
call SetUnitX(u, GetUnitX(nextU) + EMB_SEGMENT_SEPERATION_DIST*Cos(ang))
call SetUnitY(u, GetUnitY(nextU) + EMB_SEGMENT_SEPERATION_DIST*Sin(ang))
set this = next[this]
endloop
endmethod
static method create takes unit tempU, unit prevLink, unit nextLink returns thistype
local thistype this
/*
* If the next unit is null, don't create an instance.
*/
if nextLink == null then
return thistype(0)
endif
/*
* allocate an isntance
*/
set this = allocate()
/*
* insert instance to the list
*/
call insert()
/*
* set the fields to the respective data
*/
set u = tempU
set nextU = nextLink
set prevU = prevLink
/*
* save the instace to the table
*/
set segList[GetHandleId(u)] = this
/*
* if the list has only one instance, start the timer.
*/
if single then
call TimerStart(t, TIMEOUT, true, function thistype.period)
endif
return this
endmethod
private static method onInit takes nothing returns nothing
set segList = Table.create()
endmethod
endstruct
private function LinearR takes real a, real b, real t returns real
return a + (b - a)*t
endfunction
private function LinearInt takes integer a, integer b, real t returns integer
return R2I(LinearR(I2R(a), I2R(b), t))
endfunction
private struct Embodiment
private unit u
private unit owner
private effect mdl
private boolean hasShadowAssault
private boolean hasVeilOfOrdeal
private boolean hasRoarOfTime
private boolean isIdle
private boolean returning
private boolean attacking
private unit target
private timer attackTimer
private timer castTimer
endstruct
endlibrary
If you've coded those 21 traps well it's still just two-three triggers.
Sounds rather simple, unless the summoned ones are special in some way.yep that's the plan ^^
////////////////////////////////////////////////////////////////////
// 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_SummonedUnitID takes nothing returns integer
return 'u002'
endfunction
constant function NP_BuffModel takes nothing returns string
return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactory.mdl"
endfunction
constant function NP_ProjectileModel takes nothing returns string
return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactoryMissle.mdl"
endfunction
constant function NP_SpawnBuff 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_EndOnNoProduction takes nothing returns boolean
return false
endfunction
constant function NP_BuffHeightOffset takes nothing returns real
return 150.
endfunction
constant function NP_BuffOffset takes nothing returns real
return 100.
endfunction
constant function NP_Gravity takes nothing returns real
return 4.
endfunction
function NP_AOE takes real level returns real
return 250 + (50 * level)
endfunction
function NP_BuffSize takes real level returns real
return 0.3 + (0.05 * level)
endfunction
function NP_ProjectileSize takes real level returns real
return 0.8 + (0.1 * level)
endfunction
function NP_InfectDuration takes real level returns real
return 4 + (1 * level)
endfunction
function NP_InfectDPS takes real level returns real
return 25 + (25 * level)
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 3 + (1 * level)
endfunction
function NP_FlightTime takes real level returns real
return 2 + (-0.2 * 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.4 + (0.1 * level)
endfunction
function NP_SummonHealthBonus takes real level returns integer
return R2I(125 * level)
endfunction
function NP_SummonHealthRegenBonus takes real level returns integer
return R2I(0.1 * level)
endfunction
function NP_SummonArmourBonus takes real level returns integer
return R2I(-1 + (1 * level))
endfunction
function NP_SummonAttackDamageBonus takes real level returns integer
return R2I(10 * level)
endfunction
function NP_summonAttackSpeedBonus takes real level returns integer
return R2I(0)
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
//----------------------------------------------------------------//
// END OF CONFIGURATION //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function used to get the Z location height at a given point //
////////////////////////////////////////////////////////////////////
function NP_GetLocationZ takes real x, real y returns real
call MoveLocation(udg_NP_ZLoc, x, y)
return GetLocationZ(udg_NP_ZLoc)
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]
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 run the main engine of the spell, handles //
// creation of new units, buff control and projectile control //
////////////////////////////////////////////////////////////////////
function NP_Loop takes nothing returns nothing
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
endloop
return udg_NP_Instance[Node] == instance
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 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 unit u
local unit u2
local boolean bNew = false
//Check if something was infected
if SpellID == NP_AbilityID() or SpellID == NP_InfectAbilityID() then
set u = GetSpellTargetUnit()
set udg_NP_Unit[Node] = GetSpellTargetUnit()
//Check if a the unit was infected by a summoned unit or by itself
if SpellID == NP_InfectAbilityID() then
set u2 = GetSpellAbilityUnit()
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 (NP_GetInfections(u) >= 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])
else
//Remove the infecting unit
set udg_NP_Instance[Node] = udg_NP_Instance[instance]
call RemoveUnit(udg_NP_Unit[instance])
call NP_RecycleNode(instance)
set bNew = true
endif
set u2 = null
elseif NP_GetInfections(u) < NP_InfectionLimit() then
set bNew = true
endif
//Check if a new instance needs to be created
if (bNew) then
set Node = NP_CreateNode()
if udg_NP_PrevNode[Node] == 0 then
call TimerStart(udg_NP_Timer, NP_TimerSpeed(), true, function NP_Loop)
endif
set udg_NP_Unit[Node] = u
if SpellID == NP_InfectAbilityID() then
set udg_NP_Instance[Node] = udg_NP_Instance[instance]
else
set udg_NP_Instance[Node] = Node
endif
endif
set u = null
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.)
set t = null
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL //
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// 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_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_EndOnNoProduction takes nothing returns boolean
return false
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 4.
endfunction
function NP_AOE takes real level returns real
return 250 + (50 * level)
endfunction
function NP_BuffSize takes real level returns real
return 0.2 + (0.05 * level)
endfunction
function NP_ProjectileSize takes real level returns real
return 0.8 + (0.1 * 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 3 + (1 * 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.4 + (0.1 * level)
endfunction
function NP_SummonHealthBonus takes integer level returns integer
return 125 * 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 10 * level
endfunction
function NP_SummonAttackSpeedBonus takes integer level returns integer
return 0
endfunction
function NP_SwarmStrengthAttackBonus takes integer SwarmStrength returns integer
return R2I(1.5 * 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
//----------------------------------------------------------------//
// 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 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]
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
endloop
return udg_NP_Instance[Node] == instance
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 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(GetOwningPlayer(u)))
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
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)
//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[Node]) + GetRandomReal(0, udg_NP_AOE[Node])
if offset > udg_NP_AOE[Node] then
set offset = 2 * udg_NP_AOE[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)
call SetUnitScale(udg_NP_Unit[tempNode], udg_NP_ProjectileSize[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.)
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
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
call UnitDamageTarget(udg_NP_Caster[Node], udg_NP_Unit[Node], udg_NP_InfectionDamageHealth[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[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[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)
else
set udg_NP_CurrentProductionDelay[Node] = udg_NP_CurrentProductionDelay[Node] - NP_TimerSpeed()
endif
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
elseif udg_NP_StageID[Node] == NP_ProjectileStageID() then
//Move projectiles
elseif udg_NP_StageID[Node] == NP_NanobotStageID() then
//Calculate bonuses
else
//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.)
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 udg_NP_Instance[Node] = udg_NP_Instance[instance]
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
//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_Unit[instance]
else
set udg_NP_Instance[Node] = Node
set udg_NP_Caster[Node] = u
set udg_NP_SwarmStrength[Node] = 1
endif
//Add buff if this is the first infection
if infections == 0 then
call UnitAddAbility(udg_NP_Unit[Node], NP_BuffID())
endif
//Set up plague data
set level = GetUnitAbilityLevel(u2, SpellID)
set ilevel = R2I(level)
set udg_NP_Unit[Node] = u
set udg_NP_BuffNode[Node] = NP_CreateBuff(u, infections + 1, level)
set udg_NP_AOE[Node] = NP_AOE(level)
set udg_NP_TargetSelf[Node] = bTargetSelf
set udg_NP_TargetX[Node] = GetSpellTargetX()
set udg_NP_TargetY[Node] = GetSpellTargetY()
set udg_NP_ProjectileSize[Node] = NP_ProjectileSize(level)
set udg_NP_ProjectileFlightTime[Node] = NP_ProjectileFlightTime(level)
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_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_StageID[Node] = NP_InfectionStageID()
endif
set u2 = null
set u = null
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.)
set t = null
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL //
////////////////////////////////////////////////////////////////////
Not complete basic code to show, so here is my WIP tooltip:
View attachment 154281
To rephrase, hero channels, dummies pop up, player clicks dummies,
dependent on dummy type the hero gets additional stats, hero finishes channeling,
summons some units with percentage of additional hero stats.
Hehe, yes, it is going to be a long conf ^^Sounds epic, Ardenian. A lot of room for fancy effects
On a side note, I want to see some photos/GIFS of these spells. Screw these code WIPs, reading isn't fun!
Hm, I thought a lot of this point.I really like that.
Does that mean that if you click on all of them I will get a lot of bonuses, or 1 max?
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.
scope ElementalChaos initializer Init
//================================================================================
//============================Configurable Part===================================
//================================================================================
globals
//The spell id of the spell
private constant integer SPELL_ID = 'A000'
//Dummy unit id
private constant integer DUMMY_UNIT_ID = 'h000'
//Timer updating time constant
private constant real TIMER_TIMEOUT = 0.03125
//Sphere effect model path
private constant string SPHERE_EFFECT_PATH = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
//Sphere effect attachment point
private constant string SPHERE_EFFECT_ATTACHMENT_PATH = "origin"
//Color adjustment - red color component of sphere
private constant integer SPHERE_COLOR_RED = 70
//Color adjustment - green color component of sphere
private constant integer SPHERE_COLOR_GREEN = 200
//Color adjustment - blue color component of sphere
private constant integer SPHERE_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere
private constant integer SPHERE_TRANSPARENCY = 255
//Initial scale of the sphere before generating start
private constant real SPHERE_INITIAL_SCALE = 0.
//Sphere particle effect stop spawn timing (those little particles move toward the sphere)
private constant real SPHERE_PARTICLE_STOP_TIMING = 1.
//Sphere particle maximum scale
private constant real SPHERE_PARTICLE_MAX_SCALE = 2.5
//Sphere particle minimum scale
private constant real SPHERE_PARTICLE_MIN_SCALE = 1.0
//Sphere particle maximum distance from sphere
private constant real SPHERE_PARTICLE_MAX_DIST = 300.
//Sphere particle minimum distance from sphere
private constant real SPHERE_PARTICLE_MIN_DIST = 200.
//Sphere particle maximum height distance from shpere
private constant real SPHERE_PARTICLE_MAX_HEIGHT = 200.
//Sphere particle minimum height fistance from sphere
private constant real SPHERE_PARTICLE_MIN_HEIGHT = 100.
//Sphere particle maximum speed moving toward sphere (horizontal)
//(verticle) speed is dependent on the (horizontal) speed and height (distance) between particle and sphere
private constant real SPHERE_PARTICLE_MAX_SPEED = 32.
//Sphere particle minimum speed moving toward sphere (horizontal)
private constant real SPHERE_PARTICLE_MIN_SPEED = 16.
//Sphere particle dissapear (remove) distance between sphere and sphere particle
private constant real SPHERE_PARTICLE_END_DIST = 50.
//Sphere particle effect model path
private constant string SPHERE_PARTICLE_EFFECT_PATH = "war3mapImported\\Stars_lightning_.MDX"
//Sphere particle effect attachment point
private constant string SPHERE_PARTICLE_ATTACHMENT_PATH = "origin"
//Sphere height (Configurable at function Init)
private real array SPHERE_HEIGHT
//Sphere maximum scale (Configurable at function Init)
private real array SPHERE_MAX_SCALE
//Sphere generates time (Configurable at function Init)
private real array SPHERE_GENERATE_TIME
endglobals
private function Init takes nothing returns nothing
//[1] indicates Lv1, [2] indicates Lv2 and [3] indicates Lv3
//Arrays are used instead of globals for preventing overwhelming globals as well as unnessasary coding.
//Setting sphere height
set SPHERE_HEIGHT[1] = 500.
set SPHERE_HEIGHT[2] = 500.
set SPHERE_HEIGHT[3] = 500.
//Setting sphere maximum scale
set SPHERE_MAX_SCALE[1] = 2.5
set SPHERE_MAX_SCALE[2] = 3.0
set SPHERE_MAX_SCALE[3] = 3.0
//Setting sphere generates time
set SPHERE_GENERATE_TIME[1] = 3.0
set SPHERE_GENERATE_TIME[2] = 2.5
set SPHERE_GENERATE_TIME[3] = 2.0
endfunction
//================================================================================
//==========================End Configurable Part=================================
//================================================================================
//================================================================================
//===========================Non-Configurable Part================================
//================================================================================
globals
endglobals
private function UnitAddRemoveCrowForm takes unit u returns nothing
if UnitAddAbility(u, 'Amrf') then
call UnitRemoveAbility(u, 'Amrf')
endif
endfunction
//================================================================================
//=========================End Non-Configurable Part==============================
//================================================================================
//-----------------------------------------------------------------
//-----------------------------------------------------------------
//A linked list created by myself
private module LinkedList
static method create takes nothing returns thistype
local thistype this = instanceCount + 1
set instanceCount = this
if thistype(0).next == 0 then
set thistype(0).next = this
set lastNode = this
else
set lastNode.next = this
set this.prev = lastNode
set lastNode = this
endif
set this.next = 0
set thistype(0).prev = this
return this
endmethod
method destroy takes nothing returns nothing
set this.prev.next = this.next
set this.next.prev = this.prev
if this.next == 0 and this.prev == 0 then
set instanceCount = 0
//call instanceClear if exists
static if thistype.instanceClear.exists then
call thistype.instanceClear()
endif
elseif lastNode == this then
set lastNode = this.prev
endif
endmethod
endmodule
//-----------------------------------------------------------------
//-----------------------------------------------------------------
private struct ElementalSummon extends array
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
implement LinkedList
endstruct
private struct SphereParticleEffect extends array
//==========Sphere Side Effect Globals============
static timer timerSPE = CreateTimer()
unit dummy
effect e //effect attached to particle
real x //y coordinate of particle
real y //x coordinate of particle
real distance //distance between sphere and particle
real height //height between sphere and particle
real speedh //speed horizontal
real speedv //speed verticle
real destinationHeight //sphere height
real destinationX //sphere x coordinate
real destinationY //sphere y coordinate
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerSPE)
endmethod
implement LinkedList
static method onPeriodic takes nothing returns nothing
local thistype this = thistype(0).next
local real angle
local real x2
loop
exitwhen this == 0
if this.distance >= SPHERE_PARTICLE_END_DIST then
set this.distance = this.distance - this.speedh
set x2 = GetUnitX(this.dummy)-this.destinationX
set angle = Atan2(GetUnitY(this.dummy)-this.destinationY, x2)
//move the particle toward sphere (verticle)
set this.height = this.height - this.speedv
call SetUnitFlyHeight(this.dummy, this.destinationHeight + this.height * Cos(Atan2(this.destinationHeight-this.height,x2)), 0.)
//move the particle toward sphere (horizontal)
set this.x = this.x - this.speedh * Cos(angle)
set this.y = this.y - this.speedh * Sin(angle)
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
else
call DestroyEffect(this.e)
call RemoveUnit(this.dummy)
set this.e = null
set this.dummy = null
call this.destroy()
endif
set this = this.next
endloop
endmethod
endstruct
private struct ElementalChaos extends array
//============Elemental Chaos Globals=============
private static timer timerEC = CreateTimer() //Elemental Chaos Timer
private unit triggerUnit
private unit sphereDummy
private effect sphereEffect
private player owner
private integer spellLv
private integer phase //spell phase
private real targetX //spell target X
private real targetY //spell target Y
private real spellDuration
private real sphereScale
private real sphereScaleConst //sphere scale increasement constant every TIMER_TIMEOUT seconds
private real sphereGenerateTime
private real sphereHeight
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
//instanceClear will be called if no more instance is running
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerEC)
endmethod
implement LinkedList
private static method onPeriodic takes nothing returns nothing
local thistype this = thistype(0).next
local SphereParticleEffect particleEffect
local real rand
loop
exitwhen this == 0
set spellDuration = spellDuration + TIMER_TIMEOUT
//phase 0 indicates sphere generating phase
if this.phase == 0 then
//=================================================================================
//Sphere Generate Effect of the sphere (the effect shows when sphere is generating)
//=================================================================================
//if (sphere generate time - passed spell timing) >= SPHERE_PARTICLE_STOP_TIMING
if (this.sphereGenerateTime-this.spellDuration) >= SPHERE_PARTICLE_STOP_TIMING then
set particleEffect = SphereParticleEffect.create()
//Setting up particle data
set particleEffect.destinationHeight = this.sphereHeight
set particleEffect.destinationX = this.targetX
set particleEffect.destinationY = this.targetY
set particleEffect.distance = GetRandomReal(SPHERE_PARTICLE_MIN_DIST, SPHERE_PARTICLE_MAX_DIST)
set particleEffect.height = GetRandomReal(SPHERE_PARTICLE_MIN_HEIGHT, SPHERE_PARTICLE_MAX_HEIGHT)
set particleEffect.speedh = GetRandomReal(SPHERE_PARTICLE_MIN_SPEED, SPHERE_PARTICLE_MAX_SPEED)
//speedv = height / time
//time = ditance / speedh
set particleEffect.speedv = particleEffect.height/(particleEffect.distance/particleEffect.speedh)
set particleEffect.x = this.targetX + particleEffect.distance * Cos(GetRandomReal(0., 2*bj_PI))
set particleEffect.y = this.targetY + particleEffect.distance * Sin(GetRandomReal(0., 2*bj_PI))
set particleEffect.dummy = CreateUnit(this.owner, DUMMY_UNIT_ID, particleEffect.x, particleEffect.y, 0)
set particleEffect.e = AddSpecialEffectTarget(SPHERE_PARTICLE_EFFECT_PATH, particleEffect.dummy, SPHERE_PARTICLE_ATTACHMENT_PATH)
//Set unit scale randomly
set rand = GetRandomReal(SPHERE_PARTICLE_MIN_SCALE, SPHERE_PARTICLE_MAX_SCALE)
call SetUnitScale(particleEffect.dummy, rand, rand, rand)
//add and remove crow form
call UnitAddRemoveCrowForm(particleEffect.dummy)
//rendomly obtain a number so that height can be appear lower or higher thn the sphere
if GetRandomInt(0, 1) == 1 then
set particleEffect.height = particleEffect.height * -1
endif
//set unit height
call SetUnitFlyHeight(particleEffect.dummy, this.sphereHeight + particleEffect.height, 0)
if particleEffect == 1 then
call TimerStart(SphereParticleEffect.timerSPE, TIMER_TIMEOUT, true, function SphereParticleEffect.onPeriodic)
endif
//Setting sphere scale, reason it is inside the block is to prevent sphere increasing scale when no more particle effects
//are generated.
set this.sphereScale = this.sphereScale + this.sphereScaleConst
call SetUnitScale(this.sphereDummy, this.sphereScale, this.sphereScale, this.sphereScale)
endif
//=================================================================================
//===========================End Sphere Generate Effect============================
//=================================================================================
//Check if the spell duration >= sphere generate time
if this.spellDuration >= this.sphereGenerateTime then
//Sphere generate phase ends, switch spell phase to 1
set this.phase = 1
endif
elseif this.phase == 1 then
//--------------
endif
set this = this.next
endloop
endmethod
private static method onCast takes nothing returns boolean
local thistype this
if GetSpellAbilityId() == SPELL_ID then
set this = thistype.create() //create a new instance
//Storing data
set this.triggerUnit = GetTriggerUnit()
set this.owner = GetOwningPlayer(this.triggerUnit)
set this.targetX = GetSpellTargetX()
set this.targetY = GetSpellTargetY()
set this.spellLv = GetUnitAbilityLevel(this.triggerUnit, SPELL_ID)
set this.phase = 0
set this.sphereGenerateTime = SPHERE_GENERATE_TIME[this.spellLv]
set this.sphereHeight = SPHERE_HEIGHT[this.spellLv]
set this.sphereScale = SPHERE_INITIAL_SCALE
//sphereScaleConst is dependent on SPHERE_MAX_SCALE, SPHERE_GENERATE_TIME, TIMER_TIMEOUT and SHPERE_PARTICLE_STOP_TIMING
//(this.sphereGenerateTime-SPHERE_PARTICLE_STOP_TIMING) : as sphere stop increase scale if no more particle is generating
// divide by TIMER_TIMEOUT : to obtain the overall running times
// SPHERE_MAX_SCALE[this.spellLv] : obtain the maximum scale of the sphere
// maximum scale / running times : so that a constant is obtained for scale increasement every TIMER_TIMEOUT
set this.sphereScaleConst = SPHERE_MAX_SCALE[this.spellLv]/((this.sphereGenerateTime-SPHERE_PARTICLE_STOP_TIMING)/TIMER_TIMEOUT)
set this.spellDuration = 0.
set this.sphereDummy = CreateUnit(this.owner, DUMMY_UNIT_ID, this.targetX, this.targetY, 0.)
//Add and remove crow form
call UnitAddRemoveCrowForm(this.sphereDummy)
//Set the unit height
call SetUnitFlyHeight(this.sphereDummy, this.sphereHeight, 0.)
//Setting the sphere to initial scale value
call SetUnitScale(this.sphereDummy, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE)
//Setting the sphere color
call SetUnitVertexColor(this.sphereDummy, SPHERE_COLOR_RED, SPHERE_COLOR_GREEN, SPHERE_COLOR_BLUE, SPHERE_TRANSPARENCY)
//Add the sphere effect to the unit
set this.sphereEffect = AddSpecialEffectTarget(SPHERE_EFFECT_PATH, this.sphereDummy, SPHERE_EFFECT_ATTACHMENT_PATH)
if this == 1 then
call TimerStart(timerEC, TIMER_TIMEOUT, true, function thistype.onPeriodic)
endif
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerAddCondition(t, Condition(function thistype.onCast))
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
set t = null
endmethod
endstruct
endscope
Try using Mana Flare or Possession (Target) with 0,0,255 color
Thanks for your suggestionsAlmia said:Try using Mana Flare or Possession (Target) with 0,0,255 color
Possession is the best model for spheres and also easy to color with(due to it's white light)
Does it mean there will be only one review then?- Anitarf (code)
- Bribe (visuals)
- PurgeandFire (concept)
library ContractOfSuffering/*
*************************************************************************************
Contract of Suffering
Signs a contract to call on your embodiment of suffering that escorts you in
battle.
Signs a contract. If the caster have received a certain amount of damage
in a given duration, it summons the embodiment. The Embodiment requires 20/25/30
attacks to be killed, and has a timed life duration of 40/50/60 seconds.
and has 3 abilities that it gains per level:
Level 1 - Shadow Assault
The Embodiment strikes a nearby random unit within 800 range, dealing 50/100/150
damage to it's target and 15/30/45 damage to the units the embodiment passed
through.
Attack cooldown : 2.5/2/1.5
Level 2 - Veil of Ordeal
*************************************************************************************
*/ requires /*
*/ MissileRecycler /*
*/ SpellEffectEvent /*
*/ GetUnitCollision /*
*/ MapBounds /*
*/ TimerUtils /*
*/ StunSystem /*
*/ RealEvent /*
*/ Table /*
*/ optional OrderEvent /*
*/ optional ZLibrary/*
*************************************************************************************/
globals
/*
* Timeout of the all timers in this spell
*/
private constant real TIMEOUT = 0.03125
/*
* Ability id of the Contract of Suffering
*/
private constant integer ABIL_ID = 0
/*
* All summoned units with respect to their levels
*/
private constant integer EMB_LEVEL_1_UNIT = 'cos1'
private constant integer EMB_LEVEL_2_UNIT = 'cos2'
private constant integer EMB_LEVEL_3_UNIT = 'cos3'
/*
* Damage required to summon the embodiment
* Note: The require amount is not necessarily
* the exact amount, instead it is the
* the total amount of damage received
* on the spell's duration
*/
private constant real REQUIRED_DAMAGE = 600
private constant real REQUIRED_DAMAGE_PER_LVL = -100
/*
* Spell duration
*/
private constant real SPELL_DURATION = 10
private constant real SPELL_DURATION_PER_LVL = 0
/**************************************
*
* Embodiment section
*
**************************************/
/*
* Movespeed of the embodiment
*/
private constant real MOVESPEED = 600
private constant real MOVESPEED_PER_LVL = 0
/*
* After the embodiment has received an order,
* idle check starts. If after a given period
* (in this case, the constant below) and
* the embodiment has not received any order,
* it is now considered as idle.
*/
private constant real IDLE_PERIOD = 1.0
/*
* Idle Period Order interval
*/
private constant real IDLE_ORDER_PERIOD = 1.0
/*
* Max range for the embodiment from it's owner
*/
private constant real ESCORT_RANGE = 800
/*
* Period to order the embodiment to wander around it's owner
*/
private constant real ESCORT_WANDER_PERIOD = 0.25
/*
* Does the embodiment return to it's owner if it is
* outside the wander range?
* Note: Returns after the order has been fulfilled OR
* if it is cancelled
*/
private constant boolean RETURN_IF_OUTRANGE = false
/*
* If the embodiment is ouside the escort range, do
* you want it to forcefully return to the owner?
* This is regardless of whether the embodiment is
* casting, idle or being controlled.
*/
private constant boolean FORCE_RETURN = false
/*
* Embodiment Timed Life
*/
private constant real TIMED_LIFE = 30
private constant real TIMED_LIFE_PER_LVL = 10
/*
* Attack counter check
*/
private constant boolean ALLOW_HERO_DAMAGE = true
private constant boolean ALLOW_UNIT_DAMAGE = true
private constant boolean ALLOW_STRUCTURE_DAMAGE = true
/*
* Range from the owner to consider the embodiment
* that is has returned
*/
private constant real RETURN_RANGE = 128
/**************************************
*
* Level 1 : Shadow Assault
*
**************************************/
/*
* Level requirement
*/
private constant integer ATTACK_LEVEL_REQ = 1
/*
* Order id of Shadow Assault
* Note: the player can also cast this
* using this order
*/
private constant integer ATTACK_ORDER_ID = 851983 // "attack"
/*
* Range to acquire targets
*/
private constant real ACQUISITION_RANGE = 800
/*
* If true, it uses the owner's position to search targets
* instead of using the embodiment's position
*/
private constant boolean USE_OWNER_POSITION = false
/*
* Does the embodiment cancel it's return order to attack?
*/
private constant boolean ATTACK_CANCEL_RETURN = false
/*
* Attack cooldown of the embodiment
*/
private constant real ATTACK_COOLDOWN = 2
private constant real ATTACK_COOLDOWN_PER_LVL = -0.25
/*
* Range of the attack
* Note: the actual range is :
* ATTACK_RANGE + target collision size + embodiment collision size
*/
private constant real ATTACK_RANGE = 0
/*
* Attack damage of the embodiment
*/
private constant real ATTACK_DAMAGE = 25
private constant real ATTACK_DAMAGE_PER_LVL = 25
private constant attacktype ATK_DMG_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype ATK_DMG_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Does the attack cooldown start if the embodiment has
* returned to it's owner?
* Note: if false, the attack cooldown starts after
* the attack has landed
*/
private constant boolean START_ATK_COOLDOWN_IF_RETURNED = false
/*
* Does the embodiment cancel it's attack if it is
* outside the escort range?
* Note: This is useless if CANCEL_ORDERS_IF_OUTRANGE
* is false.
*/
private constant boolean CANCEL_ATK_IF_OUTRANGE = false
/*
* Distance check of the difference of it's target's previous
* position to it's current position.
* If the difference is larger than ENEMY_ESCAPE_DISTANCE,
* the embodiment cancels it's attack
*/
private constant real ENEMY_ESCAPE_DISTANCE = 1250
/*
* Does the attack cooldown start if it escapes?
*/
private constant boolean START_ATK_COOLDOWN_IF_ESCAPED = false
/*
* Can the attack be stopped/cancelled by the player?
* Note : through the order "Stop", the attack is cancelled.
*/
private constant boolean ATTACK_CANCELABLE = true
/*
* Does the attack cooldown start if the attack is cancelled
* by the player?
*/
private constant boolean START_ATK_COOLDOWN_IF_CANCEL = true
/*
* Pass-through damage
* Pass-through damage uses the same attacktype
* and damagetype to shadow assault
*/
private constant real PASS_DAMAGE = 10
private constant real PASS_DAMAGE_PER_LVL = 10
private constant real PASS_DAMAGE_RANGE = 64
private constant real PASS_DAMAGE_RANGE_PER_LVL = 0
/**************************************
*
* Level 2 : Veil of Ordeal
*
**************************************/
/*
* Veil of Ordeal Level Requirement
*/
private constant integer VEIL_LEVEL_REQ = 2
/*
* Required receive damage to feed the embodiment
*/
private constant real REQ_DAMAGE = 100
private constant real REQ_DAMAGE_PER_LVL = 0
/*
* Time to wait for the total damage
*/
private constant real REQ_DMG_WAIT_TIME = 2
private constant real REQ_DMG_WAIT_TIME_PER_LVL = 0
/*
* Amount of timed life increased to the embodiment
* when fed.
*/
private constant real TIMED_LIFE_INC = 0
private constant real TIMED_LIFE_INC_PER_LVL = 1
/**************************************
*
* Level 3 : Roar of Pain
*
**************************************/
/*
* Roar of Pain required level
*/
private constant integer ROAR_LEVEL_REQ = 3
/*
* Order Id of Roar of Pain
*/
private constant integer ROAR_ORDER_ID = 851971 // smart
/*
* Number of units allowed to proc the effect
*/
private constant integer ROAR_UNIT_COUNT = 3
/*
* Radius and offset for unit checking
* Note: offset is front of the embodiment
*/
private constant real ROAR_UNIT_CHECK_RADIUS = 256
private constant real ROAR_UNIT_CHECK_OFFSET = 256
/*
* Cast time for Roar of Pain
*/
private constant real ROAR_CAST_TIME = 1.5
private constant real ROAR_CAST_TIME_PER_LVL = 0
/*
* Speed of Roar of Pain
*/
private constant real ROAR_SPEED = 500
private constant real ROAR_SPEED_PER_LVL = 0
/*
* Range of Roar of Pain
*/
private constant real ROAR_TRAVEL_RANGE = 1000
private constant real ROAR_TRAVEL_RANGE_PER_LVL = 0
/*
* Roar of Pain radius
*/
private constant real ROAR_RADIUS = 256
private constant real ROAR_RADIUS_PER_LVL = 0
/*
* Roar of Pain damage
*/
private constant real ROAR_DAMAGE = 300
private constant real ROAR_DAMAGE_PER_LVL = 0
private constant attacktype ROAR_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype ROAR_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Roar stun duration
*/
private constant real ROAR_STUN_DURATION = 2
private constant real ROAR_STUN_DURATION_PER_LVL = 0
/*
* Roar cooldown
*/
private constant real ROAR_COOLDOWN = 15
private constant real ROAR_COOLDOWN_PER_LVL = 0
/*
* Does the embodiment cancel it's cast
* if outside the escort range?
*/
private constant boolean CANCEL_ROAR_IF_OUTRANGE = false
/*
* Can the player cancel the cast?
*/
private constant boolean ROAR_CANCELABLE = true
/*
* Does the roar cooldown start if cancelled?
*/
private constant boolean START_ROAR_COOLDOWN_IF_CANCEL = false
/**************************************
*
* SFX AND APPEARANCE SECTION
*
**************************************/
/*
* Embodiment Appearance
*/
private constant string EMB_MODEL = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl"
private constant real EMB_SCALE = 1.5
private constant integer EMB_RED = 255
private constant integer EMB_GREEN = 255
private constant integer EMB_BLUE = 255
private constant integer EMB_ALPHA = 240
/*
* Embodiment's segment appearances
* Take note that the MIN_SCALE refers to the
* model scale of the final unit.
* Starting from EMB_SCALE to MIN_SCALE,
* the calculated scale is linear to it.
*/
private constant integer EMB_SEGMENT_LENGTH = 8 //defines the number of segments
private constant string EMB_SEGMENT_MODEL = EMB_MODEL
private constant real MIN_SCALE = 0.25
private constant integer EMB_SEGMENT_RED = 255
private constant integer EMB_SEGMENT_GREEN = 255
private constant integer EMB_SEGMENT_BLUE = 255
private constant integer EMB_SEGMENT_ALPHA = 240
private constant real EMB_SEGMENT_SEPERATION_DIST = 16 // defines the distance between each segments
/*
*
* SPECIAL EFFECTS
*
*/
private constant string COS_ON_CAST_SFX = "" //Effect when the spell is casted
private constant string COS_CONTRACT_SFX = "" //Effect model of the contract
private constant string COS_RELEASE_SFX = "" //Effect when the spell ends or when the embodiment is summoned
private constant string EMB_ON_SUMMON_SFX = "" //Effect when the embodiment is summoned
private constant boolean ON_SUMMON_SFX_ON_SEGMENTS = true //Create the same effect on segments
private constant string ACQUIRED_TARGET_SFX = "" //Effect made when acquiring a target
private constant string ATTACK_RELEASE_SFX = "" //Effect when the attack is released
private constant string ATTACK_DAMAGE_SFX = "" //Effect on the unit when damaged by attack .
private constant string PASS_THROUGH_SFX = "" //Effect when passing on units.
private constant string VEIL_SFX = "" //Model of veil
private constant string VEIL_FEED_SFX = "" //Effect when releasing the received damage
private constant string VEIL_HEAL_SFX = "" //Effect when the embodiment receives the feed
private constant string ROAR_CHANNEL_SFX = "" //Effect when the embodiment starts channeling
private constant string ROAR_RELEASE_SFX = "" //Effect when the channel succeeds
private constant string ROAR_EXPLODE_SFX = "" //Effect of the explosions
private constant real ROAR_SEPERATION = 250 //Explosion seperation
private constant string EMB_DEATH_SFX = "" //Effect when embodiment dies
private constant boolean ON_DEATH_SFX_ON_SEGMENTS = true //Create the same effect on segments
endglobals
private function ShadowAssaultValidTarget takes unit saTarget, player embOwner returns boolean
/*
* Just put "//" before each statement to cancel those conditions
*/
return UnitAlive (saTarget) and /*
*/ IsUnitEnemy (saTarget, embOwner) and /*
*/ IsUnitVisible(saTarget, embOwner) and /*
*/ not IsUnitType (saTarget, UNIT_TYPE_SLEEPING) and /*
*/ not IsUnitType (saTarget, UNIT_TYPE_ETHEREAL) and /*
*/ not IsUnitType (saTarget, UNIT_TYPE_STRUCTURE)
endfunction
private function RoarOfPainValidTarget takes unit ropTarget, player embOwner returns boolean
/*
* Just put "//" before each statement to cancel those conditions
*/
return UnitAlive (ropTarget) and /*
*/ IsUnitEnemy (ropTarget, embOwner) and /*
*/ IsUnitVisible(ropTarget, embOwner) and /*
*/ not IsUnitType (ropTarget, UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ not IsUnitType (ropTarget, UNIT_TYPE_STRUCTURE)
endfunction
private function ReceivedDamageCheck takes integer ddType, unit dSource returns boolean
return IsUnitType(dSource, UNIT_TYPE_HERO) == ALLOW_HERO_DAMAGE or /*
*/ IsUnitType(dSource, UNIT_TYPE_STRUCTURE) == ALLOW_STRUCTURE_DAMAGE or /*
*/ ALLOW_UNIT_DAMAGE
endfunction
private function GetLevelValue takes real base, real increment, integer level returns real
return base + increment*level
endfunction
private function GetLevelIntValue takes integer base, integer increment, integer level returns integer
return base + increment*level
endfunction
/**************************************
*
* For getting the location surface z
*
**************************************/
static if (not LIBRARY_ZLibrary) then
globals
private constant location p = Location(0.0, 0.0)
endglobals
function GetSurfaceZ takes real x, real y returns real
call MoveLocation(p, x, y)
return GetLocationZ(p)
endfunction
endif
/**************************************
*
* List methods (Modules don't work
* as intended)
*
**************************************/
//! textmacro COS_LIST
private static thistype array prev
private static thistype array next
/*
* Insert node method
*/
private method insert takes nothing returns nothing
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
endmethod
/*
* Remove node method
*/
private method remove takes nothing returns nothing
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
endmethod
/*
* Check if list is empty
*/
static method operator empty takes nothing returns boolean
return next[0] == 0
endmethod
/*
* Check if list has only a single instance
*/
method operator single takes nothing returns boolean
return prev[this] == this
endmethod
//! endtextmacro
/**************************************
*
* Struct for segments to automatically
* adjust their facing.
*
**************************************/
private struct SegmentFace
/*
* u = the main unit
* nextU = the unit it's facing angle is based
* prevU = for destroying purposes
*/
readonly unit u
readonly unit nextU
readonly unit prevU
/*
* holds the object instance of the units
*/
private static Table segList
/*
* implement the list
*/
//! runtextmacro COS_LIST()
private static constant timer t = CreateTimer()
method destroy takes nothing returns nothing
/*
* remove all data
*/
set segList[GetHandleId(u)] = 0
set u = null
set nextU = null
set prevU = null
/*
* remove node
*/
call deallocate()
call remove()
/*
* if list is empty, pause timer
*/
if empty then
call PauseTimer(t)
endif
endmethod
static method operator [] takes unit iu returns thistype
return segList[GetHandleId(iu)]
endmethod
static method destroySegment takes unit du returns nothing
/*
* this method is used for chain destruction
* e.g. unit A is destroyed, destroy prev unit B
*/
call thistype[du].destroy()
endmethod
private static method period takes nothing returns nothing
local thistype this = next[0]
local real ang = 0
/*
* Iterate over instances
*/
loop
exitwhen 0 == this
/*
* get the next unit's facing
*/
set ang = GetUnitFacing(nextU)
/*
* Set the unit's facing towards the facing
* of the next unit.
*/
call SetUnitFacing(u, ang)
/*
* Negate the given angle by rotating it
* 180 degrees
*/
set ang = ang*bj_DEGTORAD + bj_PI
/*
* Move the unit to it's seperation distance
*/
call SetUnitX(u, GetUnitX(nextU) + EMB_SEGMENT_SEPERATION_DIST*Cos(ang))
call SetUnitY(u, GetUnitY(nextU) + EMB_SEGMENT_SEPERATION_DIST*Sin(ang))
set this = next[this]
endloop
endmethod
static method create takes unit tempU, unit prevLink, unit nextLink returns thistype
local thistype this
/*
* If the next unit is null, don't create an instance.
*/
if nextLink == null then
return thistype(0)
endif
/*
* allocate an isntance
*/
set this = allocate()
/*
* insert instance to the list
*/
call insert()
/*
* set the fields to the respective data
*/
set u = tempU
set nextU = nextLink
set prevU = prevLink
/*
* save the instace to the table
*/
set segList[GetHandleId(u)] = this
/*
* if the list has only one instance, start the timer.
*/
if single then
call TimerStart(t, TIMEOUT, true, function thistype.period)
endif
return this
endmethod
private static method onInit takes nothing returns nothing
set segList = Table.create()
endmethod
endstruct
private struct RoarOfPain
private real totalDistance
private real currentDistance
private real effectDistance
private real x
private real y
private real vectorX
private real vectorY
private real speed
private unit source
private real damage
private real radius
private real stunTime
private player owner
private group g = CreateGroup()
//! runtextmacro COS_LIST()
private static constant timer t = CreateTimer()
method destroy takes nothing returns nothing
/*
* Clean data
*/
set currentDistance = 0
set totalDistance = 0
set x = 0
set y = 0
set vectorX = 0
set vectorY = 0
set speed = 0
set radius = 0
set damage = 0
set stunTime = 0
set source = null
set owner = null
/*
* remove node
*/
call deallocate()
call remove()
/*
* if list is empty, pause timer
*/
if empty then
call PauseTimer(t)
endif
endmethod
private static method periodic takes nothing returns nothing
local thistype this = next[0]
local unit u
loop
exitwhen 0 == this
/*
* Check if roar has not yet reached the destination
*/
if currentDistance < totalDistance then
/*
* Increment current distance with calculated speed
*/
set currentDistance = currentDistance + speed
/*
* Move coordinates
*/
set x = x + vectorX
set y = y + vectorY
/*
* Iterate on units
*/
call GroupEnumUnitsInRange(g, x, y, radius, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
/*
* Check if picked unit is valid
*/
if RoarOfPainValidTarget(u, owner) then
/*
* if true, damage unit
*/
call UnitDamageTarget(source, u, damage, true, false, ROAR_DAMAGE_ATKTYPE, ROAR_DAMAGE_DMGTYPE, null)
/*
* Stun the unit
*/
call Stun.apply(u, stunTime, false)
endif
call GroupRemoveUnit(g, u)
endloop
/*
* Check if effect distance has reached the effect seperation
*/
if effectDistance < ROAR_SEPERATION then
set effectDistance = effectDistance + speed
else
set effectDistance = 0
if ROAR_EXPLODE_SFX != "" then
call DestroyEffect(AddSpecialEffect(ROAR_EXPLODE_SFX, x, y))
endif
endif
else
call destroy()
endif
set this = next[this]
endloop
endmethod
static method create takes unit Source, real targetAngle, integer level returns thistype
local thistype this
/*
* Check first whether the Source is valid or the level is greater than 0
*/
if Source == null or level == 0 then
return thistype(0)
endif
/*
* Allocate new instance
*/
set this = allocate()
/*
* set source
*/
set source = Source
/*
* Get position of source
*/
set x = GetUnitX(Source)
set y = GetUnitY(Source)
/*
* Get source owner
*/
set owner = GetOwningPlayer(Source)
/*
* Calculate speed
*/
set speed = GetLevelValue(ROAR_SPEED, ROAR_SPEED_PER_LVL, level)*TIMEOUT
/*
* Calculate vectors
*/
set vectorX = speed*Cos(targetAngle)
set vectorY = speed*Sin(targetAngle)
/*
* Setup of distance data
*/
set currentDistance = 0
set effectDistance = 0
set totalDistance = GetLevelValue(ROAR_TRAVEL_RANGE, ROAR_TRAVEL_RANGE_PER_LVL, level)
/*
* Calculate effect radius
*/
set radius = GetLevelValue(ROAR_RADIUS, ROAR_RADIUS_PER_LVL, level)
/*
* Calculate stun duration
*/
set stunTime = GetLevelValue(ROAR_STUN_DURATION, ROAR_STUN_DURATION_PER_LVL, level)
/*
* insert node
*/
call insert()
/*
* if the list has only one instance, start timer
*/
if single then
call TimerStart(t, TIMEOUT, true, function thistype.periodic)
endif
return this
endmethod
endstruct
private function LinearR takes real a, real b, real t returns real
return a + (b - a)*t
endfunction
private function LinearInt takes integer a, integer b, real t returns integer
return R2I(LinearR(I2R(a), I2R(b), t))
endfunction
private struct Embodiment
/*
* Embodiment data
*/
private unit u // the embodiment
private unit owner // the owner
private player pOwner // the owning player
private effect mdl // the model of the unit
private unit prevU // the link to the first segment
private Table segmentMDLs // the segment's mdls
private integer segmentCount
private real speed // the movespeed of the embodiment
private real tx
private real ty // the target points (movement, wander)
private boolean returning // states
private boolean isIdle
private integer level // level of the ability
private group targets // for picking random targets
private real timedLife // amount of time
private real incLife // amount of time to increment
private boolean hasVeilOfOrdeal
/*
* Shadow Assault variables
*/
private boolean hasShadowAssault // indicates if the embodiment has "learned" the ability
private real acqRange // the acquisition range
private boolean attacking // state of embodiment
private real atkTime // amount of attack cooldown
private timer atkTimer // the timer for the attack
private real atkDamage // amount of attack damage
private real passDamage // amount of damage dealt by passing through
private real passRadius // radius of pass-through
private group passTargets // for picking pass-through targets
/*
* Roar of Pain
*/
private boolean hasRoarOfPain // indicates if the embodiment has "learned" the ability
private real roarCastTime // amount of time to cast
private timer roarCastTimer // timer for the cast
private real roarCDTime // amount of time for the roar's cooldown
private timer roarCDTimer // timer for the cooldown
private group roarCheck // check for valid casting
private effect castSFX // channeling sfx
//! runtextmacro COS_LIST()
private static constant timer t = CreateTimer()
method destroy takes nothing returns nothing
local unit temp
local unit temp2
/*
* Destroy all models
*/
call DestroyEffect(mdl)
set mdl = null
loop
exitwhen segmentCount == 0
call DestroyEffect(segmentMDLs.effect[segmentCount])
set segmentCount = segmentCount - 1
endloop
/*
* Destroy the segments
*/
call RecycleMissile(u)
set temp = prevU
loop
exitwhen temp == null
set temp2 = SegmentFace[temp].prevU
call SegmentFace.destroySegment(temp)
call RecycleMissile(temp)
set temp = temp2
endloop
set u = null
set prevU = null
/*
* Clear data
*/
set owner = null
set pOwner = null
set speed = 0
set tx = 0
set ty = 0
set returning = false
set isIdle = false
set timedLife = 0
set incLife = 0
set acqRange = 0
set atkTime = 0
set atkDamage = 0
set passDamage = 0
set passRadius = 0
set roarCastTime = 0
set roarCDTime = 0
/*
* remove node
*/
call deallocate()
call remove()
/*
* if list is empty, pause timer
*/
if empty then
call PauseTimer(t)
endif
endmethod
endstruct
endlibrary