No_exit
Regular User (What is Custom User Title?)
- Reaction score
- 40
Soul Slave
General information
Gui/Jass/vJass?: vJass
JESP?: I think so but correct me if I'm wrong.
Lags?: The first framedrop on high end pcs is around 70 souls on a single hero. Lag will really start to kick in from around 120 souls. Feedback on low end pc performance will be greatly appreciated.
MUI?: Yes but do read the above line again.
Type:: Aura I guess?
N-levels?: Yes
Functionality
Whenever a nearby unit (friendly or hostile) dies, the souls of this unit will be your slave (the amount of souls depends on the dying unit level).
The soul slaves are forced to follow your hero around and can only free themselves by suiciding on an enemy unit which deals damage depending on the ability level.
Pictures
Picture 1:
Picture 2:
Picture 3:
Soul Position
A lot of work has been made in the position of the soul to make it feel more natural and to make it look better.
Souls will orbit at a random radius. Souls that are further away from the hero will take a longer time to orbit around the hero (The orbit radius is randomly chosen and the direction is randomly chosen clockwise or counter clockwise). (See picture 1)
The relative position of the hero and the soul are not fixed, this means that if the hero moves the souls will be "dragged" towards this direction. (see Picture 2)
Whenever a leading soul acquires a target, it will speed up. If it loses a target, it will slow down again. All other souls will follow their previous soul. (See Picture 3)
Soul Color
The souls will vary their colors depending on what state they are in: birth (default: blue), normal(default: green) or angry(default: red). Transition between the colors is smoothly (see pictures 1 and 3).
Code
Confused or still not convinced:
Try the demomap .
Comments
- This is my first vJass so any comments(bugs, improvements, remarks, ideas, lovemessages, hatemessages ...) are greatly appreciated :thup:.
- If this spell rapes low end pcs then please tell me so so I can further optimize the code.
- In the Soul Struct, There is not really a need to store the previous soul in a chain. This is in due to early designs that allowed all non-leading souls to acquire targets too and leave their chains. I dropped this idea to get better performance.
- If there is a lot of interest, I might change the spell such that you can activate and deactivate and the soul spawning takes a bit of mana everytime.
- Enjoy the skill as much I enjoyed making it .
Changelog
13 May 2009: Released v1
14 May 2009: Made functions private/Timer no longer runs idle/Improved a boolexpr (Thanks to Vestras/kenny!/kenny!)
15 May 2009: Made death animation scale configurable/Made Soul struct private/Checks to see if a unit is dead now use InUnitType instead of GetUnitState/Rewrote and simplified init/removed onDestroy. (Thanks to BlackRose/Vestras/Romek/kenny!+Romek/Romek)
General information
Gui/Jass/vJass?: vJass
JESP?: I think so but correct me if I'm wrong.
Lags?: The first framedrop on high end pcs is around 70 souls on a single hero. Lag will really start to kick in from around 120 souls. Feedback on low end pc performance will be greatly appreciated.
MUI?: Yes but do read the above line again.
Type:: Aura I guess?
N-levels?: Yes
Functionality
Whenever a nearby unit (friendly or hostile) dies, the souls of this unit will be your slave (the amount of souls depends on the dying unit level).
The soul slaves are forced to follow your hero around and can only free themselves by suiciding on an enemy unit which deals damage depending on the ability level.
Pictures
Picture 1:
Picture 2:
Picture 3:
Soul Position
A lot of work has been made in the position of the soul to make it feel more natural and to make it look better.
Souls will orbit at a random radius. Souls that are further away from the hero will take a longer time to orbit around the hero (The orbit radius is randomly chosen and the direction is randomly chosen clockwise or counter clockwise). (See picture 1)
The relative position of the hero and the soul are not fixed, this means that if the hero moves the souls will be "dragged" towards this direction. (see Picture 2)
Whenever a leading soul acquires a target, it will speed up. If it loses a target, it will slow down again. All other souls will follow their previous soul. (See Picture 3)
Soul Color
The souls will vary their colors depending on what state they are in: birth (default: blue), normal(default: green) or angry(default: red). Transition between the colors is smoothly (see pictures 1 and 3).
Code
JASS:
scope ness initializer Init
globals
//-----------------
//----- Setup -----
//-----------------
//These variables are a bridge between the map and the (vJass) code.
private constant integer SPELL_ID = 039;A001039; //Spell ID
private constant integer SOUL_ID = 039;e001039; //Soul ID
//-------------------------
//----- Configurables -----
//-------------------------
//These variables can be changed to adapt the ability for your needs.
//Damage Configurables.
private constant real DAMAGE_OFFSET = 0. //Basic damage a soul does.
private constant real DAMAGE_PERLEVEL = 16. //Damage that is added for every level.
private constant attacktype DAMAGE_ATTACKTYPE = ATTACK_TYPE_MAGIC //The attacktype that the damage does.
private constant damagetype DAMAGE_DAMAGETYPE = DAMAGE_TYPE_DEATH //The weapontype that the damage does.
private constant weapontype DAMAGE_WEAPONTYPE = WEAPON_TYPE_WHOKNOWS //The weapontype that the damage does.
//Spawn Configurables.
private constant real SPAWN_AMOUNT_OFFSET = .8 //Amount of souls that spawn. (on average)
private constant real SPAWN_AMOUNT_PERABILLEVEL = .0 //Amount of extra souls you get per ability level increase. (on average)
private constant real SPAWN_AMOUNT_PERUNITLEVEL = .6 //Amount of extra souls you get per unit level of the dying unit. (on average)
private constant real SPAWN_RADIUS = 800. //The maximum distance at which a unit will be coverted to souls.
private constant real SPAWN_INTERVAL = .32 //Time between the spawning of souls from one dying unit.
private constant real SPAWN_ILLNESS = 1.4 //Interval in which souls will not acquire any targets. MUST BE BIGGER THEN SPAWN_INTERVAL TO AVOID SPAWNING TO STOP, I warned you.
//Position Configurables.
private constant real MAIN_INTERVAL = .035 //Interval at which the souls will rotate around the caster.
private constant real SPEED_ROTATE_NORMAL = 10. //Desired speed of the soul (per tick)
private constant real SPEED_ROTATE_VICTIM = 20. //Desired speed of the soul when they have a victim (per tick).
private constant real SPEED_CATCHUP_SPEED_PERC = .11 //Has to be between 0. and 1. where 0. would mean it would never go to the desired location and 1. would mean it would go there instantly.
private constant real SPEED_CATCHUP_RADIUS_PERC = .09 //Has to be between 0. and 1. where 0. would mean it would never go to the desired location and 1. would mean it would go there instantly.
private constant real SPEED_CATCHUP_COLOR_PERC = .14 //Has to be between 0. and 1. where 0. would mean it would never go to the desired color and 1. would mean it would go there instantly.
private constant real DES_DIST = 48. //Desired distance between souls
private constant real RADIUS_MIN = 90. //(Desired) minimum distance between the caster and the soul
private constant real RADIUS_MAX = 230. //(Desired) maximum distance between the caster and the soul
private constant real CLOCKWISE_CHANCE = .5 //1. is always rotate clockwise, 0. is always counter clockwise, .3 is 30% clockwise and 70% counter clockwise.
//Color Configurables.
private constant integer COLOR_OPAQUE = 224 //The opaque level of the souls color.
//Color that the soul will spawn with. (All values are between 0 and 255)
private constant integer COLOR_SPAWN_RED = 0
private constant integer COLOR_SPAWN_GREEN = 0
private constant integer COLOR_SPAWN_BLUE = 255
//Color that the soul will try to maintain when it has no victim. (All values are between 0 and 255)
private constant integer COLOR_NORMAL_RED = 0
private constant integer COLOR_NORMAL_GREEN = 255
private constant integer COLOR_NORMAL_BLUE = 0
//Color that the soul will try to maintain when it has a victim. (All values are between 0 and 255)
private constant integer COLOR_ANGRY_RED = 255
private constant integer COLOR_ANGRY_GREEN = 0
private constant integer COLOR_ANGRY_BLUE = 0
//Other Victim Configurables.
private constant integer VICTIM_RANGE = 222 //Range in which the soul will acquire a victim.
private constant integer VICTIM_DAMAGE_RANGE = 90 //Range in which the soul will need to be in order to suicide and damage the victim.
private constant integer VICTIM_FOLLOW_RANGE = 1200 //Range from which the soul will stop chasing the victim and follow the normal path again. (Between the Caster and the Victim, so not the soul).
//Death configurables.
private constant real DEATH_SCALE = .3 //The size that the death animation will be.
endglobals
//----------------------------------
//----- Configurable functions -----
//----------------------------------
//This function will determine if the dying unit was a valid one (and that you need to spawn new souls).
//This is an important function if you have other dummy units that die.
private function IsValidUnit takes nothing returns boolean
return GetUnitTypeId(GetDyingUnit()) != SOUL_ID and IsUnitType(GetDyingUnit(), UNIT_TYPE_STRUCTURE) == false
endfunction
//The damage function (because I know you want to).
private constant function CalculateDamage takes integer level returns real
return (level * DAMAGE_PERLEVEL) + DAMAGE_OFFSET
endfunction
//The amount of souls that spawns function (because I know you want to).
private function SpawnAmount takes unit dyingUnit, integer lvlSpell returns integer
local real target = SPAWN_AMOUNT_OFFSET + GetUnitLevel(dyingUnit) * SPAWN_AMOUNT_PERUNITLEVEL + lvlSpell * SPAWN_AMOUNT_PERABILLEVEL
local integer effective = R2I(target)
if target - effective < GetRandomReal(0.,1.) then //for example if target is 5.3 then you will have 5 spawns + 30% chance to have 1 more.
return effective + 1
endif
return effective
endfunction
//The desired Radius. (Used for the desired distance between the soul and the hero.)
private function GetDesiredRadius takes nothing returns real
return GetRandomReal(RADIUS_MIN,RADIUS_MAX)
endfunction
//The position update you do compared to your desired position. (This will determine the amount of space between
private constant function CatchupSpeed takes real current_distance, real desired_distance returns real
return (1.-SPEED_CATCHUP_SPEED_PERC) * current_distance + SPEED_CATCHUP_SPEED_PERC * desired_distance
endfunction
//The radius update you do compared to your desired radius.
private constant function CatchupRadius takes real current_distance, real desired_distance returns real
return (1.-SPEED_CATCHUP_RADIUS_PERC) * current_distance + SPEED_CATCHUP_RADIUS_PERC * desired_distance
endfunction
//The color update you do compared to your desired color.
private constant function CatchupColor takes real current_distance, real desired_distance returns real
return (1.-SPEED_CATCHUP_COLOR_PERC) * current_distance + SPEED_CATCHUP_COLOR_PERC * desired_distance
endfunction
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!! Don't change anything past here unless if you know what you are doing !!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
private keyword Soul
globals
//---------------------------
//----- Other variables -----
//---------------------------
private trigger gg_trg_maintimer = CreateTrigger() //Main trigger.
private trigger gg_trg_dyingunit = CreateTrigger() //Dying unit trigger.
private Soul array souls //Contains the first soul of the chain.
private integer maxsouls = -1 //Max amount of souls.
private timer main_timer = CreateTimer()
//-----------------
//----- Cache -----
//-----------------
//These variables are kept to (hopefully) speed up the spell.
private boolexpr cache_filteralive
private group cache_group = CreateGroup()
private player cache_player
endglobals
//Filter functions.
private function IsFilterAlive takes nothing returns boolean
return IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private struct Soul
//All souls have
unit u //The soul itself.
unit caster //The one it's circling around.
Soul nxt //Next soul in the chain.
Soul pre //Previous soul in the chain.
real sign // Will determine if something is clockwise or not.
real radius //Distance between this soul and the caster.
real speed //Speed of the unit
real red //Color of the soul (red)
real green // ... (green)
real blue // ... (blue)
real illness //Amount of time that this wisp will not try get a target.
//Only the first souls in the chain need
unit victim //Sees if the soul aquired a victim to suicide on.
real desRadius //Desired radius.
//All souls except the ones that are first in their chain need
real dist //Distance between this soul and the previous soul.
//Only the last souls in the chain need
integer spawnAmount //Amount of souls still to spawn behind you.
real spawnTime //Amount of time for the next soul to spawn.
real xCreate //Where this unit is created (and the next one will be created).
real yCreate //Where this unit is created (and the next one will be created).
static method create takes unit c, Soul p, real s_x, real s_y, integer extraAmount returns Soul
local real c_x = GetUnitX(c)
local real c_y = GetUnitY(c)
local real dx = s_x - c_x
local real dy = s_y - c_y
local real angle
local real radius = SquareRoot(dx * dx + dy * dy)
local Soul d = Soul.allocate()
set d.u = CreateUnit(GetOwningPlayer(c), SOUL_ID, s_x, s_y, 270.)
call SetUnitColor(d.u, PLAYER_COLOR_LIGHT_GRAY)
set d.caster = c
if radius < 10. then
set radius = 10.
endif
set d.radius = radius
set d.speed = 0
set d.red = COLOR_SPAWN_RED
set d.green = COLOR_SPAWN_GREEN
set d.blue = COLOR_SPAWN_BLUE
call SetUnitVertexColor(d.u, R2I(d.red), R2I(d.green), R2I(d.blue), COLOR_OPAQUE)
set d.victim = null
set d.xCreate = s_x
set d.yCreate = s_y
set d.illness = SPAWN_ILLNESS
set d.nxt = 0 //This will be set by the next one.
set d.pre = p
if p == 0 then //Checking if we have a previous or not.
set d.dist = 0.
set d.desRadius = GetDesiredRadius()
//Setting spawn amount and time.
set d.spawnAmount = extraAmount - 1
set d.spawnTime = SPAWN_INTERVAL
if (GetRandomReal(0.,1.) <= CLOCKWISE_CHANCE) then
set d.sign = -1
else
set d.sign = 1
endif
set maxsouls = maxsouls + 1 //Updating the soul max.
set souls[maxsouls] = d
else
set p.nxt = d
set d.sign = p.sign
set d.desRadius = p.desRadius
//We steal data from previous Soul.
set d.spawnAmount = d.spawnAmount + p.spawnAmount
set p.spawnAmount = 0
set d.spawnTime = p.spawnTime
set p.spawnTime = 0
//Get first distance to previous
//set d.dist = d.distanceToPrevious()
set angle = d.sign * (Atan2(GetUnitY(p.u) - c_y, GetUnitX(p.u) - c_x) - Atan2(s_y - c_y, s_x - c_x))
if angle < 0 then
set angle = angle + 2 * bj_PI
endif
set d.dist = angle * radius
endif
return d
endmethod
endstruct
private function MainTick takes nothing returns nothing
// This function is called (1 / MAIN_TIMER) times per second.
// With the default values this means about 30 times per second.
// This function is made to be very efficient (at the cost of readability/modifiability/reusability)
// Also if you don't like maths then this function might not be your favorit one.
local integer s = 0
local Soul mainSoul //First of the chain.
local Soul curSoul //Current Soul in the chain.
local real cx
local real cy
local real sx
local real sy
local real vx
local real vy
local real dx
local real dy
local real radius
local real d
local real angle
local real angle_pre
local real angle_diff
loop //MainLoop over all leading souls.
exitwhen s > maxsouls
set mainSoul = souls<s>
if IsUnitType(mainSoul.caster, UNIT_TYPE_DEAD) == true then
loop //Kill the chain(all except the last)
call KillUnit(mainSoul.u) //Kill all soul units in the chain. (inclusive of the first and last)
exitwhen mainSoul.nxt == 0
set mainSoul = mainSoul.nxt
call mainSoul.pre.destroy() //You can't just destroy a soul and then ask the next, hence this weird way of doing it.
endloop
call mainSoul.destroy() //Destroy the last
set souls<s> = souls[maxsouls] //Update the soul array.
set maxsouls = maxsouls - 1
elseif IsUnitType(mainSoul.u, UNIT_TYPE_DEAD) == true then
//Need to remove but we need to get a new leader for the chain if needed.
if mainSoul.nxt == 0 then
set souls<s> = souls[maxsouls]
set maxsouls = maxsouls - 1
else
//We get all the data from the leader to the new soul.
set curSoul = mainSoul.nxt
//Keep in mind that a lot of these values are calculated and set only by the first in the soulchain.
//So we gain efficiency by not having to assign these values every main timer tick.
set curSoul.pre = 0
set curSoul.victim = mainSoul.victim
set curSoul.desRadius = mainSoul.desRadius
set curSoul.sign = curSoul.sign
set souls<s> = curSoul //Bow down to the new leader.
endif
call mainSoul.destroy()
else //A good soul.
//Getting Coordinates and distances.
//Caster coordinates.
set cx = GetUnitX(mainSoul.caster)
set cy = GetUnitY(mainSoul.caster)
//Soul coordinates.
set sx = GetUnitX(mainSoul.u)
set sy = GetUnitY(mainSoul.u)
//Distance between caster and soul.
set dx = cx - sx
set dy = cy - sy
set radius = SquareRoot(dx * dx + dy * dy) //Current distance between the caster and the soul.
if radius < 10. then
set radius = 10. //To avoid divisions by 0. Why 10? This way we hope to avoid to come in this situation again too soon.
endif
//Updating the victim.
//Clearing old target (if needed)
if mainSoul.victim != null and IsUnitType(mainSoul.victim, UNIT_TYPE_DEAD) == true then
set mainSoul.victim = null
endif
//Finding new target (if needed)
if mainSoul.victim == null and mainSoul.illness <= 0 then //Can't use elseif due to actions in the previous if
set cache_player = GetOwningPlayer(mainSoul.caster)
call GroupClear(cache_group)
call GroupEnumUnitsInRange(cache_group, sx, sy, VICTIM_RANGE , cache_filteralive)
loop
set mainSoul.victim = FirstOfGroup(cache_group)
exitwhen mainSoul.victim == null or IsUnitEnemy(mainSoul.victim, cache_player)
call GroupRemoveUnit(cache_group, mainSoul.victim)
endloop
//We don' use the group elsewhere so we clean it up immediatly.
elseif mainSoul.illness > 0 then
set mainSoul.illness = mainSoul.illness - MAIN_INTERVAL //This can cause illness to drop under 0 but we don't really care.
endif
//Final update
if mainSoul.victim == null then
set mainSoul.speed = CatchupSpeed(mainSoul.speed, SPEED_ROTATE_NORMAL)
set mainSoul.radius = CatchupRadius(radius, mainSoul.desRadius) //New Radius
set angle = Atan2(sy - cy, sx - cx) + mainSoul.sign * mainSoul.speed / mainSoul.radius //New angle
if mainSoul.illness <= 0. then
set mainSoul.red = CatchupColor(mainSoul.red, COLOR_NORMAL_RED)
set mainSoul.blue = CatchupColor(mainSoul.blue, COLOR_NORMAL_BLUE)
set mainSoul.green = CatchupColor(mainSoul.green, COLOR_NORMAL_GREEN)
call SetUnitVertexColor(mainSoul.u, R2I(mainSoul.red), R2I(mainSoul.green), R2I(mainSoul.blue), COLOR_OPAQUE)
endif
else
set vx = GetUnitX(mainSoul.victim)
set vy = GetUnitY(mainSoul.victim)
set dx = vx - cx
set dy = vy - cy
set d = SquareRoot(dx * dx + dy * dy) //Distance between victim and caster
set mainSoul.radius = CatchupRadius(mainSoul.radius, d)
if d > VICTIM_FOLLOW_RANGE then
//out of range, abort
set angle = Atan2(sy - cy, sx - cx) + mainSoul.sign * mainSoul.speed / mainSoul.radius
set mainSoul.speed = CatchupSpeed(mainSoul.speed, SPEED_ROTATE_NORMAL)
set mainSoul.victim = null
else
//set angle = Atan2(vy - cy, vx - cx) + angle
//if angle < 0 then
// set angle = angle + 2 * bj_PI
//endif
set mainSoul.speed = CatchupSpeed(mainSoul.speed, SPEED_ROTATE_VICTIM)
set angle = Atan2(sy - cy, sx - cx) + mainSoul.sign * mainSoul.speed / mainSoul.radius
set dx = vx - sx
set dy = vy - sy
set d = SquareRoot(dx * dx + dy * dy) //Distance between victim and the soul
if d < VICTIM_DAMAGE_RANGE then
call UnitDamageTarget(mainSoul.caster, mainSoul.victim, CalculateDamage(GetUnitAbilityLevel(mainSoul.caster, SPELL_ID)), false, false, DAMAGE_ATTACKTYPE, DAMAGE_DAMAGETYPE, DAMAGE_WEAPONTYPE)
call SetUnitScale(mainSoul.u,DEATH_SCALE,DEATH_SCALE,DEATH_SCALE)
call KillUnit(mainSoul.u)
endif
endif
set mainSoul.red = CatchupColor(mainSoul.red, COLOR_ANGRY_RED)
set mainSoul.blue = CatchupColor(mainSoul.blue, COLOR_ANGRY_BLUE)
set mainSoul.green = CatchupColor(mainSoul.green, COLOR_ANGRY_GREEN)
call SetUnitVertexColor(mainSoul.u, R2I(mainSoul.red), R2I(mainSoul.green), R2I(mainSoul.blue), COLOR_OPAQUE)
endif
call SetUnitX(mainSoul.u, cx + mainSoul.radius * Cos(angle)) //New x coord
call SetUnitY(mainSoul.u, cy + mainSoul.radius * Sin(angle)) //New y coord
set curSoul = mainSoul
set angle_pre = angle
//update rest of chain too
loop
exitwhen curSoul.nxt == 0 //Note that you aren't allowed to do circular soul chains.
set curSoul = curSoul.nxt
if curSoul.illness > 0 then
set curSoul.illness = curSoul.illness - MAIN_INTERVAL
else
set curSoul.red = CatchupColor(curSoul.red, curSoul.pre.red)
set curSoul.green = CatchupColor(curSoul.green, curSoul.pre.green)
set curSoul.blue = CatchupColor(curSoul.blue, curSoul.pre.blue)
call SetUnitVertexColor(curSoul.u, R2I(curSoul.red), R2I(curSoul.green), R2I(curSoul.blue), COLOR_OPAQUE)
endif
set sx = GetUnitX(curSoul.u)
set sy = GetUnitY(curSoul.u)
set curSoul.radius = CatchupRadius(curSoul.radius, curSoul.pre.radius)
set curSoul.speed = CatchupSpeed(curSoul.speed, curSoul.pre.speed)
set angle = Atan2(sy - cy, sx - cx) + mainSoul.sign * (curSoul.speed + curSoul.dist - CatchupSpeed(curSoul.dist, DES_DIST)) / curSoul.radius //New angle
call SetUnitX(curSoul.u, cx + curSoul.radius * Cos(angle)) //New x coord
call SetUnitY(curSoul.u, cy + curSoul.radius * Sin(angle)) //New y coord
set angle_diff = curSoul.sign * (angle_pre - angle)
if angle_diff < 0 then
set angle_diff = angle_diff + 2 * bj_PI
endif
set curSoul.dist = angle_diff * curSoul.radius
set angle_pre = angle
endloop
//Extra commands for the last soul.
if curSoul.spawnAmount > 0 then
set curSoul.spawnTime = curSoul.spawnTime - MAIN_INTERVAL
if curSoul.spawnTime <= 0 then
set curSoul.spawnAmount = curSoul.spawnAmount - 1
set curSoul.spawnTime = curSoul.spawnTime + SPAWN_INTERVAL
call Soul.create(mainSoul.caster, curSoul, mainSoul.xCreate, mainSoul.yCreate, 0)
endif
endif
//To the next soul chain.
set s = s + 1
endif //end of good soul.
endloop
if maxsouls < 0 then
call PauseTimer(main_timer)
endif
endfunction
private function DyingUnit takes nothing returns nothing
//Input
local unit dying = GetDyingUnit()
//Other locals
local real x = GetUnitX(dying)
local real y = GetUnitY(dying)
local group g = CreateGroup()
local unit u
local integer lvl
call GroupEnumUnitsInRange(g, x, y, SPAWN_RADIUS, cache_filteralive)
loop
set u = FirstOfGroup(g)
exitwhen u == null
set lvl = GetUnitAbilityLevel(u, SPELL_ID)
if lvl > 0 then
if maxsouls < 0 then //No souls were currently active.
call TimerStart(main_timer, MAIN_INTERVAL, true, function MainTick) //Lights, camera, action!
endif
call Soul.create(u, 0, x, y, SpawnAmount(dying, lvl))
endif
call GroupRemoveUnit(g, u)
endloop
//cleanup
call DestroyGroup(g)
set g = null
//u is already null.
endfunction
private function Init takes nothing returns nothing
//Cache inits
set cache_filteralive = Condition(function IsFilterAlive)
//Triggers
//Trigger: A unit dies.
call TriggerRegisterAnyUnitEventBJ(gg_trg_dyingunit, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(gg_trg_dyingunit, Condition(function IsValidUnit))
call TriggerAddAction(gg_trg_dyingunit, function DyingUnit)
endfunction
endscope</s></s></s></s>
Confused or still not convinced:
Try the demomap .
Comments
- This is my first vJass so any comments(bugs, improvements, remarks, ideas, lovemessages, hatemessages ...) are greatly appreciated :thup:.
- If this spell rapes low end pcs then please tell me so so I can further optimize the code.
- In the Soul Struct, There is not really a need to store the previous soul in a chain. This is in due to early designs that allowed all non-leading souls to acquire targets too and leave their chains. I dropped this idea to get better performance.
- If there is a lot of interest, I might change the spell such that you can activate and deactivate and the soul spawning takes a bit of mana everytime.
- Enjoy the skill as much I enjoyed making it .
Changelog
13 May 2009: Released v1
14 May 2009: Made functions private/Timer no longer runs idle/Improved a boolexpr (Thanks to Vestras/kenny!/kenny!)
15 May 2009: Made death animation scale configurable/Made Soul struct private/Checks to see if a unit is dead now use InUnitType instead of GetUnitState/Rewrote and simplified init/removed onDestroy. (Thanks to BlackRose/Vestras/Romek/kenny!+Romek/Romek)