// by Dalvengyr @Hiveworkshop.com
// *******************************************************
// *
// Arcane Glaive v1.2 *
// *
// Description *
// Causes Spell Breaker's attacks to deal no physical *
// damage. Arcane Glaive will burn mana on hit and deals *
// 4x of burned mana as magic damage to units within 175 *
// AoE. The glaive will bounce 2 times before expired. *
// *
// Requirement(s) *
// - None *
// *
// How to import *
// - Copy this trigger folder into your map *
// - Copy dummy unit & spell into your map *
// - Configure the spell *
// *
// Credits: *
// - ArcaneNova.mdx by dhguardianes *
// - ArcaneGlaive.mdx by Infrisios *
// - BloodElfSpellThief.blp by shockwave *
// - BTNTranquilityStar by QuadraDowN *
// *
// *******************************************************
// Configurations
// Main spell's rawcode
constant function AGL__SpellID takes nothing returns integer
return 'A000'
endfunction
// Dummy unit's rawcode
constant function AGL__DummyID takes nothing returns integer
return 'h000'
endfunction
// Spell's periodic interval
constant function AGL__Accuracy takes nothing returns real
return 0.03125
endfunction
// Created sfx on hit
constant function AGL__HitSfx takes nothing returns string
return "war3mapImported\\Arcane Nova.mdx"
endfunction
// Sfx attachment point
constant function AGL__HitSfxPt takes nothing returns string
return "origin"
endfunction
// Missile's model filepath
constant function AGL__MissileModel takes nothing returns string
return "war3mapImported\\ArcaneGlaive_2.mdx"
endfunction
// Missile's scale
constant function AGL__MissileSize takes nothing returns real
return 1.0
endfunction
// Missile's moverate
constant function AGL__MissileSpeed takes nothing returns real
return 25.0
endfunction
// Missile's turn rate (in radian)
constant function AGL__MissileTurnRate takes nothing returns real
return 10.75 * bj_DEGTORAD
endfunction
// Missile will be expired if does not hit anything after
// this duration
constant function AGL__MissileLifespan takes nothing returns real
return 3.0
endfunction
// Launch z
constant function AGL__NormalZ takes nothing returns real
return 66.0
endfunction
// Hit range
constant function AGL__HitRadius takes nothing returns real
return 50.0
endfunction
// Missile decay time
constant function AGL__SfxDecayTime takes nothing returns real
return 3.0
endfunction
// Target search radius (minimun range)
constant function AGL__SearchRadiusMin takes nothing returns real
return 175.0
endfunction
// Target search radius (maximum range)
constant function AGL__SearchRadiusMax takes nothing returns real
return 600.0
endfunction
// Each unit can only be targeted once
constant function AGL__TargetsOnce takes nothing returns boolean
return false
endfunction
// If true, glaive will removed if caster is dead/removed
constant function AGL__RemoveOnDead takes nothing returns boolean
return false
endfunction
// Number of jumps for each level
constant function AGL__bounceCount takes integer level returns integer
return 2
endfunction
// Burned mana amount for each level
constant function AGL__manaBurn takes integer level returns real
return 5.0 * level
endfunction
// Dealt damage factor for each level
constant function AGL__damageFactor takes integer level returns real
return 4.0
endfunction
// AoE of damage for each level
constant function AGL__damageAoE takes integer level returns real
return 175.0
endfunction
// Attack type of dealt damage
constant function AGL__AttackType takes nothing returns attacktype
return ATTACK_TYPE_NORMAL
endfunction
// Damage type of dealt damage
constant function AGL__DamageType takes nothing returns damagetype
return DAMAGE_TYPE_NORMAL
endfunction
// Target classification
constant function AGL__filterTarget takes unit u returns boolean
return not(IsUnitType(u, UNIT_TYPE_FLYING) or IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) or IsUnitType(u, UNIT_TYPE_MECHANICAL) or IsUnitType(u, UNIT_TYPE_STRUCTURE))
endfunction
// ===========================================================================
constant function AGL__isInBound takes real x, real y returns boolean
return x < udg_AGL__MapBounds[0] and x > udg_AGL__MapBounds[1] and y < udg_AGL__MapBounds[2] and y > udg_AGL__MapBounds[3]
endfunction
function AGL__deindex takes integer i returns integer
if AGL__TargetsOnce() then
call DestroyGroup(udg_AGL__Group[i])
endif
call DestroyEffect(udg_AGL__MissileSfx[i])
call UnitApplyTimedLife(udg_AGL__Missile[i], 'BTLF', AGL__SfxDecayTime())
set udg_AGL__Caster[i] = udg_AGL__Caster[udg_AGL__Counter]
set udg_AGL__Target[i] = udg_AGL__Target[udg_AGL__Counter]
set udg_AGL__Player[i] = udg_AGL__Player[udg_AGL__Counter]
set udg_AGL__Damage[i] = udg_AGL__Damage[udg_AGL__Counter]
set udg_AGL__Bounce[i] = udg_AGL__Bounce[udg_AGL__Counter]
set udg_AGL__Mana[i] = udg_AGL__Mana[udg_AGL__Counter]
set udg_AGL__Lifespan[i] = udg_AGL__Lifespan[udg_AGL__Counter]
set udg_AGL__MissileX[i] = udg_AGL__MissileX[udg_AGL__Counter]
set udg_AGL__AoE[i] = udg_AGL__AoE[udg_AGL__Counter]
set udg_AGL__MissileY[i] = udg_AGL__MissileY[udg_AGL__Counter]
set udg_AGL__Missile[i] = udg_AGL__Missile[udg_AGL__Counter]
set udg_AGL__MissileFace[i] = udg_AGL__MissileFace[udg_AGL__Counter]
set udg_AGL__MissileSfx[i] = udg_AGL__MissileSfx[udg_AGL__Counter]
if AGL__TargetsOnce() then
set udg_AGL__Group[i] = udg_AGL__Group[udg_AGL__Counter]
set udg_AGL__Group[udg_AGL__Counter] = null
endif
set udg_AGL__MissileSfx[udg_AGL__Counter] = null
set udg_AGL__Missile[udg_AGL__Counter] = null
set udg_AGL__Caster[udg_AGL__Counter] = null
set udg_AGL__Target[udg_AGL__Counter] = null
set udg_AGL__Counter = udg_AGL__Counter - 1
if udg_AGL__Counter < 1 then
call PauseTimer(udg_AGL__Timer)
else
return i - 1
endif
return i
endfunction
// Get new target with lowest mana around previous target
function AGL__getTarget takes integer i returns unit
local real m = -1
local unit u
local real x
local real y
call GroupEnumUnitsInRange(udg_AGL__TempGroup, udg_AGL__MissileX[i], udg_AGL__MissileY[i], AGL__SearchRadiusMax(), null)
loop
set u = FirstOfGroup(udg_AGL__TempGroup)
exitwhen u == null
call GroupRemoveUnit(udg_AGL__TempGroup, u)
if u != udg_AGL__Target[i] and not IsUnitInGroup(u, udg_AGL__Group[i]) and AGL__filterTarget(u) then
if IsPlayerAlly(GetOwningPlayer(u), udg_AGL__Player[i]) and GetUnitState(u, UNIT_STATE_MANA) > m then
set x = GetUnitX(u)
set y = GetUnitY(u)
if (udg_AGL__MissileX[i] - x)*(udg_AGL__MissileX[i] - x)+(udg_AGL__MissileY[i] - y)*(udg_AGL__MissileY[i] - y) > AGL__SearchRadiusMin() * AGL__SearchRadiusMin() then
if not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0 then
set udg_AGL__TempUnit = u
set m = GetUnitState(u, UNIT_STATE_MANA)
endif
endif
endif
endif
endloop
return udg_AGL__TempUnit
endfunction
function AGL__onTick takes nothing returns nothing
local integer i = 1
local real a
local real x
local real y
local real m
loop
exitwhen i > udg_AGL__Counter
if udg_AGL__Lifespan[i] > AGL__Accuracy() and (not IsUnitType(udg_AGL__Caster[i], UNIT_TYPE_DEAD) and GetUnitTypeId(udg_AGL__Caster[i]) != 0 or not AGL__RemoveOnDead()) then
set udg_AGL__Lifespan[i] = udg_AGL__Lifespan[i] - AGL__Accuracy()
set x = GetUnitX(udg_AGL__Target[i])
set y = GetUnitY(udg_AGL__Target[i])
set a = Atan2(y - udg_AGL__MissileY[i], x - udg_AGL__MissileX[i])
// Adjust missile's facing angle to the target
if Cos(udg_AGL__MissileFace[i] - a) < Cos(AGL__MissileTurnRate()) then
if Sin(a - udg_AGL__MissileFace[i]) >= 0 then
set udg_AGL__MissileFace[i] = udg_AGL__MissileFace[i] + AGL__MissileTurnRate()
else
set udg_AGL__MissileFace[i] = udg_AGL__MissileFace[i] - AGL__MissileTurnRate()
endif
else
set udg_AGL__MissileFace[i] = a
endif
// Set the target loc coordinate
set udg_AGL__MissileX[i] = udg_AGL__MissileX[i] + AGL__MissileSpeed() * Cos(udg_AGL__MissileFace[i])
set udg_AGL__MissileY[i] = udg_AGL__MissileY[i] + AGL__MissileSpeed() * Sin(udg_AGL__MissileFace[i])
if AGL__isInBound(udg_AGL__MissileX[i], udg_AGL__MissileY[i]) then
if (udg_AGL__MissileX[i] - x)*(udg_AGL__MissileX[i] - x)+(udg_AGL__MissileY[i] - y)*(udg_AGL__MissileY[i] - y) < AGL__HitRadius() * AGL__HitRadius() then
// Decrease mana
set m = GetUnitState(udg_AGL__Target[i], UNIT_STATE_MANA)
call SetUnitState(udg_AGL__Target[i], UNIT_STATE_MANA, m - udg_AGL__Mana[i])
set m = m - GetUnitState(udg_AGL__Target[i], UNIT_STATE_MANA)
call DestroyEffect(AddSpecialEffectTarget(AGL__HitSfx(), udg_AGL__Target[i], AGL__HitSfxPt()))
// Deal damage to all units around the target
call GroupEnumUnitsInRange(udg_AGL__TempGroup, udg_AGL__MissileX[i], udg_AGL__MissileY[i], udg_AGL__AoE[i], null)
loop
set udg_AGL__TempUnit = FirstOfGroup(udg_AGL__TempGroup)
exitwhen udg_AGL__TempUnit == null
call GroupRemoveUnit(udg_AGL__TempGroup, udg_AGL__TempUnit)
if IsPlayerAlly(GetOwningPlayer(udg_AGL__TempUnit), udg_AGL__Player[i]) and not IsUnitType(udg_AGL__TempUnit, UNIT_TYPE_DEAD) and GetUnitTypeId(udg_AGL__TempUnit) != 0 then
if AGL__filterTarget(udg_AGL__TempUnit) then
call UnitDamageTarget(udg_AGL__Caster[i], udg_AGL__TempUnit, m, false, false, AGL__AttackType(), AGL__DamageType(), null)
endif
endif
endloop
// Get next target if still can bounce
if udg_AGL__Bounce[i] > 0 then
set udg_AGL__Bounce[i] = udg_AGL__Bounce[i] - 1
if AGL__TargetsOnce() then
call GroupAddUnit(udg_AGL__Group[i], udg_AGL__Target[i])
endif
set udg_AGL__Target[i] = AGL__getTarget(i)
if udg_AGL__Target[i] == null then
set i = AGL__deindex(i)
else
set udg_AGL__Lifespan[i] = AGL__MissileLifespan()
endif
else
set i = AGL__deindex(i)
endif
else
call SetUnitX(udg_AGL__Missile[i], udg_AGL__MissileX[i])
call SetUnitY(udg_AGL__Missile[i], udg_AGL__MissileY[i])
endif
else
set i = AGL__deindex(i)
endif
else
set i = AGL__deindex(i)
endif
set i = i + 1
endloop
endfunction
function AGL__onCast takes nothing returns boolean
local integer l
if GetSpellAbilityId() == AGL__SpellID() and AGL__filterTarget(GetSpellTargetUnit()) then
set udg_AGL__Counter = udg_AGL__Counter + 1
set udg_AGL__Caster[udg_AGL__Counter] = GetTriggerUnit()
set udg_AGL__Target[udg_AGL__Counter] = GetSpellTargetUnit()
set udg_AGL__Player[udg_AGL__Counter] = GetOwningPlayer(udg_AGL__Target[udg_AGL__Counter])
set l = GetUnitAbilityLevel(udg_AGL__Caster[udg_AGL__Counter], AGL__SpellID())
set udg_AGL__Lifespan[udg_AGL__Counter] = AGL__MissileLifespan()
set udg_AGL__Damage[udg_AGL__Counter] = AGL__damageFactor(l)
set udg_AGL__Bounce[udg_AGL__Counter] = AGL__bounceCount(l)
set udg_AGL__Mana[udg_AGL__Counter] = AGL__manaBurn(l)
set udg_AGL__AoE[udg_AGL__Counter] = AGL__damageAoE(l)
set udg_AGL__MissileX[udg_AGL__Counter] = GetUnitX(udg_AGL__Caster[udg_AGL__Counter])
set udg_AGL__MissileY[udg_AGL__Counter] = GetUnitY(udg_AGL__Caster[udg_AGL__Counter])
set udg_AGL__MissileFace[udg_AGL__Counter] = Atan2(GetUnitY(udg_AGL__Target[udg_AGL__Counter]) - udg_AGL__MissileY[udg_AGL__Counter], GetUnitX(udg_AGL__Target[udg_AGL__Counter]) - udg_AGL__MissileX[udg_AGL__Counter])
set udg_AGL__Missile[udg_AGL__Counter] = CreateUnit(GetTriggerPlayer(), AGL__DummyID(), udg_AGL__MissileX[udg_AGL__Counter], udg_AGL__MissileY[udg_AGL__Counter], udg_AGL__MissileFace[udg_AGL__Counter] * bj_RADTODEG)
set udg_AGL__MissileSfx[udg_AGL__Counter] = AddSpecialEffectTarget(AGL__MissileModel(), udg_AGL__Missile[udg_AGL__Counter], "origin")
if AGL__TargetsOnce() then
set udg_AGL__Group[udg_AGL__Counter] = CreateGroup()
endif
if UnitAddAbility(udg_AGL__Missile[udg_AGL__Counter], 'Amrf') and UnitRemoveAbility(udg_AGL__Missile[udg_AGL__Counter], 'Amrf') then
endif
call SetUnitFlyHeight(udg_AGL__Missile[udg_AGL__Counter], AGL__NormalZ(), 0)
call SetUnitScale(udg_AGL__Missile[udg_AGL__Counter], AGL__MissileSize(), 1, 1)
if udg_AGL__Counter == 1 then
call TimerStart(udg_AGL__Timer, AGL__Accuracy(), true, function AGL__onTick)
endif
endif
return false
endfunction
function InitTrig_ArcaneGlaive takes nothing returns nothing
// Initialize map bounds
set udg_AGL__MapBounds[0] = GetRectMaxX(bj_mapInitialPlayableArea)
set udg_AGL__MapBounds[1] = GetRectMinX(bj_mapInitialPlayableArea)
set udg_AGL__MapBounds[2] = GetRectMaxY(bj_mapInitialPlayableArea)
set udg_AGL__MapBounds[3] = GetRectMinY(bj_mapInitialPlayableArea)
set gg_trg_ArcaneGlaive = CreateTrigger()
set udg_AGL__TempGroup = CreateGroup()
set udg_AGL__Timer = CreateTimer()
call TriggerRegisterAnyUnitEventBJ(gg_trg_ArcaneGlaive, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_ArcaneGlaive, Condition(function AGL__onCast))
endfunction