//====================================
//Malice Orb v1.01 by watermelon_1234
//====================================
//Required Libraries: TimerUtils, GroupUtils, and xe system (xebasic & xefx)
//====================================
//Copy Object Editor Data:
//-Malice Orb ability
//====================================
scope MaliceOrb
native UnitAlive takes unit id returns boolean //Remove this line if it's already implemented
//*********************************
//Settings
//*********************************
globals
private constant integer SPELL_ID = 'A000' //The raw id of the Malice Orb ability
private constant string SPELL_ORDER = "darkritual" //The order string of the Malice Orb ability. Must be the Base Order Id, not Order String!
private constant real TIMER_LOOP = 0.1 //How many times the timer will loop. High values aren't recommended for this spell
private constant string ORB_SFX = "Abilities\\Spells\\Undead\\AntiMagicShell\\AntiMagicShell.mdl" //The SFX for the orb
private constant real ORB_HEIGHT = 200. //What height the orb should be at upon creation
private constant string BURST_SFX = "Objects\\Spawnmodels\\Undead\\UDeathSmall\\UDeathSmall.mdl" //The explosion SFX when the spell stops
private constant real BURST_HEIGHT = 150. //Height of the burst sfx
private constant string GROW_SFX = "Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl" //A little SFX shown periodically to show the orb is growing
private constant real GROW_HEIGHT = 150. //Height of the grow sfx
private constant real GROW_SFX_INT = 1.5 //The interval when the GROWSFX will be played
private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL //Attack type of the damage
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL //Damage type of the damage
private constant weapontype WPN_TYPE = null //Weapon type of the damage
private constant boolean DO_PRELOADING = true //Determine whether or not to preload the ghost unit and the special effects.
endglobals
//The Channel duration of the spell. Make sure it matches the duration put in the spell.
private function ChannelDuration takes integer lvl returns real
return 6.
endfunction
//The initial size of the orb.
private function InitScale takes integer lvl returns real
return 0.5+0.5*lvl
endfunction
//How much the orb should increase in size per second.
private function ScaleIncrement takes integer lvl returns real
return .1
endfunction
private function InitArea takes integer lvl returns real
//The initial area of the spell
return 100.+50*lvl
endfunction
private function AreaIncrement takes integer lvl returns real
//How much the area will increase per second.
return 5.
endfunction
private function InitDamage takes integer lvl returns real
//The amount of damage done without any consideration of DamageIncrement
return 75. + 25*lvl
endfunction
private function DamageIncrement takes integer lvl returns real
//How much the damage should increase by every second
return 5. + 10*lvl
endfunction
//Settings for the texttag that will display the total amount of damage dealt
private function MakeTextTag takes unit cast,real dmg returns nothing
set bj_lastCreatedTextTag = CreateTextTag() //Used a global variable since I'm lazy. :P
call SetTextTagPosUnit(bj_lastCreatedTextTag,cast,-15)
call SetTextTagText(bj_lastCreatedTextTag,"-"+I2S(R2I(dmg))+"!",0.0312)
call SetTextTagColor(bj_lastCreatedTextTag,0,255,0,255)
call SetTextTagVelocity(bj_lastCreatedTextTag,0,0.04)
call SetTextTagFadepoint(bj_lastCreatedTextTag,0.65)
call SetTextTagLifespan(bj_lastCreatedTextTag,1)
call SetTextTagPermanent(bj_lastCreatedTextTag,false)
endfunction
//***************************************************************************************************
//Actual coding of the spell below here
//***************************************************************************************************
globals
private boolexpr e
endglobals
private struct Data
unit cast
xefx orb
integer lvl
real count = 0 //This variable counts how long the spell has been channeled
real growCount = 0 //A separate variable is needed to know when to play the GROWSFX
real dmg
real totalDmg = 0
timer t
private static thistype temp
static method create takes unit c, real x, real y returns thistype
local thistype this = thistype.allocate()
set .cast = c
set .lvl = GetUnitAbilityLevel(.cast,SPELL_ID)
set .orb = xefx.create(x,y,0)
set .orb.z = ORB_HEIGHT
set .orb.fxpath = ORB_SFX
set .orb.scale = InitScale(.lvl)
set .t = NewTimer()
call SetTimerData(.t,this)
call TimerStart(.t,TIMER_LOOP,true,function thistype.onLoop)
return this
endmethod
//Chooses the targets that will be affected and deals damage. Also adds the damage done
static method filter takes nothing returns boolean
local unit u = GetFilterUnit()
local real life = GetWidgetLife(u)
if UnitAlive(u) and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(u,GetOwningPlayer(temp.cast)) then
call UnitDamageTarget(temp.cast,u,temp.dmg,false,true,ATK_TYPE,DMG_TYPE,WPN_TYPE)
set temp.totalDmg = temp.totalDmg + life - GetWidgetLife(u)
endif
set u = null
return false
endmethod
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local xefx sfx //Plays an sfx where the orb is. Has the same height as the orb.
if GetUnitCurrentOrder(.cast) == OrderId(SPELL_ORDER) and .count <= ChannelDuration(.lvl) then
set .count = .count + TIMER_LOOP
set .growCount = .growCount + TIMER_LOOP
set .orb.scale = InitScale(.lvl) +ScaleIncrement(.lvl)*.count
if .growCount >= GROW_SFX_INT then //If the growCount is at least the GROW_SFX_INT, create a nice effect for the orb
set sfx = xefx.create(.orb.x,.orb.y,0)
set sfx.fxpath = GROW_SFX
set sfx.z = GROW_HEIGHT
set sfx.scale = InitScale(.lvl) +ScaleIncrement(.lvl)*.count
call sfx.destroy()
set .growCount = 0 //set it back to 0 to restart counting
endif
else
if .count > ChannelDuration(.lvl) then //This is just to prevent the spell from damaging higher than it should
set .count = ChannelDuration(.lvl)
endif
set sfx = xefx.create(.orb.x,.orb.y,0)
set sfx.fxpath = BURST_SFX
set sfx.z = BURST_HEIGHT
set sfx.scale = InitScale(.lvl) +ScaleIncrement(.lvl)*.count
call sfx.destroy()
set .dmg = InitDamage(.lvl)+DamageIncrement(.lvl)*.count
set temp = this
call GroupEnumUnitsInArea(ENUM_GROUP,.orb.x,.orb.y,InitArea(.lvl) + AreaIncrement(.lvl)*.count,e)
if .totalDmg > 0 then //This deals with the creation of a texttag that displays the total damage done if greater than 0.
call MakeTextTag(.cast,.totalDmg)
endif
call ReleaseTimer(.t)
call .orb.destroy() //Destroy the orb effect.
call .destroy()
endif
endmethod
static method spellActions takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
endif
return false
endmethod
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.spellActions))
static if DO_PRELOADING then
call Preload(ORB_SFX)
call Preload(BURST_SFX)
call Preload(GROW_SFX)
endif
set e = Filter(function thistype.filter)
endmethod
endstruct
endscope