Ayanami
칼리
- Reaction score
- 288
Introduction
Decided to make Shadowfiend's spell in vJASS for fun.
Requirements
- Timer32
- GroupUtils
The spells:
Details
- The spells are vJASS
- They should be leak-less and lag-less
- It is MUI, meaning can be cast many times at the same instance
- Shadowfiend from DoTA
Implementation
Spells
Shadowraze
Description:
Gives the Shadow Fiend the power to desecrate regions in front of him at varying distances (200/450/700).
Level 1 - 75 damage.
Level 2 - 150 damage.
Level 3 - 225 damage.
Level 4 - 300 damage.
Cast Range: Varies
Target Type: Instant
Cooldown: 10 seconds
Screenshot:
Code:
Necromastery
Description:
Whenever the Shadow Fiend kills a target, he stores the unfortunate soul inside of him. For each stored soul he gains 2 bonus damage until his own death releases half of them from bondage.
Level 1 - 16 damage limit.
Level 2 - 30 damage limit.
Level 3 - 46 damage limit.
Level 4 - 60 damage limit.
Passive
Note: The souls (black projectile) must reach the hero in before providing its bonus damage.
Screenshot:
Code:
Presence of the Dark Lord
Description:
The presence of such a horrible creature terrifies nearby enemies, reducing their armor.
Level 1 - 2 armor reduction.
Level 2 - 3 armor reduction.
Level 3 - 4 armor reduction.
Level 4 - 5 armor reduction.
Passive
Screenshot:
Requiem of Souls
Description:
Summons evil spirits around you dealing damage to units in the area. Number of spirits is related to the number of souls stored and the movement/damage reduction is related to the distance from the Shadow Fiend. Lowers movement speed and damage of nearby units. The closer the units are the greater the effect. Reduction lasts 5 seconds.
Level 1 - 80 damage for each line, 15% reduction.
Level 2 - 120 damage for each line, 20% reduction.
Level 3 - 160 damage for each line, 25% reduction.
Cast Range: Self
Target Type: Instant
Cast Time: 1 second
Cooldown: 120/110/100 seconds
Note: I believe DoTA actually uses multiple carrion swarms for Requiem of Souls. However, for configuration purposes and better visual effects, I did not.
Screenshot:
Code:
Credits
Changelogs
Feedback will be appreciated.
Decided to make Shadowfiend's spell in vJASS for fun.
Requirements
- Timer32
- GroupUtils
The spells:
- Shadowraze [Active]
- Necromastery [Passive]
- Presence of the Dark Lord [Passive]
- Requiem of Souls [Active]
Details
- The spells are vJASS
- They should be leak-less and lag-less
- It is MUI, meaning can be cast many times at the same instance
- Shadowfiend from DoTA
Implementation
JASS:
//==============================================================================
// SHADOW FIELD SPELLPACK v1.2
// BY Ayanami
//==============================================================================
//==============================================================================
// REQUIREMENTS
//==============================================================================
//
// - vJASS Compiler (NewGen)
// - Timer32
// - GroupUtils
//
//==============================================================================
//==============================================================================
// IMPLEMENTATION
//==============================================================================
//
// 1) Copy the whole "Required Systems" Trigger folder & paste in map
// 2) Save the map, it will take a bit longer than usual
// 3) After saving, close the map and re-open it
// 4) Delete or disable the trigger "Objects"
// 5) Copy all 8 abilities under "Undead" & paste in map
// 6) Copy the single buff under "Undead" & paste in map
// 7) Ensure that the following abilities have their buff set properly:
// Presence of the Dark Lord - Presence of the Dark Lord
// 8) Copy the whole "Shadowfiend" Trigger folder
// 9) Go through all the spell Configurations
//
//==============================================================================
Spells
Shadowraze
Description:
Gives the Shadow Fiend the power to desecrate regions in front of him at varying distances (200/450/700).
Level 1 - 75 damage.
Level 2 - 150 damage.
Level 3 - 225 damage.
Level 4 - 300 damage.
Cast Range: Varies
Target Type: Instant
Cooldown: 10 seconds
Screenshot:

Code:
JASS:
scope Shadowraze initializer OnInit // requires GroupUtils
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 'ABSR' // raw code of ability "Shadowraze"
private constant integer ABILIDZ = 'ASR0' // raw code of ability "Shadowraze (Z)"
private constant integer ABILIDX = 'ASR1' // raw code of ability "Shadowraze (X)"
private constant integer ABILIDC = 'ASR2' // raw code of ability "Shadowraze (C)"
private constant integer PRELOADID = 'prel' // raw code of unit "Preloader"
private constant integer DUMMYID = 'sHAD' // raw code of unit "Shadowraze Dummy", acts as the visual effect itself
private constant real ANIMDUR = 2.0 // duration of DUMMYID
private constant integer RED = 0 // red vertex color of DUMMYID
private constant integer GREEN = 0 // green vertex color of DUMMYID
private constant integer BLUE = 0 // blue vertex color of DUMMYID
private constant integer TRANS = 255 // transparency of DUMMYID, where 0 is fully transparent
private constant real SCALE = 1.2 // scale size of DUMMYID
private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage
endglobals
private function GetDamage takes integer level returns real
return 75.0 * level // damage dealt
endfunction
private function GetArea takes integer level returns real
return 250.0 // area of effect
endfunction
private function GetRangeZ takes integer level returns real
return 200.0 // range of "Shadowraze (Z)"
endfunction
private function GetRangeX takes integer level returns real
return 450.0 // range of "Shadowraze (X)"
endfunction
private function GetRangeC takes integer level returns real
return 700.0 // range of "Shadowraze (C)"
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
native UnitAlive takes unit id returns boolean
globals
private unit Caster
private real Damage
endglobals
private function OnRemove takes nothing returns boolean
local trigger t = GetTriggeringTrigger()
call RemoveUnit(GetTriggerUnit())
call TriggerClearConditions(t)
call DestroyTrigger(t)
set t = null
return false
endfunction
private function FilterFunc takes nothing returns boolean
local unit u = GetFilterUnit()
if IsUnitEnemy(u, GetOwningPlayer(Caster)) and UnitAlive(u) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
call UnitDamageTarget(Caster, u, Damage, true, false, ATK, DMG, WEP)
endif
set u = null
return false
endfunction
private function Actions takes nothing returns boolean
local trigger t
local unit u
local integer spellid = GetSpellAbilityId()
local integer level
local real range
local real a
local real x
local real y
if spellid == ABILIDZ or spellid == ABILIDX or spellid == ABILIDC then
set Caster = GetTriggerUnit()
set level = GetUnitAbilityLevel(Caster, ABILID)
set Damage = GetDamage(level)
if spellid == ABILIDZ then
set range = GetRangeZ(level)
elseif spellid == ABILIDX then
set range = GetRangeX(level)
else
set range = GetRangeC(level)
endif
set a = GetUnitFacing(Caster) * bj_DEGTORAD
set x = GetUnitX(Caster) + range * Cos(a)
set y = GetUnitY(Caster) + range * Sin(a)
set u = CreateUnit(GetOwningPlayer(Caster), DUMMYID, x, y, a * bj_RADTODEG)
call SetUnitVertexColor(u, RED, GREEN, BLUE, TRANS)
call SetUnitScale(u, SCALE, SCALE, SCALE)
call UnitApplyTimedLife(u, 'BTLF', ANIMDUR)
set t = CreateTrigger()
call TriggerRegisterDeathEvent(t, u)
call TriggerAddCondition(t, Condition(function OnRemove))
call GroupEnumUnitsInArea(ENUM_GROUP, x, y, GetArea(level), Filter(function FilterFunc))
endif
set t = null
set u = null
return false
endfunction
private function Learn takes nothing returns boolean
local unit u
local integer level
if GetLearnedSkill() == ABILID then
set u = GetTriggerUnit()
set level = GetUnitAbilityLevel(u, ABILID)
if level == 1 then
call UnitAddAbility(u, ABILIDZ)
call UnitAddAbility(u, ABILIDX)
call UnitAddAbility(u, ABILIDC)
else
call SetUnitAbilityLevel(u, ABILIDZ, level)
call SetUnitAbilityLevel(u, ABILIDX, level)
call SetUnitAbilityLevel(u, ABILIDC, level)
endif
endif
set u = null
return false
endfunction
private function OnInit takes nothing returns nothing
local trigger t = CreateTrigger()
local unit u = CreateUnit(Player(13), PRELOADID, 0, 0, 0)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function Actions))
set t = null
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(t, Condition(function Learn))
set t = null
//preload
call UnitAddAbility(u, ABILIDZ)
call UnitAddAbility(u, ABILIDX)
call UnitAddAbility(u, ABILIDC)
call RemoveUnit(CreateUnit(Player(13), DUMMYID, 0, 0, 0))
call RemoveUnit(u)
set u = null
endfunction
endscope
Necromastery
Description:
Whenever the Shadow Fiend kills a target, he stores the unfortunate soul inside of him. For each stored soul he gains 2 bonus damage until his own death releases half of them from bondage.
Level 1 - 16 damage limit.
Level 2 - 30 damage limit.
Level 3 - 46 damage limit.
Level 4 - 60 damage limit.
Passive
Note: The souls (black projectile) must reach the hero in before providing its bonus damage.
Screenshot:

Code:
JASS:
scope Necromastery initializer SetUp // requires Timer32
globals
private integer array DAMAGEID[4]
endglobals
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 'ABNe' // raw code of ability "Necromastery"
public constant integer DUMABILID = 'ANe0' // raw code of ability "Necromastery (Display)"
private constant integer DUMMYID = 'nECD' // raw code of unit "Necromastery Dummy"
private constant integer PRELOADID = 'prel' // raw code of unit "Preloader"
private constant integer DAMAGE = 2 // damage bonus per soul
private constant real COL = 50.0 // collision size of the soul
private constant real SPEED = 500.0 // distance traveled by the soul per second
private constant real TRUESPEED = SPEED * T32_PERIOD // distance traveled by the soul per period
private constant string DEATHART = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl" // death art used upon soul expiration
private constant string DEATHARTPOINT = "chest" // attachment point of DEATHART
endglobals
private function SetUp takes nothing returns nothing
local integer i = 0
local unit u = CreateUnit(Player(13), PRELOADID, 0, 0, 0)
set DAMAGEID[0] = 'dmg0' // raw code of ability "Damage (10000's) (Necromastery)"
set DAMAGEID[1] = 'dmg1' // raw code of ability "Damage (1000's) (Necromastery)"
set DAMAGEID[2] = 'dmg2' // raw code of ability "Damage (100's) (Necromastery)"
set DAMAGEID[3] = 'dmg3' // raw code of ability "Damage (10's) (Necromastery)"
set DAMAGEID[4] = 'dmg4' // raw code of ability "Damage (1's) (Necromastery)"
//preload
loop
exitwhen i > 4
call UnitAddAbility(u, DAMAGEID<i>)
set i = i + 1
endloop
call UnitAddAbility(u, DUMABILID)
call RemoveUnit(CreateUnit(Player(13), DUMMYID, 0, 0, 0))
call RemoveUnit(u)
set u = null
endfunction
private function GetMaxSoul takes integer level returns integer // maximum number of souls
if level == 1 then
return 8
elseif level == 2 then
return 15
elseif level == 3 then
return 23
elseif level == 4 then
return 30
endif
return 0
endfunction
private function GetSoulLost takes integer level returns real
return 0.50 // amount of souls lost on death, where 1.0 = 100%
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
private struct Data
unit killer
unit dummy
private static method setDamage takes unit u returns nothing
local integer damage = DAMAGE * (GetUnitAbilityLevel(u, DUMABILID) - 1)
local integer factor = 10000
local integer level
local integer i = 0
loop
exitwhen i > 4
set level = damage / factor
call SetUnitAbilityLevel(u, DAMAGEID<i>, level + 1)
set damage = damage - (level * factor)
set factor = factor / 10
set i = i + 1
endloop
endmethod
private method periodic takes nothing returns nothing
local integer level
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
local real dx = GetUnitX(this.killer) - x
local real dy = GetUnitY(this.killer) - y
local real a
local real height
local real dist = SquareRoot(dx * dx + dy * dy)
if dist <= 50.0 then
set level = GetUnitAbilityLevel(this.killer, DUMABILID)
if level < GetMaxSoul(GetUnitAbilityLevel(this.killer, ABILID)) + 1 then
call UnitRemoveAbility(this.killer, DUMABILID)
call UnitAddAbility(this.killer, DUMABILID)
call SetUnitAbilityLevel(this.killer, DUMABILID, level + 1)
call setDamage(this.killer)
endif
call DestroyEffect(AddSpecialEffectTarget(DEATHART, this.killer, DEATHARTPOINT))
call RemoveUnit(this.dummy)
call this.stopPeriodic()
call this.deallocate()
else
set a = Atan2(dy, dx)
call SetUnitX(this.dummy, x + TRUESPEED * Cos(a))
call SetUnitY(this.dummy, y + TRUESPEED * Sin(a))
set height = GetUnitFlyHeight(this.dummy)
call SetUnitFlyHeight(this.dummy, height + ((GetUnitFlyHeight(this.killer) + 100) - height) / (dist / TRUESPEED), 0)
endif
endmethod
implement T32x
private static method actions takes nothing returns boolean
local thistype this
local unit u = GetTriggerUnit()
local unit killer = GetKillingUnit()
local integer ulevel = GetUnitAbilityLevel(u, ABILID)
local integer klevel = GetUnitAbilityLevel(killer, ABILID)
local integer i
local real x
local real y
if ulevel > 0 then
set i = R2I((GetUnitAbilityLevel(u, DUMABILID) - 1) * GetSoulLost(ulevel))
call UnitRemoveAbility(u, DUMABILID)
call UnitAddAbility(u, DUMABILID)
call SetUnitAbilityLevel(u, DUMABILID, i + 1)
call thistype.setDamage(u)
elseif klevel > 0 then
set this = this.allocate()
set x = GetUnitX(u)
set y = GetUnitY(u)
set this.killer = killer
set this.dummy = CreateUnit(Player(13), DUMMYID, x, y, Atan2(GetUnitY(this.killer) - y, GetUnitX(this.killer) - x) * bj_RADTODEG)
call this.startPeriodic()
endif
set u = null
set killer = null
return false
endmethod
private static method learn takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer i
if GetLearnedSkill() == ABILID and GetUnitAbilityLevel(u, DUMABILID) == 0 then
call UnitAddAbility(u, DUMABILID)
set i = 0
loop
exitwhen i > 4
call UnitAddAbility(u, DAMAGEID<i>)
set i = i + 1
endloop
endif
set u = null
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Condition(function thistype.actions))
set t = null
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(t, Condition(function thistype.learn))
set t = null
endmethod
endstruct
endscope
</i></i></i>
Presence of the Dark Lord
Description:
The presence of such a horrible creature terrifies nearby enemies, reducing their armor.
Level 1 - 2 armor reduction.
Level 2 - 3 armor reduction.
Level 3 - 4 armor reduction.
Level 4 - 5 armor reduction.
Passive
Screenshot:

Requiem of Souls
Description:
Summons evil spirits around you dealing damage to units in the area. Number of spirits is related to the number of souls stored and the movement/damage reduction is related to the distance from the Shadow Fiend. Lowers movement speed and damage of nearby units. The closer the units are the greater the effect. Reduction lasts 5 seconds.
Level 1 - 80 damage for each line, 15% reduction.
Level 2 - 120 damage for each line, 20% reduction.
Level 3 - 160 damage for each line, 25% reduction.
Cast Range: Self
Target Type: Instant
Cast Time: 1 second
Cooldown: 120/110/100 seconds
Note: I believe DoTA actually uses multiple carrion swarms for Requiem of Souls. However, for configuration purposes and better visual effects, I did not.
Screenshot:

Code:
JASS:
scope RequiemOfSouls // requires Timer32, GroupUtils
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 'ABRS' // raw code of ability "Requiem of Souls"
private constant integer DUMABILID = 'ARS0' // raw code of ability "Requiem of Souls (Reduction)"
private constant integer DUMMYID = 'rOSD' // raw code of unit "Requiem of Souls Dummy"
private constant integer CASTERID = 'cAST' // raw code of unit "Caster Dummy"
private constant integer SOULCOUNT = 2 // number of souls required to create a "line"
private constant real SOULAOE = 150.0 // area of effect of each "line"
private constant real SPEED = 1000.0 // distance travelled by "line" per second
private constant real TRUESPEED = SPEED * T32_PERIOD // distance travelled by "line" per period
private constant real REDUCTIONAOE = 675.0 // area of effect where units will be affected by damage and move speed reduction
private constant real INTERVAL = 200.0 // distance interval where effect is spawned
private constant string EFFECT = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl" // effect spawned every interval
private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage
endglobals
private function GetDamage takes integer level returns real
return 40.0 + (40.0 * level) // damage dealt per line of soul
endfunction
private function GetArea takes integer level returns real
return 1350.0 + (25.0 * level) // area of effect
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
native UnitAlive takes unit id returns boolean
private struct Data
unit caster
unit dummy
group damagedgroup
real damage
real dist
real angle
real interval = INTERVAL
static thistype tempData
static unit tempUnit
private static method reductionFilter takes nothing returns boolean
local unit u = GetFilterUnit()
if IsUnitEnemy(u, GetOwningPlayer(tempUnit)) and UnitAlive(u) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
call IssueTargetOrder(tempUnit, "cripple", u)
endif
set u = null
return false
endmethod
private static method filterFunc takes nothing returns boolean
local thistype this = tempData
local unit u = GetFilterUnit()
if IsUnitEnemy(u, GetOwningPlayer(this.caster)) and UnitAlive(u) and not IsUnitInGroup(u, this.damagedgroup) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
call UnitDamageTarget(this.caster, u, this.damage, true, false, ATK, DMG, WEP)
call GroupAddUnit(this.damagedgroup, u)
endif
set u = null
return false
endmethod
private method periodic takes nothing returns nothing
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
if this.dist <= 0 then
call DestroyEffect(AddSpecialEffect(EFFECT, x, y))
call RemoveUnit(this.dummy)
call ReleaseGroup(this.damagedgroup)
call this.stopPeriodic()
call this.deallocate()
else
if this.interval <= 0 then
call DestroyEffect(AddSpecialEffect(EFFECT, x, y))
set this.interval = INTERVAL
else
set this.interval = this.interval - TRUESPEED
endif
call SetUnitPosition(this.dummy, x + TRUESPEED * Cos(this.angle), y + TRUESPEED * Sin(this.angle))
set tempData = this
call GroupEnumUnitsInArea(ENUM_GROUP, x, y, SOULAOE, Filter(function thistype.filterFunc))
set this.dist = this.dist - TRUESPEED
endif
endmethod
implement T32x
private static method actions takes nothing returns boolean
local thistype this
local unit u
local integer level
local integer i
local real angle
local real anglerate
local real x
local real y
if GetSpellAbilityId() == ABILID then
set u = GetTriggerUnit()
set level = GetUnitAbilityLevel(u, ABILID)
set i = (GetUnitAbilityLevel(u, Necromastery_DUMABILID) - 1) / SOULCOUNT
set angle = 0
if i != 0 then
set anglerate = (2 * bj_PI) / i
endif
set x = GetUnitX(u)
set y = GetUnitY(u)
loop
exitwhen i == 0
set this = thistype.allocate()
set this.caster = u
set this.damagedgroup = NewGroup()
set this.damage = GetDamage(level)
set this.dist = GetArea(level)
set this.angle = angle
set this.dummy = CreateUnit(GetOwningPlayer(this.caster), DUMMYID, x, y, angle)
call this.startPeriodic()
set angle = angle + anglerate
set i = i - 1
endloop
set tempUnit = CreateUnit(GetOwningPlayer(u), CASTERID, x, y, 0)
call UnitAddAbility(tempUnit, DUMABILID)
call SetUnitAbilityLevel(tempUnit, DUMABILID, level)
call GroupEnumUnitsInArea(ENUM_GROUP, x, y, REDUCTIONAOE, Filter(function thistype.reductionFilter))
call RemoveUnit(tempUnit)
endif
set u = null
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.actions))
set t = null
endmethod
endstruct
endscope
Credits
Code:
Jesus4Lyf - [URL="http://www.thehelper.net/forums/showthread.php/132538-Timer32?highlight=Timer32"]Timer32[/URL]
Rising_Dusk - [URL="http://www.wc3c.net/showthread.php?t=104464"]GroupUtils[/URL]
Changelogs
Code:
Version 1.0
- Initial relase
Version 1.1
- Optimized code
- Made Attack Type, Weapon Type and Damage Type configurable for Shadowraze and Requiem of Souls
Version 1.2
- Rewrote codes using Timer32 and GroupUtils, instead of KT2 and Recycle
Attachments
-
66.4 KB Views: 697
-
68 KB Views: 1,049