scope RealisticCharge initializer Start
globals
//!*******************************SETTINGS*************************!\\
//! general settings
private constant integer SpellID = 'A000'
//^The spell's rawcode
private constant real TimerInterval = 0.02
//^The interval used by the timer. I suggest leaving it at 0.02
private constant integer Speed = 30
//^The speed, with which the caster charges
private constant string ChargeSFX = "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl"
//^The SFX occuring every (TimerInterval) seconds at the caster's position
private constant string TargetSFX = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
//^The SFX created upon impact
private constant string CastAttach = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
//^The attachment created at the AttachPt
private constant string AttachPt = "weapon"
//^The point where the attachment is created.
private constant string CastAnim = "attack slam"
//^The animation played every "AnimInterval"
private constant real AnimInterval = 0.5
//^The Interval in which the animation is played.
private constant boolean PathingCheck = false
//^Determines whether the caster should stop, when he has reached an unpathable point
private constant boolean DestroyTrees = true
//^This sets if trees are destroyed.
private constant real TreesAoE = 200
//^Sets the radius in which trees are destroyed.
endglobals
private function Damage takes integer level returns integer
return (level*100)
//^The damage dealt upon impact. If you don't want the level to influence the damage,
//^simply return the wanted damage
endfunction
//!*******************************SETTINGS END*********************!\\
globals
private boolexpr b
endglobals
//Filter for leakless Event
private constant function NoLeakFilter takes nothing returns boolean
return true
endfunction
//The condition ;D
private function condition takes nothing returns boolean
return GetSpellAbilityId() == SpellID
endfunction
private function KillTrees takes nothing returns nothing
call KillDestructable(GetEnumDestructable())
endfunction
//Main Struct
private struct Spell
//Needed variables
private unit caster //The casting unit
private unit target //The target ;P
private real x //The X-Coordinate, which determines the new position of the caster
private real y //The Y-Coordinate, which determines the new position of the caster
private real x1 //The X-Coordinate, which determines the start position of the caster
private real y1 //The Y-Coordinate, which determines the start position of the caster
private real x2 //The X-Coordinate, which determines the position of the target
private real y2 //The Y-Coordinate, which determines the position of the target
private integer steps //This will be the number of single moves I have to do
private integer level //The level for damage calculation reasons
private integer curstep //The counter, which step is currently being run
private integer animstep//Tells the game when the animation should be played
private effect attach //The attachment
private boolean finished//Neccesary, for not damaging target, if unit stopped, because of unpathable terrain.
private rect r //Needed for tree destroy
//Neccesary struct variables
private static Spell array indx
private static integer counter = 0
private static timer time = CreateTimer()
//The "real" actions done every Interval
static method Execution takes nothing returns nothing
local Spell d
local integer i = 0
loop
exitwhen i >= Spell.counter
set d = Spell.indx[i]
//Counting the curstep one up, so it will stop when it has reached steps
set d.curstep = d.curstep + 1
if d.curstep < d.steps then
//Setting new X and Y to old X/Y + (distance between cast and target X/Y) / steps
set d.x = d.x + ((d.x2 - d.x1) / d.steps)
set d.y = d.y + ((d.y2 - d.y1) / d.steps)
if DestroyTrees == true then
set d.r = Rect(d.x - TreesAoE, d.y - TreesAoE, d.x + TreesAoE, d.y + TreesAoE)
call EnumDestructablesInRect(d.r, b, function KillTrees)
call RemoveRect(d.r)
set d.r = null
endif
if PathingCheck == true then
if IsTerrainPathable(d.x, d.y, PATHING_TYPE_WALKABILITY) == false then
call SetUnitX(d.caster, d.x)
call SetUnitY(d.caster, d.y)
//SFX
call DestroyEffect(AddSpecialEffect(ChargeSFX, d.x, d.y))
if d.animstep < R2I(AnimInterval / TimerInterval) then
set d.animstep = d.animstep + 1
else
set d.animstep = 0
call SetUnitAnimation(d.caster, CastAnim)
endif
else
set d.curstep = d.steps
set d.finished = false
endif
else
call SetUnitX(d.caster, d.x)
call SetUnitY(d.caster, d.y)
//SFX
call DestroyEffect(AddSpecialEffect(ChargeSFX, d.x, d.y))
if d.animstep < R2I(AnimInterval / TimerInterval) then
set d.animstep = d.animstep + 1
else
set d.animstep = 0
call SetUnitAnimation(d.caster, CastAnim)
endif
endif
else
//Damaging the target, unpausing units, giving them pathing
if d.finished == true then
call UnitDamageTarget(d.caster, d.target, Damage(d.level), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNKNOWN, null)
call DestroyEffect(AddSpecialEffect(TargetSFX, d.x, d.y))
endif
call SetUnitPathing(d.caster, true)
call PauseUnit(d.caster, false)
call PauseUnit(d.target, false)
call DestroyEffect(d.attach)
call SetUnitAnimation(d.caster, "walk")
//Struct actions
set d.curstep = 0
set d.animstep = 0
call d.destroy()
set d.counter = d.counter - 1
set d.indx[i] = d.indx[d.counter]
set i = i - 1
endif
set i = i + 1
endloop
set i = 0
//Pause Timer if no instance is running
if Spell.counter == 0 then
call PauseTimer(Spell.time)
endif
endmethod
//Setting variables at spellcast
static method Set takes unit c, unit t, integer l returns nothing
local Spell d = Spell.allocate()
//Obvious settings ;P
set d.caster = c
set d.target = t
set d.x1 = GetUnitX(c)
set d.y1 = GetUnitY(c)
set d.x2 = GetUnitX(t)
set d.y2 = GetUnitY(t)
set d.x = d.x1
set d.y = d.y1
set d.level = l
set d.finished = true //Standard settubg is true, so it damages the target.
//Now I calculate how many steps it takes, by dividing distance between start and end point through speed
set d.steps = R2I((SquareRoot((Pow((d.x2-d.x1), 2.00)+Pow((d.y2-d.y1), 2.00)))) / Speed)
//Pathing and Pausing
call SetUnitPathing(c, false)
call PauseUnit(c, true)
call PauseUnit(t, true)
call SetUnitFacing(c, bj_RADTODEG * Atan2(d.y2 - d.y1, d.x2 - d.x1))
call SetUnitAnimation(c, CastAnim)
set d.attach = AddSpecialEffectTarget(CastAttach, c, "weapon")
//Starting Timer if no instance is already running
if Spell.counter == 0 then
call TimerStart(Spell.time,TimerInterval,true, function Spell.Execution)
endif
set Spell.indx[Spell.counter] = d
set Spell.counter = Spell.counter + 1
endmethod
endstruct
//Function that initializes the actions
private function VInput takes nothing returns nothing
call Spell.Set(GetTriggerUnit(),GetSpellTargetUnit(),GetUnitAbilityLevel(GetTriggerUnit(),SpellID))
endfunction
private function Start takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local filterfunc ff = Filter(function NoLeakFilter)
//No leaks through TriggerRegisterAnyUnitEventBJ
loop
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, ff)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
set i = 0
call TriggerAddCondition(t, Condition(function condition))
call TriggerAddAction(t, function VInput)
call DestroyFilter(ff)
call Preload(ChargeSFX)
call Preload(TargetSFX)
call PreloadStart()
set ff = null
set b = Condition(function NoLeakFilter)
endfunction
endscope