Dinowc
don't expect anything, prepare for everything
- Reaction score
- 223
Morbent Splinterspine
it's been a long time since I've posted my last spellnow I've decided to post a whole spellpack
screenshots are useless, but since everybody wants them, they are here
Bone Spear
Releases a bone spear towards target location that pierces trough enemy units dealing physical damage and reduces their armor. Nearby Skeleton Warriors are also transformed into bone spears, dealing bonus damage. Armor reduction stacks up to 5 times.
Level 1 - Deals 60 damage (10% of skeleton's HP in bonus damage), -1 armor.
Level 2 - Deals 100 damage (15% of skeleton's HP in bonus damage), -2 armor.
Level 3 - Deals 140 damage (20% of skeleton's HP in bonus damage), -3 armor.
Level 4 - Deals 180 damage (25% of skeleton's HP in bonus damage), -4 armor.
Level 1 - Deals 60 damage (10% of skeleton's HP in bonus damage), -1 armor.
Level 2 - Deals 100 damage (15% of skeleton's HP in bonus damage), -2 armor.
Level 3 - Deals 140 damage (20% of skeleton's HP in bonus damage), -3 armor.
Level 4 - Deals 180 damage (25% of skeleton's HP in bonus damage), -4 armor.
JASS:
scope bonespear initializer init
//CONFIGURABLE:
globals
public constant integer SPELL_ID = 039;A002039;
private constant integer SPELL_UPGRADE_ID = 039;A00B039;
private constant integer SPEAR_ID = 039;h001039;
private constant integer PIERCE_DUMMY = 039;h003039;
private constant integer PIERCE_ID = 039;A007039;
private constant integer PIERCE_BUFF = 039;B001039; // btw pierce == armor reduction spell
private constant integer SKELETON1_ID = 039;u001039;
private constant integer SKELETON2_ID = 039;u002039;
private constant integer SKELETON3_ID = 039;u003039;
private constant integer SKELETON4_ID = 039;u004039; // if you want more levels, you'll have to edit the code a bit
private constant real INTERVAL = 0.03125
private constant real DAMAGE_BASE = 60.
private constant real DAMAGE_INCREMENT = 40.
private constant real DAMAGE_AOE = 75.
private constant real RANGE_BASE = 800.
private constant real RANGE_INCREMENT = 50.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_HERO
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant real SPEED = 40.
private constant real OFFSET = 50. // offset from unit where the spear is initialy created
private constant boolean DESTROY_TREES = true
private constant string DAMAGE_EFFECT = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
private constant boolean TRANSFORM_SKELETONS = true
private constant real DAMAGE_BONUS = 10. // percent of skeleton's HP added in damage
private constant real PERCENT_INCREMENT = 5.
private constant real SEARCH_AOE = 300. // AOE at which nearby skellies are detected
private constant boolean REDUCE_ARMOR = true // armor reductions are set in object editor
endglobals
//NOT SO MUCH CONFIGURABLE:
globals
private timer T = CreateTimer()
private group G = CreateGroup()
private group SKELETONS = CreateGroup()
private integer COUNT = 0
private real MaxX
private real MaxY
private real MinX
private real MinY
private player OWNER
private unit CASTER
private real ANGLE
private real DAMAGE
private group DAMAGED = CreateGroup()
private rect R
private unit DUMMY
private integer LVL
private integer array BUFF_LEVEL
public boolean array DIRECTION
private real X
private real Y
endglobals
private function Range takes integer lvl returns real
return RANGE_BASE + (lvl - 1) * RANGE_INCREMENT
endfunction
private function Damage takes integer lvl returns real
return DAMAGE_BASE + (lvl - 1) * DAMAGE_INCREMENT
endfunction
private function DamageBonus takes integer lvl returns real
return DAMAGE_BONUS + (lvl - 1) * PERCENT_INCREMENT
endfunction
private function SafeX takes real x returns real
if x > MaxX then
return MaxX
elseif x < MinX then
return MinX
endif
return x
endfunction
private function SafeY takes real y returns real
if y > MaxY then
return MaxY
elseif y < MinY then
return MinY
endif
return y
endfunction
private struct spear extends array
//! runtextmacro AIDS()
unit owner
real sin
real cos
real dist
real range
real dmgBonus
group g
integer spell
method AIDS_onCreate takes nothing returns nothing
set this.dist = OFFSET
set this.g = NewGroup()
set this.dmgBonus = 0.
endmethod
static method AIDS_filter takes unit u returns boolean
return GetUnitTypeId(u) == SPEAR_ID
endmethod
method AIDS_onDestroy takes nothing returns nothing
call ReleaseGroup(this.g)
set COUNT = COUNT - 1
if COUNT == 0 then
call PauseTimer(T)
endif
endmethod
endstruct
private function DamageFilter takes nothing returns boolean
local unit picked = GetFilterUnit()
local integer i
if IsUnitEnemy(picked, OWNER) and IsUnitType(picked, UNIT_TYPE_MAGIC_IMMUNE) == false and GetWidgetLife(picked) > 0. and IsUnitInGroup(picked, DAMAGED) == false then
call UnitDamageTarget(CASTER, picked, DAMAGE, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, picked, "chest"))
call GroupAddUnit(DAMAGED, picked)
static if REDUCE_ARMOR then
set i = GetUnitId(picked)
call SetUnitAbilityLevel(DUMMY, PIERCE_ID, LVL + (BUFF_LEVEL<i> * 4))
call SetUnitX(DUMMY, GetUnitX(picked))
call SetUnitY(DUMMY, GetUnitY(picked))
call IssueTargetOrder(DUMMY, "innerfire", picked)
if GetUnitAbilityLevel(picked, PIERCE_BUFF) > 0 then
if BUFF_LEVEL<i> < 4 then
set BUFF_LEVEL<i> = BUFF_LEVEL<i> + 1
endif
else
set BUFF_LEVEL<i> = 0
endif
endif
endif
set picked = null
return false
endfunction
private function filterTrees takes nothing returns boolean
local destructable d = GetFilterDestructable()
if GetDestructableLife(d) > 0. then
call KillDestructable(d)
endif
set d = null
return false
endfunction
private function MoveSkellies takes nothing returns nothing
local unit picked = GetEnumUnit()
local spear d = spear[picked]
local real x = SafeX(GetUnitX(picked) + d.cos)
local real y = SafeY(GetUnitY(picked) + d.sin)
call SetUnitX(picked, x)
call SetUnitY(picked, y)
if d.dist <= spear[picked].range then
set CASTER = d.owner
set OWNER = GetOwningPlayer(CASTER)
set LVL = GetUnitAbilityLevel(CASTER, d.spell)
set DAMAGE = Damage(LVL) + d.dmgBonus
set DAMAGED = d.g
static if REDUCE_ARMOR then
call SetUnitOwner(DUMMY, OWNER, false)
endif
call GroupEnumUnitsInRange(G, x, y, DAMAGE_AOE, Filter(function DamageFilter))
if DESTROY_TREES then
call MoveRectTo(R, x, y)
call EnumDestructablesInRect(R, Filter(function filterTrees), null)
endif
else
call KillUnit(picked)
call GroupRemoveUnit(SKELETONS, picked)
endif
set d.dist = d.dist + SPEED
set picked = null
endfunction
private function callback takes nothing returns nothing
call ForGroup(SKELETONS, function MoveSkellies)
endfunction
private function filterSkellies takes nothing returns boolean
local unit picked = GetFilterUnit()
local unit skeleton
local integer i = GetUnitTypeId(picked)
local spear d
if (i == SKELETON1_ID or i == SKELETON2_ID or i == SKELETON3_ID or i == SKELETON4_ID) and GetOwningPlayer(picked) == OWNER and GetWidgetLife(picked) > 0. then
set skeleton = CreateUnit(OWNER, SPEAR_ID, GetUnitX(picked), GetUnitY(picked), ANGLE)
call GroupAddUnit(SKELETONS, skeleton)
set d = spear[skeleton]
if DIRECTION[LVL] == false then
set d.cos = PolarX(SPEED, ANGLE)
set d.sin = PolarY(SPEED, ANGLE)
else
set ANGLE = GetAngle(GetUnitX(picked), GetUnitY(picked), X, Y)
set d.cos = PolarX(SPEED, ANGLE)
set d.sin = PolarY(SPEED, ANGLE)
endif
set d.owner = CASTER
set i = GetUnitAbilityLevel(CASTER, d.spell)
set d.range = Range(i)
set d.dmgBonus = GetUnitState(picked, UNIT_STATE_LIFE) * 0.01 * DamageBonus(i)
call KillUnit(picked)
set COUNT = COUNT + 1
endif
set skeleton = null
set picked = null
return false
endfunction
private function actions takes nothing returns boolean
local unit caster
local real angle
local real x
local real y
local unit skeleton
local integer i = GetSpellAbilityId()
local spear d
if i == SPELL_ID or i == SPELL_UPGRADE_ID then
set caster = GetTriggerUnit()
set x = GetUnitX(caster)
set y = GetUnitY(caster)
set X = GetSpellTargetX()
set Y = GetSpellTargetY()
set angle = GetAngle(x, y, X, Y)
set OWNER = GetOwningPlayer(caster)
set skeleton = CreateUnit(OWNER, SPEAR_ID, x + PolarX(OFFSET, angle), y + PolarY(OFFSET, angle), angle)
call GroupAddUnit(SKELETONS, skeleton)
set d = spear[skeleton]
set d.cos = PolarX(SPEED, angle)
set d.sin = PolarY(SPEED, angle)
set d.owner = caster
set d.range = Range(GetUnitAbilityLevel(caster, i))
set d.spell = i
if COUNT == 0 then
call TimerStart(T, INTERVAL, true, function callback)
endif
set COUNT = COUNT + 1
set ANGLE = angle
set CASTER = caster
set LVL = GetUnitId(caster)
static if TRANSFORM_SKELETONS then
call GroupEnumUnitsInRange(G, x, y, SEARCH_AOE, Filter(function filterSkellies))
endif
endif
set caster = null
set skeleton = null
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function actions))
set MaxX = GetRectMaxX(bj_mapInitialPlayableArea) - 50.
set MaxY = GetRectMaxY(bj_mapInitialPlayableArea) - 50.
set MinX = GetRectMinX(bj_mapInitialPlayableArea) + 50.
set MinY = GetRectMinY(bj_mapInitialPlayableArea) + 50.
set R = Rect(-DAMAGE_AOE, -DAMAGE_AOE, DAMAGE_AOE, DAMAGE_AOE)
set DUMMY = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), PIERCE_DUMMY, 0., 0., 0.)
endfunction
endscope
</i></i></i></i></i>
Necromancy
Steals souls of nearby dying non-hero units, healing Necro Walker and storing the soul. He can release stored souls upon group of corpses, raising Skeleton Warriors from them. Souls deal damage as they fly trough enemy units. Necro Walker loses a soul every 30 seconds.
Level 1 - Heals by 5% of dying unit's max HP, souls deal 60 damage, can store up to 4 souls, summons lesser skeletons.
Level 2 - Heals by 7% of dying unit's max HP, souls deal 90 damage, can store up to 6 souls, summons skeletons.
Level 3 - Heals by 9% of dying unit's max HP, souls deal 120 damage, can store up to 8 souls, summons greater skeletons.
Level 4 - Heals by 11% of dying unit's max HP, souls deal 150 damage, can store up to 10 souls, summons elite skeletons with critical strike.
Level 1 - Heals by 5% of dying unit's max HP, souls deal 60 damage, can store up to 4 souls, summons lesser skeletons.
Level 2 - Heals by 7% of dying unit's max HP, souls deal 90 damage, can store up to 6 souls, summons skeletons.
Level 3 - Heals by 9% of dying unit's max HP, souls deal 120 damage, can store up to 8 souls, summons greater skeletons.
Level 4 - Heals by 11% of dying unit's max HP, souls deal 150 damage, can store up to 10 souls, summons elite skeletons with critical strike.
JASS:
scope necromancy initializer init
//CONFIGURABLE:
globals
private constant integer SPELL_ID = 039;A000039;
private constant integer SOUL_ID = 039;h000039;
private constant integer RAISE_DEAD = 039;A001039;
private constant real INTERVAL = 0.03125
private constant real MOVESPEED = 15.
private constant real SEARCH_AOE = 1000. // AOE at which is a nearby hero detected; closest hero with this ability learned will steal the soul
private constant integer MAX_SOULS_BASE = 4
private constant integer MAX_SOULS_INCREMENT = 2
private constant string SFX = "war3mapImported\\InvisibilityTarget.mdx"
private constant string SFX_ATTACH = "hand right"
private constant string SOUL_INDICATION = "war3mapImported\\Fire.mdx" // indicates when you have stored max amount of souls
private constant string INDICATION_ATTACH = "hand right"
private constant real HEAL_PERCENT_BASE = 3.
private constant real HEAL_PERCENT_INCREMENT = 1.
private constant boolean RAW_HEAL = false // setting this to true will heal the unit by a specified raw value instead
private constant real RAW_HEAL_BASE = 60.
private constant real RAW_HEAL_INCREMENT = 30.
private constant real DAMAGE_BASE = 60.
private constant real DAMAGE_INCREMENT = 30.
private constant real DAMAGE_AOE = 100.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
private constant real AOE_BASE = 400.
private constant real AOE_INCREMENT = 50. // these values should obviously be the same as they are in object editor
endglobals
//NOT SO MUCH CONFIGURABLE:
globals
private unit HERO
private unit SOUL
private integer COUNT
private boolean IS_DEAD
private real DISTANCE = SEARCH_AOE + 100.
private real X1
private real Y1
private real X2
private real Y2
private group G = CreateGroup()
private group G2 = CreateGroup()
endglobals
private function HealPercent takes integer lvl returns real
return HEAL_PERCENT_BASE + (lvl - 1) * HEAL_PERCENT_INCREMENT
endfunction
private function HealRaw takes integer lvl returns real
return RAW_HEAL_BASE + (lvl - 1) * RAW_HEAL_INCREMENT
endfunction
private function MaxSouls takes integer lvl returns integer
return MAX_SOULS_BASE + (lvl - 1) * MAX_SOULS_INCREMENT
endfunction
private function AoE takes integer lvl returns real
return AOE_BASE + (lvl - 1) * AOE_INCREMENT
endfunction
//===========================================================
private struct dead extends array
//! runtextmacro AIDS()
boolean IsDead
method AIDS_onCreate takes nothing returns nothing
set this.IsDead = false
endmethod
static method AIDS_filter takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_HERO) == false and IsUnitType(u, UNIT_TYPE_SUMMONED) == false and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) == false
endmethod
endstruct
private struct data extends array
//! runtextmacro AIDS()
group g
timer t
integer count
effect sfx
method AIDS_onCreate takes nothing returns nothing
set this.g = NewGroup()
set this.t = NewTimer()
call SetTimerData(this.t, this)
set this.count = 0
endmethod
method AIDS_onDestroy takes nothing returns nothing
call ReleaseTimer(this.t)
call ReleaseGroup(this.g)
endmethod
static method AIDS_filter takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_HERO)
endmethod
endstruct
private struct soul extends array
//! runtextmacro AIDS()
unit target
group g
boolean b
real heal
method AIDS_onCreate takes nothing returns nothing
set this.g = NewGroup()
set this.b = true
set this.heal = 0.
endmethod
method AIDS_onDestroy takes nothing returns nothing
call ReleaseGroup(this.g)
endmethod
static method AIDS_filter takes unit u returns boolean
return GetUnitTypeId(u) == SOUL_ID
endmethod
endstruct
//===========================================================
private function DamageAmount takes integer lvl returns real
return DAMAGE_BASE + (lvl - 1) * DAMAGE_INCREMENT
endfunction
private function filterDamage takes nothing returns boolean
local unit picked = GetFilterUnit()
if IsUnitEnemy(picked, GetOwningPlayer(HERO)) and IsUnitInGroup(picked, soul[SOUL].g) == false and IsUnitType(picked, UNIT_TYPE_MAGIC_IMMUNE) == false and GetWidgetLife(picked) > 0. then
call UnitDamageTarget(SOUL, picked, DamageAmount(GetUnitAbilityLevel(HERO, SPELL_ID)), false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS)
call GroupAddUnit(soul[SOUL].g, picked)
endif
set picked = null
return false
endfunction
private function MoveSouls takes nothing returns nothing
local unit picked = GetEnumUnit()
local real x = GetUnitX(picked)
local real y = GetUnitY(picked)
local real angle = GetAngle(x, y, X1, Y1)
local real heal = GetUnitState(HERO, UNIT_STATE_LIFE)
local integer lvl = GetUnitAbilityLevel(HERO, SPELL_ID)
local data d
local soul s = soul[picked]
if s.b == true then
call SetUnitX(picked, x + PolarX(MOVESPEED, angle))
call SetUnitY(picked, y + PolarY(MOVESPEED, angle))
call SetUnitFacingTimed(picked, angle, INTERVAL)
set d = data[HERO]
set DISTANCE = GetDistance(x, y, X1, Y1)
if d.count < MaxSouls(lvl) then
if DISTANCE < MOVESPEED + 5. and IsUnitHidden(picked) == false then
static if RAW_HEAL then
set heal = heal + HealRaw(lvl)
else
set heal = heal + s.heal
endif
call SetUnitState(HERO, UNIT_STATE_LIFE, heal)
call GroupRemoveUnit(data[HERO].g, picked)
call KillUnit(picked)
set d.count = d.count + 1
if d.count == MaxSouls(lvl) and d.sfx == null then
set d.sfx = AddSpecialEffectTarget(SOUL_INDICATION, HERO, INDICATION_ATTACH)
endif
call DestroyEffect(AddSpecialEffectTarget(SFX, HERO, SFX_ATTACH))
endif
else
call GroupRemoveUnit(d.g, picked)
call KillUnit(picked)
endif
endif
set SOUL = picked
call GroupEnumUnitsInRange(G, x, y, DAMAGE_AOE, Filter(function filterDamage))
set picked = null
endfunction
//=================================================
private function callback1 takes nothing returns nothing
local timer t = GetExpiredTimer()
local data d = GetTimerData(t)
set X1 = GetUnitX(d.unit)
set Y1 = GetUnitY(d.unit)
set HERO = d.unit
call ForGroup(d.g, function MoveSouls)
set HERO = null
set t = null
endfunction
private function filterFunc takes nothing returns boolean
local unit picked = GetFilterUnit()
local real dist
if IsUnitType(picked, UNIT_TYPE_HERO) and GetUnitAbilityLevel(picked, SPELL_ID) > 0 then
set X2 = GetUnitX(picked)
set Y2 = GetUnitY(picked)
set dist = GetDistance(X1, Y1, X2, Y2)
if dist < DISTANCE then
set DISTANCE = dist
set HERO = picked
endif
endif
set picked = null
return false
endfunction
private function actions2 takes nothing returns boolean
local unit dying = GetTriggerUnit()
local unit SOUL
local soul s
local data d
if IsUnitType(dying, UNIT_TYPE_SUMMONED) == false and IsUnitType(dying, UNIT_TYPE_HERO) == false and IsUnitType(dying, UNIT_TYPE_STRUCTURE) == false then
set X1 = GetUnitX(dying)
set Y1 = GetUnitY(dying)
set dead[dying].IsDead = true
call GroupEnumUnitsInRange(G, X1, Y1, SEARCH_AOE, Filter(function filterFunc))
set d = data[HERO]
if HERO != null and d.count < MaxSouls(GetUnitAbilityLevel(HERO, SPELL_ID)) then
set SOUL = CreateUnit(GetOwningPlayer(HERO), SOUL_ID, X1, Y1, GetAngle(X1, Y1, X2, Y2))
set s = soul[SOUL]
set s.target = HERO
if RAW_HEAL == false then
set s.heal = GetUnitState(dying, UNIT_STATE_MAX_LIFE) * 0.01 * HealPercent(GetUnitAbilityLevel(HERO, SPELL_ID))
endif
call GroupAddUnit(d.g, SOUL)
if d.count == 0 then
call TimerStart(d.t, INTERVAL, true, function callback1)
endif
endif
set DISTANCE = SEARCH_AOE + 100.
endif
set SOUL = null
set dying = null
return false
endfunction
//=================================================
private function RaiseDead takes nothing returns boolean
local unit picked = GetFilterUnit()
local unit SOUL
local soul s
local data d = data[HERO]
if GetWidgetLife(picked) <= 0. and d.count > 0 and IsUnitInGroup(picked, G2) == false and IsUnitType(picked, UNIT_TYPE_SUMMONED) == false and IsUnitType(picked, UNIT_TYPE_MAGIC_IMMUNE) == false and dead[picked].IsDead == true then
set SOUL = CreateUnit(GetOwningPlayer(HERO), SOUL_ID, GetUnitX(HERO), GetUnitY(HERO), GetUnitFacing(HERO))
call SetUnitAbilityLevel(SOUL, RAISE_DEAD, GetUnitAbilityLevel(HERO, SPELL_ID))
call GroupAddUnit(d.g, SOUL)
set s = soul[SOUL]
set s.b = false
set s.target = HERO
call IssueTargetOrder(SOUL, "raisedead", picked)
call GroupAddUnit(G2, picked)
set d.count = data[HERO].count - 1
if d.count <= 0 and d.sfx != null then
call DestroyEffect(d.sfx)
set d.sfx = null
endif
endif
set picked = null
set SOUL = null
return false
endfunction
private function actions1 takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
set HERO = GetTriggerUnit()
call GroupEnumUnitsInRange(G, GetSpellTargetX(), GetSpellTargetY(), AoE(GetUnitAbilityLevel(HERO, SPELL_ID)), Filter(function RaiseDead))
set HERO = null
endif
return false
endfunction
//=================================================
private function actions3 takes nothing returns boolean
local data d
local unit caster = GetTriggerUnit()
if GetUnitTypeId(caster) == SOUL_ID then
set d = data[soul[caster].target]
call GroupRemoveUnit(d.g, caster)
call GroupRemoveUnit(G2, GetSpellTargetUnit())
call KillUnit(caster)
if d.count == 0 and CountUnitsInGroup(d.g) == 0 then
call PauseTimer(d.t)
endif
endif
set caster = null
return false
endfunction
//=================================================
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function actions1))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DECAY)
call TriggerAddCondition(t, Condition(function actions2))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_FINISH)
call TriggerAddCondition(t, Condition(function actions3))
endfunction
endscope
Confusion
Curses group of enemy units causing them to attack random targets.
Level 1 - Duration 2 seconds, 300 AOE, 550 Cast Range.
Level 2 - Duration 2.75 seconds, 350 AOE, 600 Cast Range.
Level 3 - Duration 3.5 seconds, 400 AOE, 650 Cast Range.
Level 4 - Duration 4.25 seconds, 450 AOE, 700 Cast Range.
Level 1 - Duration 2 seconds, 300 AOE, 550 Cast Range.
Level 2 - Duration 2.75 seconds, 350 AOE, 600 Cast Range.
Level 3 - Duration 3.5 seconds, 400 AOE, 650 Cast Range.
Level 4 - Duration 4.25 seconds, 450 AOE, 700 Cast Range.
JASS:
scope confusion initializer init
//CONFIGURABLE:
globals
private constant integer SPELL_ID = 039;A003039;
private constant integer CURSE_DUMMY = 039;h002039;
private constant integer CURSE_SPELL = 039;A004039;
private constant integer CURSE_BUFF = 039;B000039;
private constant real INTERVAL = 0.1
private constant real AOE_BASE = 300.
private constant real AOE_INCREMENT = 50.
private constant real SEARCH_AOE = 500.
//Note: Duration depends on CURSE_SPELL duration; change it in object editor
endglobals
//NOT SO MUCH CONFIGURABLE:
globals
private group G1 = CreateGroup()
private group G2 = CreateGroup()
private group CURSED = CreateGroup()
private timer T = CreateTimer()
private unit DUMMY
private integer COUNT = 0
private unit array TARGET
private integer ID
private player OWNER
private unit U
endglobals
private function AoE takes integer lvl returns real
return AOE_BASE + (lvl - 1) * AOE_INCREMENT
endfunction
private function SearchTargets takes nothing returns boolean
local unit picked = GetFilterUnit()
if picked != U and GetWidgetLife(picked) > 0. then
call GroupAddUnit(G2, picked)
endif
set picked = null
return false
endfunction
private function ForceAttack takes nothing returns nothing
local unit picked = GetEnumUnit()
set ID = GetUnitId(picked)
if TARGET[ID] == null or GetWidgetLife(TARGET[ID]) == 0 then
set U = picked
call GroupEnumUnitsInRange(G1, GetUnitX(picked), GetUnitY(picked), SEARCH_AOE, Filter(function SearchTargets))
set TARGET[ID] = GroupPickRandomUnit(G2)
call GroupClear(G2)
else
call ClearSelectionForPlayer(GetOwningPlayer(picked))
call IssueTargetOrder(picked, "attack", TARGET[ID])
endif
if GetUnitAbilityLevel(picked, CURSE_BUFF) == 0 then
call IssueImmediateOrder(picked, "stop")
call GroupRemoveUnit(CURSED, picked)
set COUNT = COUNT - 1
endif
set picked = null
endfunction
private function callback takes nothing returns nothing
call ForGroup(CURSED, function ForceAttack)
if COUNT == 0 then
call PauseTimer(T)
endif
endfunction
private function filterFunc takes nothing returns boolean
local unit picked = GetFilterUnit()
if IsUnitEnemy(picked, OWNER) and GetWidgetLife(picked) > 0. and IsUnitType(picked, UNIT_TYPE_MAGIC_IMMUNE) == false then
call SetUnitX(DUMMY, GetUnitX(picked))
call SetUnitY(DUMMY, GetUnitY(picked))
call IssueTargetOrder(DUMMY, "curse", picked)
call GroupAddUnit(CURSED, picked)
set COUNT = COUNT + 1
endif
set picked = null
return false
endfunction
private function actions takes nothing returns boolean
local unit caster
local integer lvl
if GetSpellAbilityId() == SPELL_ID then
set caster = GetTriggerUnit()
set lvl = GetUnitAbilityLevel(caster, SPELL_ID)
set OWNER = GetOwningPlayer(caster)
call SetUnitOwner(DUMMY, OWNER, false)
call SetUnitAbilityLevel(DUMMY, CURSE_SPELL, lvl)
if COUNT == 0 then
call TimerStart(T, INTERVAL, true, function callback)
endif
call GroupEnumUnitsInRange(G1, GetSpellTargetX(), GetSpellTargetY(), AoE(lvl), Filter(function filterFunc))
endif
set caster = null
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function actions))
set DUMMY = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), CURSE_DUMMY, 0., 0., 0.)
endfunction
endscope
Living Wall
Raises a living wall of zombies that block the path and attack enemy units that stray too close.
Level 1 - Zombies have 500 HP, deal 30 damage and last for 6 seconds.
Level 2 - Zombies have 700 HP, deal 40 damage and last for 7 seconds.
Level 3 - Zombies have 900 HP, deal 50 damage and last for 8 seconds.
Level 4 - Zombies have 1100 HP, deal 60 damage and last for 9 seconds.
Level 1 - Zombies have 500 HP, deal 30 damage and last for 6 seconds.
Level 2 - Zombies have 700 HP, deal 40 damage and last for 7 seconds.
Level 3 - Zombies have 900 HP, deal 50 damage and last for 8 seconds.
Level 4 - Zombies have 1100 HP, deal 60 damage and last for 9 seconds.
JASS:
scope wall initializer init
//CONFIGURABLE:
globals
private constant integer SPELL_ID = 039;A006039;
private constant integer ZOMBIE1_ID = 039;n000039;
private constant integer ZOMBIE2_ID = 039;n001039;
private constant integer ZOMBIE3_ID = 039;n002039;
private constant integer ZOMBIE4_ID = 039;n003039;
private constant real DURATION_BASE = 6.
private constant real DURATION_INCREMENT = 1.
private constant integer EXPIRATION_TYPE = 039;BTLF039;
private constant real ZOMBIE_COLLISION_SIZE = 15.
private constant integer ZOMBIE_COUNT = 8
private constant string RAISE_EFFECT = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
endglobals
//NOT SO MUCH CONFIGURABLE:
globals
private real SIZE = ZOMBIE_COLLISION_SIZE * I2R(ZOMBIE_COUNT) * 2
private real DIST = ZOMBIE_COLLISION_SIZE * 2
private integer array ZOMBIE
private group G = CreateGroup()
private timer T = CreateTimer()
endglobals
private function duration takes integer lvl returns real
return DURATION_BASE + lvl * DURATION_INCREMENT
endfunction
private struct data
group g
timer t
method onDestroy takes nothing returns nothing
call ReleaseGroup(this.g)
call ReleaseTimer(this.t)
endmethod
endstruct
private function KillZombies takes nothing returns nothing
call KillUnit(GetEnumUnit())
endfunction
private function callback takes nothing returns nothing
local data d = GetTimerData(GetExpiredTimer())
call ForGroup(d.g, function KillZombies)
call d.destroy()
endfunction
private function actions takes nothing returns boolean
local real dist = 0.
local real angle
local real x1
local real y1
local real x2
local real y2
local real cos
local real sin
local unit caster
local integer lvl
local data d
local unit zombie
local player p
if GetSpellAbilityId() == SPELL_ID then
set d = data.create()
set caster = GetTriggerUnit()
set p = GetOwningPlayer(caster)
set lvl = GetUnitAbilityLevel(caster, SPELL_ID) - 1
set x1 = GetSpellTargetX()
set y1 = GetSpellTargetY()
set angle = GetAngle(GetUnitX(caster), GetUnitY(caster), x1, y1)
set x2 = x1 + PolarX(SIZE, angle + 90.)
set y2 = y1 + PolarY(SIZE, angle + 90.)
set cos = PolarX(DIST * 2, angle - 90.)
set sin = PolarY(DIST * 2, angle - 90.)
set d.t = NewTimer()
set d.g = NewGroup()
loop
call DestroyEffect(AddSpecialEffect(RAISE_EFFECT, x2, y2))
set zombie = CreateUnit(p, ZOMBIE[lvl], x2, y2, angle)
call SetUnitAnimation(zombie, "birth")
call QueueUnitAnimation(zombie, "stand")
call IssueImmediateOrder(zombie, "holdposition")
call GroupAddUnit(d.g, zombie)
set x2 = x2 + cos
set y2 = y2 + sin
set dist = dist + DIST
exitwhen dist > SIZE
endloop
call SetTimerData(d.t, d)
call TimerStart(d.t, duration(lvl), false, function callback)
endif
set zombie = null
set caster = null
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function actions))
set ZOMBIE[0] = ZOMBIE1_ID
set ZOMBIE[1] = ZOMBIE2_ID
set ZOMBIE[2] = ZOMBIE3_ID
set ZOMBIE[3] = ZOMBIE4_ID
endfunction
endscope
Unholy Frenzy
Enrages every nearby allied unit increasing movement and attack speed for 15 seconds. While Unholy Frenzy is active, Bone Spear cooldown is removed and any skeleton transformed spear will shoot at the target point instead.
Level 1 - Increases MS by 20% and AS by 50%.
Level 2 - Increases MS by 25% and AS by 75%.
Level 3 - Increases MS by 30% and AS by 100%.
Level 1 - Increases MS by 20% and AS by 50%.
Level 2 - Increases MS by 25% and AS by 75%.
Level 3 - Increases MS by 30% and AS by 100%.
JASS:
scope frenzy initializer init
//CONFIGURABLES
globals
private constant integer SPELL_ID = 039;A008039;
private constant integer SPEAR_UPGRADE_ID = 039;A00A039;
private constant integer BLOODLUST_DUMMY = 039;h004039;
private constant integer BLOODLUST_ID = 039;A009039;
private constant integer BLOODLUST_BUFF = 039;B002039;
private constant real INTERVAL = 0.1
private constant real AOE_BASE = 800.
private constant real AOE_INCREMENT = 100.
endglobals
//NOT SO MUCH CONFIGURABLE:
globals
private unit DUMMY
private timer T = CreateTimer()
private group G = CreateGroup()
private group G2 = CreateGroup()
private player P
private integer COUNT = 0
endglobals
private function AoE takes integer lvl returns real
return AOE_BASE + (lvl - 1) * AOE_INCREMENT
endfunction
private function CheckUnits takes nothing returns nothing
local unit picked = GetEnumUnit()
if GetUnitAbilityLevel(picked, BLOODLUST_BUFF) == 0 then
call UnitRemoveAbility(picked, SPEAR_UPGRADE_ID)
set bonespear_DIRECTION[GetUnitId(picked)] = false
call GroupRemoveUnit(G2, picked)
set COUNT = COUNT - 1
endif
set picked = null
endfunction
private function callback takes nothing returns nothing
call ForGroup(G2, function CheckUnits)
if COUNT == 0 then
call PauseTimer(GetExpiredTimer())
endif
endfunction
private function filterFunc takes nothing returns boolean
local unit picked = GetFilterUnit()
if IsUnitAlly(picked, P) and GetWidgetLife(picked) > 0. and IsUnitType(picked, UNIT_TYPE_MAGIC_IMMUNE) == false and picked != DUMMY then
call SetUnitX(DUMMY, GetUnitX(picked))
call SetUnitY(DUMMY, GetUnitY(picked))
call IssueTargetOrder(DUMMY, "bloodlust", picked)
endif
set picked = null
return false
endfunction
private function actions takes nothing returns boolean
local unit caster
local integer lvl
if GetSpellAbilityId() == SPELL_ID then
set caster = GetTriggerUnit()
set P = GetOwningPlayer(caster)
set lvl = GetUnitAbilityLevel(caster, SPELL_ID)
call SetUnitOwner(DUMMY, P, false)
call SetUnitAbilityLevel(DUMMY, BLOODLUST_ID, lvl)
call UnitAddAbility(caster, SPEAR_UPGRADE_ID)
call GroupEnumUnitsInRange(G, GetUnitX(caster), GetUnitY(caster), AoE(lvl), Filter(function filterFunc))
set bonespear_DIRECTION[GetUnitId(caster)] = true
call GroupAddUnit(G2, caster)
if COUNT == 0 then
call TimerStart(T, INTERVAL, true, function callback)
endif
set COUNT = COUNT + 1
endif
set caster = null
return false
endfunction
private function actions2 takes nothing returns boolean
local unit caster
if GetLearnedSkill() == bonespear_SPELL_ID then
set caster = GetTriggerUnit()
if GetUnitAbilityLevel(caster, bonespear_SPELL_ID) == 1 then
set bonespear_DIRECTION[GetUnitId(caster)] = false
endif
endif
set caster = null
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function actions))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_HERO_SKILL )
call TriggerAddCondition(t, Condition(function actions2))
set DUMMY = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), BLOODLUST_DUMMY, 0., 0., 0.)
endfunction
endscope
Changelog:
v1.0
-initial release
v1.1
code and tooltip fixes
Necromancy
-souls heal depending on dying unit's max HP; added a visual indication whenever max souls are stored
v1.2
code and tooltip fixes
Necromancy:
-instead of killing and creating the souls all over again, consumed souls are hidden instead; souls deal damage only when released; hero loses a soul every 30 seconds (this is optional and can be changed in CONIFGURABLE globals block)
Non-code related fixes/changes:
-fixed a bug where you would receive +2 dmg bonus when casting Unholy Frenzy
-buffed Unholy Frenzy MS bonus
-changed soul release sound
-reduced Necro Walker's cast backswing (for faster casting)
-initial release
v1.1
code and tooltip fixes
Necromancy
-souls heal depending on dying unit's max HP; added a visual indication whenever max souls are stored
v1.2
code and tooltip fixes
Necromancy:
-instead of killing and creating the souls all over again, consumed souls are hidden instead; souls deal damage only when released; hero loses a soul every 30 seconds (this is optional and can be changed in CONIFGURABLE globals block)
Non-code related fixes/changes:
-fixed a bug where you would receive +2 dmg bonus when casting Unholy Frenzy
-buffed Unholy Frenzy MS bonus
-changed soul release sound
-reduced Necro Walker's cast backswing (for faster casting)
AIDS, TimerUtils, GroupUtils, Trigonometric functions (all included in map)
J4L (AIDS), Vexorian (TU and GU?), Afronight_76 (model), tonic (icon), PeeKay (Bone Spear model and icon), Diablo 2 (Bone Spear ) and Diablo 3 (Living Wall )