scope CrazyIvan initializer init
globals
private constant integer SPELL_ID = 'A000' //The raw code of the triggering ability
private constant string TICK_SOUND_FX = "Sound\\Interface\\BattleNetTick.wav" //the sound effect that plays when the timer ticks, use "" if you want no sound.
private constant string EXPLODING_EFFECT = "ExplosionBIG.mdx" //The effect that is created when the unit explodes
private constant string BURNING_EFFECT = "Environment\\LargeBuildingFire\\LargeBuildingFire2.mdl" //The burning effect on the bombed target.
private constant string SEC_EXPLODING_EFFECT = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl" //The effect that is created when the unit explodes
private constant boolean EXPLODE_UNIT = true //Whether to make the unit explode, doing so will leave no corpse
private constant boolean BOMB_KILL_TARGET = true //Whether to kill the bombed target or just deal damage to it.
private constant boolean DO_AIRBORN = true //Whether to throw all damaged units in the air after the explosion
private constant boolean SHOW_TIME = true //Whether to show the time left before the unit explodes above it to all the players
private constant boolean GET_CREDITS = true //Whether to get credits when the unit explodes giving you experience and bounty
private constant boolean BOMB_CONTINUE = true //Whether to still explode the unit even if it dies
private constant boolean BOMB_PAUSE = false //Whether to pause the target unit on the effect
private constant boolean BOMB_INVULNERABLE = false //Whether to make the target invulnerable on the effect.
private constant real TICK_INTERVAL = .0335 //The interval of the ticking timer. Doesn't do much but only checks numbers and set the vertex colors and textag
private constant real TEXT_SIZE = .0239 //The size of the floating text above the target unit. Adjust it sensitively
private constant real AIRBORN_BASE_TIME = 2.3 //Units further away last less on air, the real time isn't the base time, it's just a base value.
private constant real AIRBORN_BASE_DIST = 570 //The airborn distance, units further away go in a decreased distance
private constant real BASE_DAMAGE = 150 //The base damage of the aoe
private constant real INCREMENT_DAMAGE = 150 //The increment damage per level
private constant real BASE_TIME = 12 //The base amount of time in seconds after the unit explodes
private constant real DECREMENT_TIME = 3 //The decrement time in second per level of the spell (Level 1 = 12, Level 2 = 9, Level 3 = 6) and so on...
private constant real BASE_RADIUS = 300 //The base area of effect of the explosion
private constant real INCREMENT_RADIUS = 100 //The increment area of effect per level for the explosion
private constant attacktype ATK_TYPE = ATTACK_TYPE_CHAOS //The attack type of the damage dealt to nearby enemy untis
private constant damagetype DMG_TYPE = DAMAGE_TYPE_DEMOLITION //The damage type of the damage dealt to nearby enemy units
private constant weapontype WEP_TYPE = WEAPON_TYPE_WHOKNOWS //The weapon type of the damage dealt to nearby enemy units
endglobals
globals
private constant boolean AFFECT_MAGIC_IMMUNE = false
private constant boolean AFFECT_INVISIBLE = true
private constant boolean AFFECT_STRUCTURES = false
private constant boolean AFFECT_HEROES = true
private constant boolean AFFECT_ALLIES = false
private constant boolean AFFECT_ANCIENTS = false
private constant boolean AFFECT_FLYING = false
//This is the filter for the damage and airborn of the units that are close to the bombed target.
//I think that affecting flying units on airborn wouldn't be quite a good idea.
endglobals
//===========================KHANT TOUCH DIS=========================================////
globals
private unit TempUnit
private unit TempTarget
private real TempDamage
private constant group TempGroup = CreateGroup ( )
endglobals
private function JumpParabola takes real d, real md, real c returns real
local real t = (d*2) / md-1
return (-t*t+1) * (md/c)
endfunction
private function Shift takes real r returns real
if (r < 120) then
return 1.25
elseif (r < 300) and (r > 119) then
return 1.5
elseif (r < 450) and (r > 299) then
return 1.75
elseif r > 449 then
return 2
endif
return 1.4
endfunction
private struct Airborn
unit u
real left
real g
real d
real c
real sin
real cos
real i = 0
real p
/////*
static real MIN_X
static real MIN_Y
static real MAX_X
static real MAX_Y
static location loc = Location(0, 0)
static location locx = Location(0, 0)
static timer tim = CreateTimer()
static thistype array arr
static integer Total = 0
/////*
static method create takes unit u, integer time, real x, real y, real c returns thistype
local thistype that = thistype.allocate()
local real tx = GetUnitX(u)
local real ty = GetUnitY(u)
local real dx = tx - x
local real dy = ty - y
local real a = Atan2(y - ty, x - tx)
local location loc1 = Location(tx, ty)
local location loc2 = Location(x, y)
set that.sin = Sin(a)
set that.cos = Cos(a)
set that.u = u
set that.left = time
set that.g = SquareRoot(dx * dx + dy * dy)
set that.d = that.g / time
set that.c = c
call MoveLocation(thistype.loc, tx, ty)
set that.p = (GetLocationZ(loc2) - GetLocationZ(loc1)) / time
call RemoveLocation(loc1)
call RemoveLocation(loc2)
set loc1 = null
set loc2 = null
call UnitAddAbility(u, 'Amrf')
call UnitRemoveAbility(u, 'Amrf')
call PauseUnit(u, true)
if thistype.Total == 0 then
call TimerStart(thistype.tim, .0305, true, function thistype.Execute)
endif
set thistype.Total = thistype.Total + 1
set thistype.arr[thistype.Total -1] = that
return that
endmethod
static method Execute takes nothing returns nothing
local thistype that
local integer i = 0
local real x
local real y
local real h
local rect r
local location l
loop
exitwhen i >= thistype.Total
set that = thistype.arr[i]
set x = GetUnitX(that.u) + (that.d) * that.cos
set y = GetUnitY(that.u) + (that.d) * that.sin
call MoveLocation(thistype.locx, x, y)
set h = JumpParabola(that.d * that.i, that.g, that.c) - (GetLocationZ(thistype.locx) - GetLocationZ(thistype.loc)) + that.p * that.i
if x < thistype.MAX_X and y < thistype.MAX_Y and x > thistype.MIN_X and y > thistype.MIN_Y then
call SetUnitX(that.u, x)
call SetUnitY(that.u, y)
endif
call SetUnitFlyHeight(that.u, h, 0)
if (that.i >= that.left) or (GetWidgetLife(that.u)<.405) then
call that.destroy()
set thistype.Total = thistype.Total - 1
set thistype.arr[i] = thistype.arr[thistype.Total]
else
set that.i = that.i + 1
endif
set i = i + 1
endloop
if thistype.Total == 0 then
call PauseTimer(thistype.tim)
endif
set l = null
endmethod
method onDestroy takes nothing returns nothing
call SetUnitFlyHeight(.u, 0, 0)
call PauseUnit(.u, false)
call DestroyEffect(AddSpecialEffectTarget("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl", .u, "origin"))
set .u = null
endmethod
static method Init takes nothing returns nothing
set thistype.MAX_X = GetRectMaxX(bj_mapInitialPlayableArea) - 64
set thistype.MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea) - 64
set thistype.MIN_X = GetRectMinX(bj_mapInitialPlayableArea) + 64
set thistype.MIN_Y = GetRectMinY(bj_mapInitialPlayableArea) + 64
endmethod
endstruct
private function filter takes nothing returns boolean
local unit u=GetFilterUnit()
local boolean array b
local real k
local real x
local real y
local real tx
local real ty
local real ang
local real dist
local real c = 1.2
if (GetWidgetLife(u)>.405) then
set b[0] = IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) == false
set b[1] = IsUnitType(u, UNIT_TYPE_STRUCTURE) == false
set b[2] = IsUnitType(u, UNIT_TYPE_HERO) == false
set b[3] = IsUnitType(u, UNIT_TYPE_ANCIENT) == false
set b[4] = IsUnitType(u, UNIT_TYPE_FLYING) == false
set b[5] = IsUnitVisible(u, GetOwningPlayer(TempUnit)) == true
set b[6] = IsUnitAlly(u, GetOwningPlayer(TempUnit)) == false
static if AFFECT_MAGIC_IMMUNE then
set b[0] = true
endif
static if AFFECT_STRUCTURES then
set b[1] = true
endif
static if AFFECT_HEROES then
set b[2] = true
endif
static if AFFECT_ANCIENTS then
set b[3] = true
endif
static if AFFECT_FLYING then
set b[4] = true
endif
static if AFFECT_INVISIBLES then
set b[5] = true
endif
static if AFFECT_ALLIES then
set b[6] = true
endif
if (b[1] and b[2] and b[3] and b[4] and b[5] and b[6]) then
call UnitDamageTarget(TempUnit,u , TempDamage, false, false, ATK_TYPE, DMG_TYPE, WEP_TYPE)
call DestroyEffect(AddSpecialEffectTarget( SEC_EXPLODING_EFFECT, u, "chest"))
static if (DO_AIRBORN) then
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set x = GetUnitX(TempTarget) - tx
set y = GetUnitY(TempTarget) - ty
set k = SquareRoot(x * x + y * y)
set dist = AIRBORN_BASE_DIST - (k * .6575)
set x = GetUnitX(TempTarget)
set y = GetUnitY(TempTarget)
set ang = bj_RADTODEG * Atan2(ty - y, tx - x)
set x = tx + dist * Cos(ang * bj_DEGTORAD)
set y = ty + dist * Sin(ang * bj_DEGTORAD)
set k = AIRBORN_BASE_TIME - c
if k < .20 then
set k = .2
endif
set k = k / .0305
call Airborn.create(u, R2I(k), x, y, c)
endif
endif
endif
set u=null
return false
endfunction
private struct boom
unit u
unit t
real red = 255
real time
real rate
real radius
real dmg
real tick = 0
sound snd
texttag txt
effect art
//
real x
real y
//
static integer max = 0
static thistype array arr
static timer tim = CreateTimer ( )
//
static method create takes unit u, unit t returns thistype
local thistype this = thistype.allocate ( )
local integer l = GetUnitAbilityLevel(u, SPELL_ID)
set this.u = u
set this.t = t
set this.time = (BASE_TIME + DECREMENT_TIME) - (DECREMENT_TIME * I2R(l))
set this.rate = (this.red / this.time) * TICK_INTERVAL
set this.radius = (BASE_RADIUS-INCREMENT_RADIUS) + (INCREMENT_RADIUS *I2R(l))
set this.dmg = (BASE_DAMAGE - INCREMENT_DAMAGE) + (INCREMENT_DAMAGE * I2R(l))
static if BOMB_PAUSE then
call PauseUnit(this.u, true)
endif
static if BOMB_INVULNERABLE then
call SetUnitInvulnerable(this.u, true)
endif
set this.txt = CreateTextTag()
set this.art = AddSpecialEffectTarget(BURNING_EFFECT, this.t, "overhead")
call SetTextTagText(this.txt, I2S(R2I(this.time)), TEXT_SIZE)
call SetTextTagPermanent(this.txt, false)
call SetTextTagLifespan(this.txt, this.time-.15)
static if SHOW_TIME then
call SetTextTagVisibility(this.txt, true)
else
call SetTextTagVisibility(this.txt, false)
if GetLocalPlayer() == GetOwningPlayer(this.u) then
call SetTextTagVisibility(this.txt, true)
endif
endif
if thistype.max == 0 then
call TimerStart(thistype.tim, TICK_INTERVAL, true, function thistype.Exec)
endif
set thistype.max = thistype.max + 1
set thistype.arr[thistype.max - 1] = this
return this
endmethod
method explode takes nothing returns nothing
set TempUnit=.u
set TempDamage=.dmg
set TempTarget = .t
call GroupEnumUnitsInRange(TempGroup, .x, .y, this.radius,Condition(function filter))
call DestroyEffect(AddSpecialEffectTarget(EXPLODING_EFFECT, .t, "chest"))
static if EXPLODE_UNIT then
call SetUnitExploded(.t, true)
endif
static if GET_CREDITS then
static if BOMB_INVULNERABLE then
call SetUnitInvulnerable(.t, false)
endif
call UnitDamageTarget(.u, .t, 9999999, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DIVINE, WEAPON_TYPE_WHOKNOWS)
else
static if BOMB_KILL_TARGET then
call KillUnit(.t)
endif
endif
call DestroyEffect(.art)
call .destroy()
endmethod
static method Exec takes nothing returns nothing
local thistype this
local integer i = 0
loop
exitwhen i>= thistype.max
set this = thistype.arr[i]
set this.time = this.time - TICK_INTERVAL
set this.tick = this.tick + TICK_INTERVAL
if this.tick > .9 then
if TICK_SOUND_FX != "" then
set this.snd = CreateSound(TICK_SOUND_FX, false, true, true, 12710, 12710, "")
call AttachSoundToUnit(this.snd, this.t)
call StartSound(this.snd)
call KillSoundWhenDone(this.snd)
set this.snd = null
set this.tick = 0
endif
endif
if GetWidgetLife(this.t)<.405 then
call SetUnitVertexColor(this.t, 255, 255, 255, 255)
static if BOMB_CONTINUE then
call this.explode()
else
set thistype.max = thistype.max - 1
set thistype.arr[i] = thistype.arr[thistype.max]
call this.destroy()
endif
else
set this.x = GetUnitX(this.t)
set this.y = GetUnitY(this.t)
call SetTextTagPosUnit(this.txt, this.t, 0)
if this.time < 1 then
call SetTextTagText(this.txt, "!!", TEXT_SIZE+.0085)
else
call SetTextTagText(this.txt, I2S(R2I(this.time)), TEXT_SIZE)
endif
set this.red = this.red - this.rate
call SetUnitVertexColor(this.t, 255, R2I(this.red), R2I(this.red), 255)
call SetTextTagColor(this.txt, 255, R2I(this.red), R2I(this.red), 255)
endif
if this.time < TICK_INTERVAL then
call this.explode()
set thistype.max = thistype.max - 1
set thistype.arr[i] = thistype.arr[thistype.max]
endif
set i = i + 1
endloop
if thistype.max == 0 then
call PauseTimer(thistype.tim)
endif
endmethod
method onDestroy takes nothing returns nothing
call DestroyTextTag(.txt)
set .txt = null
set .u = null
set .art = null
set .t = null
endmethod
endstruct
///=======================IRRELEVANT===================================================////
private function Bomb takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call boom.create(GetTriggerUnit(), GetSpellTargetUnit())
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger ( )
call TriggerAddCondition(t, Condition(function Bomb))
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
set t = null
call Airborn.Init()
endfunction
endscope