library CallOfNature requires GroupUtils, T32, optional GT
// v 1.1.6
//////////////////////////////////////////////
// Credits:
// Spell made by 3yeballz
// please give credits if you use :-)
//
// GroupUtils by Rising Dusk
// T32 by Jesus4Lyf
// GTrigger by Jesus4Lyf
// model: Navi by Elenai
///////////////////////////////////////////////
/////////////////////////////////////////
/////////// User Configuration //////////
/////////////////////////////////////////
globals
private constant integer SPELLID = 'A000' // rawcode of spell
private constant integer UNITID = 'h000' // rawcode of wisp
private constant integer TREE1 = 'ATtr' // rawcode for trees where wisps can spawn
private constant integer TREE2 = 'ATtc'
// if you require more different trees go to line 218 and change it
private constant real RADIUS = 400. // radius spell cast
private constant real RADIUSC = 80. // radius detonation init
private constant real RADIUSDMG = 150. // radius detonation damage
private constant integer ARRAYSIZE = 200 // -> 40 instances allowed
private constant integer ENDCOUNT = 650 // wisps will move outside the map after x callbacks
private constant integer MAXCOUNT = 1500 // should be much bigger than ENDCOUNT. after x callbacks struct will be destroyed (if it's not destroyed yet)
private constant real HEIGHT = 60 // fly height of wisps
////////////////////////////////////
// constants for flight route //////
////////////////////////////////////
private constant real ACCELERATION = 0.5 //default: 0.5
private constant real ACCURACY = 0.002 //default: 0.002
private constant real INERTIA = 0.06 //default: 0.06, the lower the stronger is inertia, affects acceleration as well
private constant real TOLERANCE = 1.27 //default: 1.27
private constant real MAXSPEED = 30 //default: 30
private constant real STARTSPEED = 25 //default: 25
private attacktype atktype = ATTACK_TYPE_NORMAL
private damagetype dmgtype = DAMAGE_TYPE_MAGIC
private weapontype wpntype = null
endglobals
private function GetDamageAmount takes integer level returns real
return 25. + 25. * level
endfunction
/////////////////////////////////////////
/////// End of User Configuration ///////
/////////////////////////////////////////
private module WispInit
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
static if LIBRARY_GT then
call TriggerAddCondition(GT_RegisterStartsEffectEvent(t, SPELLID), Filter(function thistype.SpellCast))
else
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Filter(function thistype.SpellCast))
endif
set t = null
set check = Filter(function thistype.CheckForEnemy)
set filter = Filter(function thistype.DealDamage)
set filt = Filter(function thistype.Init)
set mapmaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set mapmaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set mapminX = GetRectMinX(bj_mapInitialPlayableArea)
set mapminY = GetRectMinY(bj_mapInitialPlayableArea)
set rct = Rect(-RADIUS,-RADIUS,RADIUS,RADIUS)
endmethod
endmodule
private struct Wisp
private static thistype temp
private static boolean explode = false
private static boolexpr filter
private static boolexpr filt
private static boolexpr check
private static real mapmaxX
private static real mapmaxY
private static real mapminX
private static real mapminY
private static real px
private static real py
private static rect rct
private unit caster
private player owner
private real x
private real y
private integer i
private integer count
private integer timecount
private integer level
private real damage
private unit array wisp [ARRAYSIZE]
private real array facing [ARRAYSIZE]
private real array speed [ARRAYSIZE]
private real array locx [ARRAYSIZE]
private real array locy [ARRAYSIZE]
static method DealDamage takes nothing returns boolean
local unit u = GetFilterUnit()
if IsUnitEnemy(u, temp.owner) and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and GetUnitAbilityLevel(u, 'Avul')==0 then
call UnitDamageTarget(temp.caster, u, temp.damage, false, false, atktype, dmgtype, wpntype)
endif
set u = null
return false
endmethod
static method CheckForEnemy takes nothing returns boolean
local unit u
if explode then
return false
endif
set u = GetFilterUnit()
if IsUnitEnemy(u, temp.owner) and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and GetUnitAbilityLevel(u, 'Avul')==0 then
set explode = true
endif
set u = null
return false
endmethod
private method ClearData takes nothing returns nothing
set .wisp[i] = .wisp[.count]
set .locx[i] = .locx[.count]
set .locy[i] = .locy[.count]
set .facing[i] = .facing[.count]
set .speed[i] = .speed[.count]
set .wisp[.count] = null
set .locx[.count] = 0.
set .locy[.count] = 0.
set .facing[.count] = 0.
set .speed[.count] = 0.
set .count = .count - 1
endmethod
private method periodic takes nothing returns nothing
local real gravitation
set .i = 1
loop
exitwhen i > .count
if this.timecount <= ENDCOUNT then
if .facing[i] < 0 then
set .facing[i] = facing[i]+360.0
endif
set gravitation = Atan2(GetUnitY(.caster) - .locy[i], GetUnitX(.caster) - .locx[i]) * bj_RADTODEG
if gravitation < 0 then
if .facing[i] > 180.0 then
set gravitation = gravitation + 360.0
endif
endif
set .facing[i] = .facing[i] + (gravitation-.facing[i]) * ACCURACY * (MAXSPEED-.speed[i])
set .speed[i] = .speed[i] + ACCELERATION * (TOLERANCE - Pow(RAbsBJ(gravitation-.facing[i]), INERTIA))
if .speed[i] > MAXSPEED then
set .speed[i] = MAXSPEED
endif
if .speed[i] < 0 then
set .speed[i] = 0
endif
call SetUnitFacing(.wisp[i], .facing[i])
else
if .speed[i] < MAXSPEED then
set .speed[i] = .speed[i] + 0.1
endif
endif
set .locx[i] = .locx[i] + Cos(.facing[i] * bj_DEGTORAD) * .speed[i]
set .locy[i] = .locy[i] + Sin(.facing[i] * bj_DEGTORAD) * .speed[i]
if .locx[i] > mapmaxX or .locy[i] > mapmaxY or .locx[i] < mapminX or .locy[i] < mapminY then
call RemoveUnit(.wisp[i])
call .ClearData()
else
call SetUnitX(.wisp[i], .locx[i])
call SetUnitY(.wisp[i], .locy[i])
set temp = this
call GroupEnumUnitsInArea(ENUM_GROUP, .locx[i], .locy[i], RADIUSC, check)
if explode then
call GroupEnumUnitsInArea(ENUM_GROUP, .locx[i], .locy[i], RADIUSDMG, filter)
call UnitApplyTimedLife(.wisp[i], 'BTLF', 0.01)
call .ClearData()
set explode = false
else
set i = i + 1
endif
endif
endloop
set .timecount = .timecount + 1
if .count == 0 or .timecount >= MAXCOUNT then
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
private static method Init takes nothing returns boolean
local thistype this = temp
local destructable d = GetFilterDestructable()
local real x = GetDestructableX(d)
local real y = GetDestructableY(d)
local real dx = x - .x
local real dy = y - .y
local integer destrID = GetDestructableTypeId(d)
if SquareRoot(dx*dx+dy*dy) > RADIUS then
return false
endif
if (destrID == TREE1 or destrID == TREE2) then // change this if you require more trees
set .count = .count + 1
set .facing[.count] = GetRandomReal(0., 360.)
set .wisp[.count] = CreateUnit(.owner, UNITID, x, y, .facing[.count])
set .locx[.count] = x
set .locy[.count] = y
set .speed[.count] = STARTSPEED
call UnitAddAbility(.wisp[.count], 'Amrf')
call UnitRemoveAbility(.wisp[.count], 'Amrf')
call SetUnitFlyHeight(.wisp[.count], HEIGHT, 0)
endif
set d = null
return false
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set .caster = GetTriggerUnit()
set .owner = GetTriggerPlayer()
set .x = GetSpellTargetX()
set .y = GetSpellTargetY()
set .count = 0
set .timecount = 0
set .level = GetUnitAbilityLevel(.caster, SPELLID)
set .damage = GetDamageAmount(.level)
set temp = this
call MoveRectTo(rct, .x, .y)
call EnumDestructablesInRect(rct, filt, null)
return this
endmethod
private static method SpellCast takes nothing returns boolean
static if LIBRARY_GT then
call thistype.create().startPeriodic()
else
if GetSpellAbilityId() == SPELLID then
call thistype.create().startPeriodic()
endif
endif
return false
endmethod
implement WispInit
endstruct
endlibrary