/*
-------------Volty Crush 1.3 by Adiktuz---------------------------------
What is Volty Crush?
-By default its an ability which inflicts the target with a magical curse which explodes after a set time
damaging the target, and any unit near it. Also deals damage to the target during the duration
Requirements
-Warcraft 3 - TFT (1.24e and up)
-NewGenWE (comes with the JNGP)
-Jass Helper (0.A.2.A or 0.A.2.B)
-Basic editor knowledge
How to import
-Open your map in NewGenWE
-Create a new trigger and convert it to custom script
-Copy this library and paste it into the new trigger, replacing anything inside that trigger
-Modify the modifiable globals and functions to fit the new map or settings that you would like
-You can copy the ability or just make a new one
-Copy the buff ability and the buff and modify the rawcodes here (if you would create a new buff, Use slow aura as a base for the buff ability)
How to use
-Once you have done the steps on importing, you're good to go.
Credits:
-Vexorian For TimerUtils
-Bribe for Table
*/
library VoltyCrush requires TimerUtils, Table
globals
//Rawcode of the spell
private constant integer SPELL_ID = 'A000'
//Rawcode of the buff ability
private constant integer BUFF_SPELL_ID = 'A001'
//Rawcode of the buff itself
private constant integer BUFF_ID = 'B000'
//The path to the special effect which is shown on the unit every tick
private constant string TICK_FX = "Abilities\\Spells\\Items\\AIlb\\AIlbSpecialArt.mdl"
//The path to the special effect when the ability explodes
private constant string EXPLODE_FX = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"
//The path to the special effect shown on the units that will get hit by the explosiong
private constant string TARGET_FX = "Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl"
//The attachment point of TICK_FX
private constant string TICK_AP = "chest"
//The attachment point of EXPLODE_FX
private constant string EXPLODE_AP = "origin"
//The attachment point of TARGET_FX
private constant string TARGET_AP = "chest"
//this is the timer interval in which duration damage takes place
private constant real TICK = 0.50
//The attack type of the spell
private constant attacktype AT = ATTACK_TYPE_MAGIC
//The damage type of the spell
private constant damagetype DT = DAMAGE_TYPE_MAGIC
//Checks whether the curses will explode upon death of target or just fade away
private constant boolean ExUponDeath = false
private Table VoltyTab
endglobals
//Use this function to calculate the duration of the curse before it explodes
private function GetTime takes integer level returns real
return level + 1.00
endfunction
//Use this function to get the explosion radius
private function GetRadius takes integer level returns real
return 100.00 + level*(25)
endfunction
//Use this function to calculate the damage per tick
private function GetTickDamage takes integer level returns real
return 10*TICK
endfunction
//Use this function to calculate the explosion damage to the caster
private function GetExploTargetDamage takes integer level returns real
return level*75.00
endfunction
//Use this function to calculate the explosion damage to the units around the target
private function GetExploAoeDamage takes integer level returns real
return level*75.00
endfunction
//The initialization module... I used a module for init because Nestharus said that its better
private module init
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
set i = i + 1
endloop
call TriggerAddCondition(t, Condition(function thistype.FilterFunction))
set VoltyTab = Table.create()
set t = null
endmethod
endmodule
//The struct for the spell
private struct VoltyStruct
//We implement the module here
implement init
//The struct data holders
static thistype data
static thistype datum
static thistype datab
static group dgroup = CreateGroup()
static unit triggerunit = null
unit target
unit caster
player owner
integer level
real time
//This method runs when a curse finishes and checks if the unit has other instances of the spell, if none it removes the buff
method RemoveBuff takes nothing returns nothing
if VoltyTab[GetHandleId(this.target)] == 0 then
/*I remove both the ability and the buff since if only the ability is removed,
the buff will stay for about 2 seconds...
*/
call UnitRemoveAbility(this.target, BUFF_SPELL_ID)
call UnitRemoveAbility(this.target, BUFF_ID)
endif
endmethod
//This method is for dealing the damage into the unit
method OnHit takes unit fu returns nothing
if fu != this.target then
call DestroyEffect(AddSpecialEffectTarget(TARGET_FX, fu, TARGET_AP))
call UnitDamageTarget(this.caster, fu, GetExploAoeDamage(this.level), false, false, AT, DT, null)
else
call UnitDamageTarget(this.caster, fu, GetExploTargetDamage(this.level), false, false, AT, DT, null)
endif
endmethod
//The filter for the group pick, I merged the actions to the group filter for better performance
static method OnHitFilter takes nothing returns boolean
local unit fu = GetFilterUnit()
if IsUnitEnemy(fu, datab.owner) and /*
*/ GetWidgetLife(fu) > .405 and /*
just for double checking...
*/ (not IsUnitType(fu, UNIT_TYPE_DEAD)) and /*
*/ not IsUnitType(fu, UNIT_TYPE_STRUCTURE) then
call datab.OnHit(fu)
endif
set fu = null
return false
endmethod
//the method run when the timer reaches 0.00 or when the target dies
method OnExplode takes boolean dead returns nothing
//This checks if the unit is dead and whether death is registered as a cause of explosion
if not (dead and not ExUponDeath) then
call GroupEnumUnitsInRange(thistype.dgroup, GetUnitX(this.target), GetUnitY(this.target), GetRadius(this.level), Condition(function thistype.OnHitFilter) )
call DestroyEffect(AddSpecialEffectTarget(EXPLODE_FX, this.target, EXPLODE_AP))
endif
endmethod
//The method which is run after each interval
private static method Refresh takes nothing returns nothing
local boolean dead = false
set datab = GetTimerData(GetExpiredTimer())
set datab.time = datab.time - TICK
set dead = GetWidgetLife(datab.target) < .405
call DestroyEffect(AddSpecialEffectTarget(TICK_FX, datab.target, TICK_AP))
call UnitDamageTarget(datab.caster, datab.target, GetTickDamage(datab.level), false, false, AT, DT, null)
if datab.time <= 0.00 or dead then
set VoltyTab[GetHandleId(datab.target)] = VoltyTab[GetHandleId(datab.target)] - 1
call datab.OnExplode(dead)
call datab.RemoveBuff()
call datab.destroy()
call ReleaseTimer(GetExpiredTimer())
endif
endmethod
//The method run when the spell VoltyCrush is used
private static method create takes unit caster, unit target, integer level , player owner returns thistype
local timer t = NewTimer()
set data = thistype.allocate()
set data.target = target
set data.level = level
set data.owner = owner
set data.caster = caster
set data.time = GetTime(level)
call UnitAddAbility(target, BUFF_SPELL_ID)
call SetTimerData(t, data)
call TimerStart(t, TICK, true, function thistype.Refresh)
set VoltyTab[GetHandleId(target)] = VoltyTab[GetHandleId(target)] + 1
set t = null
return data
endmethod
//The filter function used by the trigger, this also contains the actions for the spell
private static method FilterFunction takes nothing returns boolean
set thistype.triggerunit = GetTriggerUnit()
if GetSpellAbilityId() == SPELL_ID then
call thistype.create( thistype.triggerunit, GetSpellTargetUnit(), GetUnitAbilityLevel(thistype.triggerunit, SPELL_ID), GetOwningPlayer(thistype.triggerunit))
endif
return false
endmethod
endstruct
endlibrary