DrinkSlurm
Eat Bachelor Chow!
- Reaction score
- 50
last updated: January 18, 2008
version 1.00: original Cool Whip spell
version 2.00: original submission to the Spells forum
version 2.50: coding optimizations, additional effects for Fire Whip, added Shock Whip and Death Whip, cancel-ability added to all spells
version 2.52: more coding optimizations, preloading added
This was my Cool Whip spell originally developed for the Official Christmas Spell Contest. With help from Pyrogasm, I've been able to clean up and optimize the code quite a bit. There is also a full configuration section included. Also, I created several other similar whip spells with various shapes and effects. In addition, I also came up with an Aim Help system.
Cool Whip
Spell type: instant-cast, pseudo-channeling
Description: Summons a magical tentacle made of ice particles that whips back and forth in an enlarging sinusoid shape with an oscillating pattern. The Hero cannot move nor attack nor cast any spells while the whip is active, but the Hero CAN turn and rotate in order to aim the whip. Enemy units that are hit by the whip take damage and are briefly slowed. [100 mana cost; 15 second cooldown]
Level 1 - creates a small whip that extends up to a distance of 630 away, deals up to 20 damage per second, slows by 50%, and lasts for 6 seconds.
Level 2 - creates a medium whip that extends up to a distance of 810 away, deals up to 24 damage per second, slows by 60%, and lasts for 8 seconds.
Level 3 - creates a large whip that extends up to a distance of 990 away, deals up to 28 damage per second, slows by 70%, and lasts for 10 seconds.
Coding: JASS; requires vJASS
Leaks: Leakless
Multi Instanceability: MUI
Lag-susceptibility: moderate
Import Difficulty: easy (instructions included)
Trigger:
Fire Whip
Spell type: instant-cast, pseudo-channeling
Description: Summons a tentacle made of fire that whips back and forth in an enlarging travelling sinusoid shape. The Hero cannot move nor attack while the whip is active, but the Hero CAN rotate in order to aim the whip. The whip deals direct damage to enemy targets and lingering flames also deal residual damage and reduce armor briefly. [100 mana cost; 15 second cooldown]
Level 1 - a small whip that extends up to 525 away, deals up to 40 damage per second, and lasts for 6 seconds. Residual flames deal 5 damage per second and reduce armor by 2 for 5 seconds.
Level 2 - a medium whip that extends up 675 away, deals up to 48 damage per second, and lasts for 8 seconds. Residual flames deal 6 damage per second and reduce armor by 3 for 5 seconds.
Level 3 - a large whip that extends up to a distance of 825 away, deals up to 56 damage per second, and lasts for 10 seconds. Residual flames deal 7 damage per second and reduce armor by 3 for 5 seconds.
Coding: JASS; requires vJASS
Leaks: Leakless
Multi Instanceability: MUI
Lag-susceptibility: moderate
Import Difficulty: easy (instructions included)
Trigger:
Screenshot:
Shock Whip
Spell type: instant-cast, pseudo-channeling
Description: Summons a magical tentacle made of electricity that whips back and forth from side to side. The Hero cannot move nor attack nor cast any spells while the whip is active, but the Hero CAN turn and rotate in order to aim the whip. Enemy units that are hit by the whip take damage and have a chance to be purged. [100 mana cost; 15 second cooldown]
Level 1 - creates a small whip that extends up to a distance of 630 away, deals up to 30 damage per second, and lasts for 6 seconds. 20% chance to purge targets.
Level 2 - creates a medium whip that extends up to a distance of 810 away, deals up to 36 damage per second, and lasts for 8 seconds. 30% chance to purge targets.
Level 3 - creates a large whip that extends up to a distance of 990 away, deals up to 42 damage per second, and lasts for 10 seconds. 40% chance to purge targets.
Coding: JASS; requires vJASS
Leaks: Leakless
Multi Instanceability: MUI
Lag-susceptibility: moderate
Import Difficulty: easy (instructions included)
Trigger:
Death Whip
Spell type: instant-cast, pseudo-channeling
Description: Summons a ghastly tentacle made of dark energies that whips from side to side and extends forward and backward. The Hero cannot move nor attack nor cast any spells while the whip is active, but the Hero CAN rotate in order to aim the whip. Enemy units that are hit by the whip take damage. If targets come into contact with the Skull Head they will be Crippled. [100 mana cost; 15 second cooldown]
Level 1 - a small whip that extends up to 420 distance away, deals up to 30 damage per second, and lasts for 6 seconds. Cripple reduces target's speed and damage by 50% for 5 seconds.
Level 2 - a medium whip that extends up to 560 distance away, deals up to 36 damage per second, and lasts for 8 seconds. Cripple reduces target's speed and damage by 60% for 6 seconds.
Level 3 - a large whip that extends up to 700 distance away, deals up to 42 damage per second, and lasts for 10 seconds. Cripple reduces target's speed and damage by 70% for 7 seconds.
Coding: JASS; requires vJASS
Leaks: Leakless
Multi Instanceability: MUI
Lag-susceptibility: moderate
Import Difficulty: easy (instructions included)
Trigger:
(Trigger not shown due to post text limit.)
Screenshot:
Aim Help
Description: More than one person has made a comment about how difficult it was to aim the original Cool Whip spell, so I have included this Aim Help Trigger to address that problem. This works for all whip spells.
Trigger:
(Trigger not shown due to post text limit.)
version 1.00: original Cool Whip spell
version 2.00: original submission to the Spells forum
version 2.50: coding optimizations, additional effects for Fire Whip, added Shock Whip and Death Whip, cancel-ability added to all spells
version 2.52: more coding optimizations, preloading added
This was my Cool Whip spell originally developed for the Official Christmas Spell Contest. With help from Pyrogasm, I've been able to clean up and optimize the code quite a bit. There is also a full configuration section included. Also, I created several other similar whip spells with various shapes and effects. In addition, I also came up with an Aim Help system.
Cool Whip
Spell type: instant-cast, pseudo-channeling
Description: Summons a magical tentacle made of ice particles that whips back and forth in an enlarging sinusoid shape with an oscillating pattern. The Hero cannot move nor attack nor cast any spells while the whip is active, but the Hero CAN turn and rotate in order to aim the whip. Enemy units that are hit by the whip take damage and are briefly slowed. [100 mana cost; 15 second cooldown]
Level 1 - creates a small whip that extends up to a distance of 630 away, deals up to 20 damage per second, slows by 50%, and lasts for 6 seconds.
Level 2 - creates a medium whip that extends up to a distance of 810 away, deals up to 24 damage per second, slows by 60%, and lasts for 8 seconds.
Level 3 - creates a large whip that extends up to a distance of 990 away, deals up to 28 damage per second, slows by 70%, and lasts for 10 seconds.
Coding: JASS; requires vJASS
Leaks: Leakless
Multi Instanceability: MUI
Lag-susceptibility: moderate
Import Difficulty: easy (instructions included)
Trigger:
JASS:
//===========================================================================
//=========================== Cool Whip v2.52 ===============================
//============================ Instructions =================================
//===========================================================================
// MUI; requires vJASS
// Note: because the damage done is based on Permanent Immolation, damage will not
// stack (i.e. a unit being hit by 2 whips at a time will NOT take double damage)
// Note: units and abilities have been preloaded to prevent 'first-cast-lag'
//===========================================================================
// 1. Go to the Object Editor, and copy + paste the following:
// Units
// * Dummy Caster
// * Cool Whip Segment
// Abilities
// * Cool Whip: Add this ability to the Hero; make sure the buff is set to the
// custom "Cool Whip (Active)" buff.
// * Silence (for Cool Whip): make sure that the buff is set to the custom "Cool
// Whip (Caster)" buff.
// * Permanent Immolation (for Cool Whip): make sure that the buff is set
// to the custom "Cool Whip (Target)" buff.
// * Slow Aura (for Cool Whip): this is what provides the slow on targets.
// Buffs
// * Cool Whip (Active): This is the buff that comes from the "Cool Whip" ability
// It appears on the Hero when the spell is activated and disappears when the
// spell is cancelled.
// * Cool Whip (Caster): This is the buff that comes from the "Silence (for Cool
// Whip)" ability.
// * Cool Whip (Target): This buff applies the snowflakes-looking effects on
// targets that are hit by the whip.
// 2. Copy the following triggers:
// * this trigger: "CoolWhip"
// * AimHelp: this is an OPTIONAL trigger used to help with the aiming of this
// spell.
// 3. Other options:
// * Hero: Note that you may want to change "Stats - Can Flee" to "False" to
// make this skill a little easier to use. Otherwise, while the Hero is
// silenced, it will tend to run away from enemies since it can't attack.
// 4. To adjust the balance of the skill, you can change some of the following values:
// Abilities
// * Cool Whip: Cooldown, Mana Cost
// * Permanent Immolation (for Cool Whip): Damage, damage interval, AoE
// * Slow Aura (for Cool Whip): Attack Speed Factor, Movement Speed Factor, AoE
// 5. Make sure that the rawcodes specified below reference the proper units,
// abilities, or buffs.
scope CoolWhip
globals
private constant integer COOLWHIP = 039;A004039; //Ability ID of "Cool Whip"
private constant integer DUMMY = 039;h000039; //Unit ID of "Dummy Caster"
private constant integer SILENCE = 039;A001039; //Ability ID of "Silence (for Cool Whip)"
constant integer CWBUFF = 039;B006039; //Buff ID of "Cool Whip (Caster)"
private constant integer WHIPSEG = 039;h001039; //Unit ID of "Cool Whip Segment"
private constant integer PERMIMMO = 039;A002039; //Ability ID of "Permanent Immolation (for Cool Whip)"
private constant integer SLOW = 039;A003039; //Ability ID of "Slow Aura (for Cool Whip)"
private constant integer CWBUFF2 = 039;B004039; //Buff ID of "Cool Whip (Active)"
// 6. More configuration options:
private constant string ANIM = "spell" //animation name during spell (adjust according to your chosen hero's model); default "spell"
private constant real SPACING = 30.00 //spacing of the whip segments (adjusts 'length' of whip); default 30.00
private constant real INTERVAL = 0.03 //timer interval; default 0.03
private constant real ANIMSCALE = 1.00 //animation scale of whip segments; default 1.00
private constant real ANIMSPEED = 0.20 //animation speed of whip segments; default 0.20
private constant integer SPW = 12 //segments per wavelength (make this a multiple of 4 for best results); default 12
private constant real AMP = 1.25 //amplitude multiplier (larger value makes the whip 'wider'; default 1.25
private constant integer WCFACTOR = 6 //whip count factor; this and WCBASE determine total amount of whip segments; default 6
private constant integer WCBASE = 15 //whip count base; this and WCFACTOR determine total amount of whip segments; default 15
private constant real DURFACTOR = 2.00 //duration factor; this and DURBASE determine total duration of the spell; default 2.00
private constant real DURBASE = 4.00 //duration base; this and DURFACTOR determine total duration of the spell; default 4.00
endglobals
//========================== End of Instructions ============================
//===========================================================================
private struct CW
unit hero
real herox
real heroy
integer c
group g = CreateGroup()
endstruct
globals
private CW array ALLDATA
private timer ALLTIMER = CreateTimer()
private integer TOTAL = 0
private integer INSTANCE
endglobals
//===========================================================================
private function Cond takes nothing returns boolean
return GetSpellAbilityId() == COOLWHIP
endfunction
private function GroupKillWhip takes nothing returns nothing
call KillUnit(GetEnumUnit())
endfunction
private function GroupMoveWhip takes nothing returns nothing
local CW data = ALLDATA[INSTANCE]
local unit u = GetEnumUnit()
local integer cv = GetUnitUserData(u)
local integer osc = IAbsBJ(ModuloInteger((data.c + 10), 40) - 20) - 10
local real face_rad = GetUnitFacing(data.hero) * bj_PI/180.0
local real dx = cv * SPACING
local real dy = AMP * Sin(cv * 2 * bj_PI / SPW) * cv * osc
local real dist = SquareRoot(dx * dx + dy * dy)
local real ang_rad = Atan2(dy, dx) + face_rad
local real x = data.herox + dist * Cos(ang_rad)
local real y = data.heroy + dist * Sin(ang_rad)
call SetUnitPosition(u, x, y)
set u = null
endfunction
private function TimerActions takes nothing returns nothing
local CW data
local unit u
local integer i = 1
loop
exitwhen i > TOTAL
set data = ALLDATA<i>
if GetUnitAbilityLevel(data.hero, CWBUFF) == 0 then
set u = CreateUnit(GetOwningPlayer(data.hero), DUMMY, data.herox, data.heroy, 0)
call UnitApplyTimedLife(u, 039;BTLF039;, 1.00)
call UnitAddAbility(u, SILENCE)
call IssuePointOrder(u, "silence", data.herox, data.heroy)
endif
call SetUnitAnimation(data.hero, ANIM)
set INSTANCE = i
call ForGroup(data.g, function GroupMoveWhip)
set data.c = data.c - 1
if GetWidgetLife(data.hero) < 0.406 or data.c <= 0 or GetUnitAbilityLevel(data.hero, CWBUFF2) == 0 then
call IssueImmediateOrder(data.hero, "manashieldoff")
if GetWidgetLife(data.hero) < 0.406 then
call SetUnitAnimation(data.hero, "death")
else
call SetUnitAnimation(data.hero, "stand")
call UnitRemoveAbility(data.hero, CWBUFF)
call IssueImmediateOrder(data.hero, "stop")
endif
call ForGroup(data.g, function GroupKillWhip)
call data.destroy()
set ALLDATA<i> = ALLDATA[TOTAL]
set TOTAL = TOTAL - 1
set i = i - 1
if TOTAL == 0 then
call PauseTimer(ALLTIMER)
endif
endif
set i = i + 1
endloop
set u = null
endfunction
private function Cast takes nothing returns nothing
local CW data = CW.create()
local real face_rad = GetUnitFacing(GetTriggerUnit()) * bj_PI/180.0
local real facecos = Cos(face_rad)
local real facesin = Sin(face_rad)
local real x = GetUnitX(GetTriggerUnit())
local real y = GetUnitY(GetTriggerUnit())
local integer lvl = GetUnitAbilityLevel(GetTriggerUnit(), COOLWHIP)
local integer whipcount = lvl * WCFACTOR + WCBASE
local real dur = lvl * DURFACTOR + DURBASE
local unit u = CreateUnit(GetOwningPlayer(GetTriggerUnit()), DUMMY, x, y, 0)
local integer i = 1
set data.hero = GetTriggerUnit()
set data.herox = x
set data.heroy = y
set data.c = R2I(dur / INTERVAL)
call UnitApplyTimedLife(u, 039;BTLF039;, 1.00)
call UnitAddAbility(u, SILENCE)
call IssuePointOrder(u, "silence", x, y)
call SetUnitAnimation(data.hero, ANIM)
loop
exitwhen i > whipcount
set x = data.herox + i * SPACING * facecos
set y = data.heroy + i * SPACING * facesin
set u = CreateUnit(GetOwningPlayer(data.hero), WHIPSEG, x, y, 0)
call UnitApplyTimedLife(u, 039;BTLF039;, dur)
call SetUnitScale(u, ANIMSCALE, ANIMSCALE, ANIMSCALE)
call SetUnitTimeScale(u, ANIMSPEED)
call UnitAddAbility(u, PERMIMMO)
call SetUnitAbilityLevel(u, PERMIMMO, lvl)
call UnitAddAbility(u, SLOW)
call SetUnitAbilityLevel(u, SLOW, lvl)
call SetUnitUserData(u, i)
call GroupAddUnit(data.g, u)
set i = i + 1
endloop
set TOTAL = TOTAL + 1
set ALLDATA[TOTAL] = data
if TOTAL == 1 then
call TimerStart(ALLTIMER, INTERVAL, true, function TimerActions)
endif
set u = null
endfunction
//===========================================================================
public function InitTrig takes nothing returns nothing
local unit u = CreateUnit(Player(0), WHIPSEG, 0, 0, 0)
call UnitAddAbility(u, SILENCE)
call UnitAddAbility(u, PERMIMMO)
call UnitAddAbility(u, SLOW)
call RemoveUnit(u)
set gg_trg_CoolWhip = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_CoolWhip, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_CoolWhip, Condition(function Cond))
call TriggerAddAction(gg_trg_CoolWhip, function Cast)
set u = null
endfunction
endscope</i></i>
Fire Whip
Spell type: instant-cast, pseudo-channeling
Description: Summons a tentacle made of fire that whips back and forth in an enlarging travelling sinusoid shape. The Hero cannot move nor attack while the whip is active, but the Hero CAN rotate in order to aim the whip. The whip deals direct damage to enemy targets and lingering flames also deal residual damage and reduce armor briefly. [100 mana cost; 15 second cooldown]
Level 1 - a small whip that extends up to 525 away, deals up to 40 damage per second, and lasts for 6 seconds. Residual flames deal 5 damage per second and reduce armor by 2 for 5 seconds.
Level 2 - a medium whip that extends up 675 away, deals up to 48 damage per second, and lasts for 8 seconds. Residual flames deal 6 damage per second and reduce armor by 3 for 5 seconds.
Level 3 - a large whip that extends up to a distance of 825 away, deals up to 56 damage per second, and lasts for 10 seconds. Residual flames deal 7 damage per second and reduce armor by 3 for 5 seconds.
Coding: JASS; requires vJASS
Leaks: Leakless
Multi Instanceability: MUI
Lag-susceptibility: moderate
Import Difficulty: easy (instructions included)
Trigger:
JASS:
//===========================================================================
//=========================== Fire Whip v2.52 ===============================
//============================ Instructions =================================
//===========================================================================
// MUI; requires vJASS
// Note: because the damage done is based on Permanent Immolation, damage will not
// stack (i.e. a unit being hit by 2 whips at a time will NOT take double damage)
// Note: units and abilities have been preloaded to prevent 'first-cast-lag'
//===========================================================================
// 1. Go to the Object Editor, and copy + paste the following:
// Units
// * Dummy Caster
// * Fire Whip Segment
// Abilities
// * Fire Whip: Add this ability to the Hero; make sure the buff is set to the
// custom "Fire Whip (Active)" buff.
// * Silence (for Fire Whip): make sure that the buff is set to the custom "Fire
// Whip (Caster)" buff.
// * Permanent Immolation (for Fire Whip): make sure that the buff is set
// to the custom "Fire Whip (Target)" buff.
// * Acid Bomb (for Fire Whip): make sure that the buff is set to the custom
// "Fire Whip (Target)" buff.
// Buffs
// * Fire Whip (Active): This is the buff that comes from the "Fire Whip" ability.
// It appears on the Hero when the spell is activated and disappears when the
// spell is cancelled.
// * Fire Whip (Caster): This is the buff that comes from the "Silence (for Fire
// Whip)" ability.
// * Fire Whip (Target): This buff applies the flames-looking effects on
// targets that are hit by the whip.
// 2. Copy the following triggers:
// * this trigger: "FireWhip"
// * AimHelp: this is an OPTIONAL trigger used to help with the aiming of this
// spell.
// 3. Other options:
// * Hero: Note that you may want to change "Stats - Can Flee" to "False" to
// make this skill a little easier to use. Otherwise, while the Hero is
// silenced, it will tend to run away from enemies since it can't attack.
// 4. To adjust the balance of the skill, you can change some of the following values:
// Abilities
// * Fire Whip: Cooldown, Mana Cost
// * Permanent Immolation (for Fire Whip): Damage, damage interval, AoE
// * Acid Bomb (for Fire Whip): primary damage, damage interval, duration, armor penalty
// 5. Make sure that the rawcodes specified below reference the proper units,
// abilities, or buffs.
scope FireWhip
globals
private constant integer FIREWHIP = 039;A008039; //Ability ID of "Fire Whip"
private constant integer DUMMY = 039;h000039; //Unit ID of "Dummy Caster"
private constant integer SILENCE = 039;A005039; //Ability ID of "Silence (for Fire Whip)"
constant integer FWBUFF = 039;B002039; //Buff ID of "Fire Whip (Caster)"
private constant integer WHIPSEG = 039;h002039; //Unit ID of "Fire Whip Segment"
private constant integer PERMIMMO = 039;A006039; //Ability ID of "Permanent Immolation (for Fire Whip)"
private constant integer FWBUFF2 = 039;B005039; //Buff ID of "Fire Whip (Active)"
private constant integer ACIDBOMB = 039;A00B039; //Ability ID of "Acid Bomb (for Fire Whip)"
// 6. More configuration options:
private constant string ANIM = "spell" //animation name during spell (adjust according to your chosen hero's model); default "spell"
private constant real SPACING = 25.00 //spacing of the whip segments (adjusts 'length' of whip); default 25.00
private constant real INTERVAL = 0.04 //timer interval; default 0.04
private constant real ANIMSCALE = 1.00 //animation scale of whip segments; default 1.00
private constant real ANIMSPEED = 0.20 //animation speed of whip segments; default 0.20
private constant integer SPW = 12 //segments per wavelength (make this a multiple of 4 for best results); default 12
private constant real AMP = 15.00 //amplitude multiplier (larger value makes the whip 'wider'; default 15.00
private constant integer WCFACTOR = 6 //whip count factor; this and WCBASE determine total amount of whip segments; default 6
private constant integer WCBASE = 15 //whip count base; this and WCFACTOR determine total amount of whip segments; default 15
private constant real DURFACTOR = 2.00 //duration factor; this and DURBASE determine total duration of the spell; default 2.00
private constant real DURBASE = 4.00 //duration base; this and DURFACTOR determine total duration of the spell; default 4.00
private constant real OSFACTOR = 6.00 //offset multiplier (adjusts speed of travelling wave motion of whip); default 6.00
private constant real CINTERVAL = 0.50 //check interval; how often (in seconds) a check for residual DoT occurs; default 0.50
endglobals
//========================== End of Instructions ============================
//===========================================================================
private struct FW
unit hero
real herox
real heroy
integer lvl
integer c
integer cpi
group g = CreateGroup()
endstruct
globals
private FW array ALLDATA
private timer ALLTIMER = CreateTimer()
private integer TOTAL = 0
private integer INSTANCE
private boolexpr FILTER
private group TEMPGROUP = CreateGroup()
private unit TEMPUNIT
endglobals
//===========================================================================
private function Cond takes nothing returns boolean
return GetSpellAbilityId() == FIREWHIP
endfunction
private function GroupFilter takes nothing returns boolean
return GetWidgetLife(GetFilterUnit()) > 0.406 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TEMPUNIT)) == true
endfunction
private function GroupKillWhip takes nothing returns nothing
call KillUnit(GetEnumUnit())
endfunction
private function GroupAcidbomb takes nothing returns nothing
local FW data = ALLDATA[INSTANCE]
local unit e = GetEnumUnit()
local real x = GetUnitX(e)
local real y = GetUnitY(e)
local unit u = CreateUnit(GetOwningPlayer(TEMPUNIT), DUMMY, x, y, 0)
call UnitApplyTimedLife(u, 039;BTLF039;, 1.00)
call UnitAddAbility(u, ACIDBOMB)
call SetUnitAbilityLevel(u, ACIDBOMB, data.lvl)
call IssueTargetOrder(u, "acidbomb", e)
set u = null
set e = null
endfunction
private function GroupMoveWhip takes nothing returns nothing
local FW data = ALLDATA[INSTANCE]
local group g = CreateGroup()
local unit u = GetEnumUnit()
local integer cv = GetUnitUserData(u)
local real offset = data.c * OSFACTOR
local real face_rad = GetUnitFacing(data.hero) * bj_PI/180.0
local real dx = cv * SPACING
local real dy = AMP * Sin(cv * 2 * bj_PI / SPW - offset) * cv
local real dist = SquareRoot(dx * dx + dy * dy)
local real ang_rad = Atan2(dy, dx) + face_rad
local real x = data.herox + dist * Cos(ang_rad)
local real y = data.heroy + dist * Sin(ang_rad)
call SetUnitPosition(u, x, y)
if ModuloInteger(data.c, data.cpi) == 0 then
set TEMPUNIT = u
call GroupEnumUnitsInRange(g, x, y, 75, FILTER)
call GroupAddGroup(g, TEMPGROUP)
endif
call DestroyGroup(g)
set g = null
set u = null
endfunction
private function TimerActions takes nothing returns nothing
local FW data
local unit u
local integer i = 1
loop
exitwhen i > TOTAL
set data = ALLDATA<i>
if GetUnitAbilityLevel(data.hero, FWBUFF) == 0 then
set u = CreateUnit(GetOwningPlayer(data.hero), DUMMY, data.herox, data.heroy, 0)
call UnitApplyTimedLife(u, 039;BTLF039;, 1.00)
call UnitAddAbility(u, SILENCE)
call IssuePointOrder(u, "silence", data.herox, data.heroy)
endif
call SetUnitAnimation(data.hero, ANIM)
set INSTANCE = i
call ForGroup(data.g, function GroupMoveWhip)
call ForGroup(TEMPGROUP, function GroupAcidbomb)
call GroupClear(TEMPGROUP)
set data.c = data.c - 1
if GetWidgetLife(data.hero) < 0.406 or data.c <= 0 or GetUnitAbilityLevel(data.hero, FWBUFF2) == 0 then
call IssueImmediateOrder(data.hero, "unimmolation")
if GetWidgetLife(data.hero) < 0.406 then
call SetUnitAnimation(data.hero, "death")
else
call SetUnitAnimation(data.hero, "stand")
call UnitRemoveAbility(data.hero, FWBUFF)
call IssueImmediateOrder(data.hero, "stop")
endif
call ForGroup(data.g, function GroupKillWhip)
call data.destroy()
set ALLDATA<i> = ALLDATA[TOTAL]
set TOTAL = TOTAL - 1
set i = i - 1
if TOTAL == 0 then
call PauseTimer(ALLTIMER)
endif
endif
set i = i + 1
endloop
set u = null
endfunction
private function Cast takes nothing returns nothing
local FW data = FW.create()
local real face_rad = GetUnitFacing(GetTriggerUnit()) * bj_PI/180.0
local real facecos = Cos(face_rad)
local real facesin = Sin(face_rad)
local real x = GetUnitX(GetTriggerUnit())
local real y = GetUnitY(GetTriggerUnit())
local integer lvl = GetUnitAbilityLevel(GetTriggerUnit(), FIREWHIP)
local integer whipcount = lvl * WCFACTOR + WCBASE
local real dur = lvl * DURFACTOR + DURBASE
local unit u = CreateUnit(GetOwningPlayer(GetTriggerUnit()), DUMMY, x, y, 0)
local integer i = 1
set data.hero = GetTriggerUnit()
set data.herox = x
set data.heroy = y
set data.lvl = lvl
set data.c = R2I(dur / INTERVAL)
set data.cpi = R2I(CINTERVAL / INTERVAL)
call UnitApplyTimedLife(u, 039;BTLF039;, 1.00)
call UnitAddAbility(u, SILENCE)
call IssuePointOrder(u, "silence", x, y)
call SetUnitAnimation(data.hero, ANIM)
loop
exitwhen i > whipcount
set x = data.herox + i * SPACING * facecos
set y = data.heroy + i * SPACING * facesin
set u = CreateUnit(GetOwningPlayer(data.hero), WHIPSEG, x, y, 0)
call UnitApplyTimedLife(u, 039;BTLF039;, dur)
call SetUnitScale(u, ANIMSCALE, ANIMSCALE, ANIMSCALE)
call SetUnitTimeScale(u, ANIMSPEED)
call UnitAddAbility(u, PERMIMMO)
call SetUnitAbilityLevel(u, PERMIMMO, lvl)
call SetUnitUserData(u, i)
call GroupAddUnit(data.g, u)
set i = i + 1
endloop
set TOTAL = TOTAL + 1
set ALLDATA[TOTAL] = data
if TOTAL == 1 then
call TimerStart(ALLTIMER, INTERVAL, true, function TimerActions)
endif
set u = null
endfunction
//===========================================================================
public function InitTrig takes nothing returns nothing
local unit u = CreateUnit(Player(0), WHIPSEG, 0, 0, 0)
call UnitAddAbility(u, SILENCE)
call UnitAddAbility(u, PERMIMMO)
call UnitAddAbility(u, ACIDBOMB)
call RemoveUnit(u)
set gg_trg_FireWhip = CreateTrigger()
set FILTER = Condition(function GroupFilter)
call TriggerRegisterAnyUnitEventBJ(gg_trg_FireWhip, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_FireWhip, Condition(function Cond))
call TriggerAddAction(gg_trg_FireWhip, function Cast)
set u = null
endfunction
endscope</i></i>
Screenshot:
Shock Whip
Spell type: instant-cast, pseudo-channeling
Description: Summons a magical tentacle made of electricity that whips back and forth from side to side. The Hero cannot move nor attack nor cast any spells while the whip is active, but the Hero CAN turn and rotate in order to aim the whip. Enemy units that are hit by the whip take damage and have a chance to be purged. [100 mana cost; 15 second cooldown]
Level 1 - creates a small whip that extends up to a distance of 630 away, deals up to 30 damage per second, and lasts for 6 seconds. 20% chance to purge targets.
Level 2 - creates a medium whip that extends up to a distance of 810 away, deals up to 36 damage per second, and lasts for 8 seconds. 30% chance to purge targets.
Level 3 - creates a large whip that extends up to a distance of 990 away, deals up to 42 damage per second, and lasts for 10 seconds. 40% chance to purge targets.
Coding: JASS; requires vJASS
Leaks: Leakless
Multi Instanceability: MUI
Lag-susceptibility: moderate
Import Difficulty: easy (instructions included)
Trigger:
JASS:
//===========================================================================
//========================== Shock Whip v2.52 ===============================
//============================ Instructions =================================
//===========================================================================
// MUI; requires vJASS
// Note: because the damage done is based on Permanent Immolation, damage will not
// stack (i.e. a unit being hit by 2 whips at a time will NOT take double damage)
// Note: units and abilities have been preloaded to prevent 'first-cast-lag'
//===========================================================================
// 1. Go to the Object Editor, and copy + paste the following:
// Units
// * Dummy Caster
// * Shcck Whip Segment
// Abilities
// * Shock Whip: Add this ability to the Hero; make sure the buff is set to the
// custom "Shock Whip (Active)" buff.
// * Silence (for Shock Whip): make sure that the buff is set to the custom "Shock
// Whip (Caster)" buff.
// * Permanent Immolation (for Shock Whip): make sure that the buff is set
// to the custom "Shock Whip (Target)" buff.
// * Purge (for Shock Whip): this is the purge that has a chance to be cast on targets
// Buffs
// * Shock Whip (Active): This is the buff that comes from the "Shock Whip" ability
// It appears on the Hero when the spell is activated and disappears when the
// spell is cancelled.
// * Shock Whip (Caster): This is the buff that comes from the "Silence (for Shock
// Whip)" ability.
// * Shock Whip (Target): This buff applies the purge effects on targets that
// are hit by the whip.
// 2. Copy the following triggers:
// * this trigger: "ShockWhip"
// * AimHelp: this is an OPTIONAL trigger used to help with the aiming of this
// spell.
// 3. Other options:
// * Hero: Note that you may want to change "Stats - Can Flee" to "False" to
// make this skill a little easier to use. Otherwise, while the Hero is
// silenced, it will tend to run away from enemies since it can't attack.
// 4. To adjust the balance of the skill, you can change some of the following values:
// Abilities
// * Shock Whip: Cooldown, Mana Cost
// * Permanent Immolation (for Shock Whip): Damage, damage interval, AoE
// * Purge (for Shock Whip): durations, damage to summons
// 5. Make sure that the rawcodes specified below reference the proper units,
// abilities, or buffs.
scope ShockWhip
globals
private constant integer SHOCKWHIP = 039;A000039; //Ability ID of "Shock Whip"
private constant integer DUMMY = 039;h000039; //Unit ID of "Dummy Caster"
private constant integer SILENCE = 039;A007039; //Ability ID of "Silence (for Shock Whip)"
constant integer SWBUFF = 039;B007039; //Buff ID of "Shock Whip (Caster)"
private constant integer WHIPSEG = 039;h003039; //Unit ID of "Shock Whip Segment"
private constant integer PERMIMMO = 039;A009039; //Ability ID of "Permanent Immolation (for Shock Whip)"
private constant integer SWBUFF2 = 039;B000039; //Buff ID of "Shock Whip (Active)"
private constant integer PURGE = 039;A00A039; //Ability ID of "Purge (for Shock Whip)"
// 6. More configuration options:
private constant string ANIM = "spell" //animation name during spell (adjust according to your chosen hero's model); default "spell"
private constant real SPACING = 30.00 //spacing of the whip segments (adjusts 'length' of whip); default 30.00
private constant real INTERVAL = 0.04 //timer interval; default 0.04
private constant real ANIMSCALE = 0.50 //animation scale of whip segments; default 1.00
private constant real ANIMSPEED = 0.50 //animation speed of whip segments; default 1.00
private constant integer WCFACTOR = 6 //whip count factor; this and WCBASE determine total amount of whip segments; default 6
private constant integer WCBASE = 15 //whip count base; this and WCFACTOR determine total amount of whip segments; default 15
private constant real DURFACTOR = 2.00 //duration factor; this and DURBASE determine total duration of the spell; default 2.00
private constant real DURBASE = 4.00 //duration base; this and DURFACTOR determine total duration of the spell; default 4.00
private constant real CHBASE = 20.00 //this and CHFACTOR determine the chance for targets to be purged; default 10.00
private constant real CHFACTOR = 10.00 //this and CHBASE determine the chance for targets to be purged; default 10.00
private constant real WHIPCURL = 2.00 //curling factor (higher value makes whip curl tighter); default 2.00
private constant real WHIPWIDTH = 1.00 //width factor (higher value makes whip swing wider); default 1.00
private constant real CINTERVAL = 0.25 //check interval; how often (in seconds) a check for the chance to purge occurs; default 0.25
endglobals
//========================== End of Instructions ============================
//===========================================================================
private struct SW
unit hero
real herox
real heroy
integer lvl
integer c
integer cpi
group g = CreateGroup()
endstruct
globals
private SW array ALLDATA
private timer ALLTIMER = CreateTimer()
private integer TOTAL = 0
private integer INSTANCE
private boolexpr FILTER
private group TEMPGROUP = CreateGroup()
private unit TEMPUNIT
endglobals
//===========================================================================
private function Cond takes nothing returns boolean
return GetSpellAbilityId() == SHOCKWHIP
endfunction
private function GroupFilter takes nothing returns boolean
return GetWidgetLife(GetFilterUnit()) > 0.406 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TEMPUNIT)) == true
endfunction
private function GroupKillWhip takes nothing returns nothing
call KillUnit(GetEnumUnit())
endfunction
private function GroupPurge takes nothing returns nothing
local SW data = ALLDATA[INSTANCE]
local unit e = GetEnumUnit()
local real x = GetUnitX(e)
local real y = GetUnitY(e)
local unit u
if GetRandomReal(1, 100) <= CHFACTOR * data.lvl + CHBASE then
set u = CreateUnit(GetOwningPlayer(TEMPUNIT), DUMMY, x, y, 0)
call UnitApplyTimedLife(u, 039;BTLF039;, 1.00)
call UnitAddAbility(u, PURGE)
call IssueTargetOrder(u, "purge", e)
endif
set u = null
set e = null
endfunction
private function GroupMoveWhip takes nothing returns nothing
local SW data = ALLDATA[INSTANCE]
local group g = CreateGroup()
local unit u = GetEnumUnit()
local integer cv = GetUnitUserData(u)
local integer osc = IAbsBJ(ModuloInteger(data.c + 10, 40) - 20) - 10
local real face_rad = GetUnitFacing(data.hero) * bj_PI/180.0
local real dist = cv * (SPACING - IAbsBJ(osc) * WHIPCURL)
local real ang_rad = face_rad + (osc * cv * 0.01 * WHIPWIDTH)
local real x = data.herox + dist * Cos(ang_rad)
local real y = data.heroy + dist * Sin(ang_rad)
call SetUnitPosition(u, x, y)
if ModuloInteger(data.c, data.cpi) == 0 then
set TEMPUNIT = u
call GroupEnumUnitsInRange(g, x, y, 50, FILTER)
call GroupAddGroup(g, TEMPGROUP)
endif
call DestroyGroup(g)
set g = null
set u = null
endfunction
private function TimerActions takes nothing returns nothing
local SW data
local unit u
local integer i = 1
loop
exitwhen i > TOTAL
set data = ALLDATA<i>
if GetUnitAbilityLevel(data.hero, SWBUFF) == 0 then
set u = CreateUnit(GetOwningPlayer(data.hero), DUMMY, data.herox, data.heroy, 0)
call UnitApplyTimedLife(u, 039;BTLF039;, 1.00)
call UnitAddAbility(u, SILENCE)
call IssuePointOrder(u, "silence", data.herox, data.heroy)
endif
call SetUnitAnimation(data.hero, ANIM)
set INSTANCE = i
call ForGroup(data.g, function GroupMoveWhip)
call ForGroup(TEMPGROUP, function GroupPurge)
call GroupClear(TEMPGROUP)
set data.c = data.c - 1
if GetWidgetLife(data.hero) < 0.406 or data.c <= 0 or GetUnitAbilityLevel(data.hero, SWBUFF2) == 0 then
call IssueImmediateOrder(data.hero, "manashieldoff")
if GetWidgetLife(data.hero) < 0.406 then
call SetUnitAnimation(data.hero, "death")
else
call SetUnitAnimation(data.hero, "stand")
call UnitRemoveAbility(data.hero, SWBUFF)
call IssueImmediateOrder(data.hero, "stop")
endif
call ForGroup(data.g, function GroupKillWhip)
call data.destroy()
set ALLDATA<i> = ALLDATA[TOTAL]
set TOTAL = TOTAL - 1
set i = i - 1
if TOTAL == 0 then
call PauseTimer(ALLTIMER)
endif
endif
set i = i + 1
endloop
set u = null
endfunction
private function Cast takes nothing returns nothing
local SW data = SW.create()
local real face_rad = GetUnitFacing(GetTriggerUnit()) * bj_PI/180.0
local real facecos = Cos(face_rad)
local real facesin = Sin(face_rad)
local real x = GetUnitX(GetTriggerUnit())
local real y = GetUnitY(GetTriggerUnit())
local integer lvl = GetUnitAbilityLevel(GetTriggerUnit(), SHOCKWHIP)
local integer whipcount = lvl * WCFACTOR + WCBASE
local real dur = lvl * DURFACTOR + DURBASE
local unit u = CreateUnit(GetOwningPlayer(GetTriggerUnit()), DUMMY, x, y, 0)
local integer i = 1
set data.hero = GetTriggerUnit()
set data.herox = x
set data.heroy = y
set data.lvl = lvl
set data.c = R2I(dur / INTERVAL)
set data.cpi = R2I(CINTERVAL / INTERVAL)
call UnitApplyTimedLife(u, 039;BTLF039;, 1.00)
call UnitAddAbility(u, SILENCE)
call IssuePointOrder(u, "silence", x, y)
call SetUnitAnimation(data.hero, ANIM)
loop
exitwhen i > whipcount
set x = data.herox + i * (SPACING) * facecos
set y = data.heroy + i * (SPACING) * facesin
set u = CreateUnit(GetOwningPlayer(data.hero), WHIPSEG, x, y, 0)
call UnitApplyTimedLife(u, 039;BTLF039;, dur)
call SetUnitScale(u, ANIMSCALE, ANIMSCALE, ANIMSCALE)
call SetUnitTimeScale(u, ANIMSPEED)
call UnitAddAbility(u, PERMIMMO)
call SetUnitAbilityLevel(u, PERMIMMO, lvl)
call SetUnitUserData(u, i)
call GroupAddUnit(data.g, u)
set i = i + 1
endloop
set TOTAL = TOTAL + 1
set ALLDATA[TOTAL] = data
if TOTAL == 1 then
call TimerStart(ALLTIMER, INTERVAL, true, function TimerActions)
endif
set u = null
endfunction
//===========================================================================
public function InitTrig takes nothing returns nothing
local unit u = CreateUnit(Player(0), WHIPSEG, 0, 0, 0)
call UnitAddAbility(u, SILENCE)
call UnitAddAbility(u, PERMIMMO)
call UnitAddAbility(u, PURGE)
call RemoveUnit(u)
set gg_trg_ShockWhip = CreateTrigger()
set FILTER = Condition(function GroupFilter)
call TriggerRegisterAnyUnitEventBJ(gg_trg_ShockWhip, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_ShockWhip, Condition(function Cond))
call TriggerAddAction(gg_trg_ShockWhip, function Cast)
set u = null
endfunction
endscope</i></i>
Death Whip
Spell type: instant-cast, pseudo-channeling
Description: Summons a ghastly tentacle made of dark energies that whips from side to side and extends forward and backward. The Hero cannot move nor attack nor cast any spells while the whip is active, but the Hero CAN rotate in order to aim the whip. Enemy units that are hit by the whip take damage. If targets come into contact with the Skull Head they will be Crippled. [100 mana cost; 15 second cooldown]
Level 1 - a small whip that extends up to 420 distance away, deals up to 30 damage per second, and lasts for 6 seconds. Cripple reduces target's speed and damage by 50% for 5 seconds.
Level 2 - a medium whip that extends up to 560 distance away, deals up to 36 damage per second, and lasts for 8 seconds. Cripple reduces target's speed and damage by 60% for 6 seconds.
Level 3 - a large whip that extends up to 700 distance away, deals up to 42 damage per second, and lasts for 10 seconds. Cripple reduces target's speed and damage by 70% for 7 seconds.
Coding: JASS; requires vJASS
Leaks: Leakless
Multi Instanceability: MUI
Lag-susceptibility: moderate
Import Difficulty: easy (instructions included)
Trigger:
(Trigger not shown due to post text limit.)
Screenshot:
Aim Help
Description: More than one person has made a comment about how difficult it was to aim the original Cool Whip spell, so I have included this Aim Help Trigger to address that problem. This works for all whip spells.
Trigger:
(Trigger not shown due to post text limit.)