library DuneWorm initializer onInit requires TimerUtils, UnitStatus
//===========================================================================
// Dune Worm v1.05b by TriggerHappy
//============================================================================
//
// How to Install:
// 1. Copy the Dune Worm folder over to your map.
// 2. In the object editor, copy the Dune Worm ability
// and the SUMMON unit (unless you'd like to use your own)
// 3. Once copied, change ABILITY_ID and WORM_DUMMY
// to the raw codes that fit your map.
//
// Notes:
// Created for the Zephyr Contest #6 at THW.
// Credits to Vexorian and Rising_Dusk for their systems
// and everyone who has helped improve this.
//===========================================================================
//===========================================================================
// Configurables
//===========================================================================
globals
private constant integer WORM_DUMMY = 'h000' // The worm dummy raw code
private constant integer ABILITY_ID = 'A000' // The raw code of the spell
private constant integer CIRCLE_DIST = 10 // How far apart each effect created in the circle is (increase if lag)
private constant integer LOCUST_ID = 'Aloc'
private constant real WAIT_TIME = 3.5 // The worm timer interval
private constant real WORM_DISTANCE = 150 // How far away from the target the worm spawns
private constant real CIRLCE_INT = 0.05 // The timer interval for the circle effect
private constant string UPRISE_ANIM = "Morph Alternate" // The animation to while when the worm rises
private constant string DIG_ANIM = "Morph" // The animation to while when the worm rises
private constant string CIRCLE_FX = "Abilities\\Spells\\Undead\\Impale\\ImpaleMissTarget.mdl" // The fx on cast
private constant string BLOOD_FX = "Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl" // The effect when the worm impales the enemy
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS // The weapon type
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL // The damage type
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL // The attack type
endglobals
private constant function GetDamage takes integer lvl returns real
return lvl*(100.00)
endfunction
// Level 1: 300
// Level 2: 400
// Level 3: 500
private constant function GetAOE takes integer lvl returns real
return 200. + (lvl * 100)
endfunction
private constant function AttackCount takes integer lvl returns integer
return lvl
endfunction
//==============================
// Do not edit past this line
//==============================
globals
private group GLOBAL_GROUP = CreateGroup()
private player TempPlayer
endglobals
native UnitAlive takes unit id returns boolean
private struct data
unit worm
unit caster
unit target = null
unit last
real castX
real castY
real aoe
real dmg
player p
integer level
integer max
integer tick = 0
boolean b = false
string nextAnim = UPRISE_ANIM
static method PickUnit takes nothing returns boolean
return IsPlayerEnemy(GetOwningPlayer(GetFilterUnit()), TempPlayer) and UnitAlive(GetFilterUnit())
endmethod
method onDestroy takes nothing returns nothing
set this.worm = null
set this.caster = null
set this.target = null
set this.last = null
endmethod
static method callback takes nothing returns nothing
local real x = 0
local real y
local real f
local timer t = GetExpiredTimer()
local data this = GetTimerData(t)
if this.tick >= this.max then
call RemoveUnit(this.worm)
call ReleaseTimer(t)
call this.destroy()
return
endif
if this.target == null then
set TempPlayer = this.p
call GroupEnumUnitsInRange(GLOBAL_GROUP, this.castX, this.castY, this.aoe, Filter(function data.PickUnit))
call ForGroup(GLOBAL_GROUP, function GroupPickRandomUnitEnum) // The BJ is completely fine.
set this.target = FirstOfGroup(GLOBAL_GROUP)
if not UnitAlive(this.target) or this.target == null then
set this.target = null
set this.last = null
set this.tick = this.tick + 1
return
endif
set this.last = this.target
endif
if this.target != null then
if this.nextAnim == UPRISE_ANIM then
set f = GetRandomReal(0, 360)
set x = GetUnitX(this.target) + WORM_DISTANCE * Cos(f* bj_DEGTORAD)
set y = GetUnitY(this.target) + WORM_DISTANCE * Sin(f* bj_DEGTORAD)
call SetUnitFacing(this.worm, f+180)
call StunUnitTimed(this.target, WAIT_TIME)
call SetUnitX(this.worm, x)
call SetUnitY(this.worm, y)
else
call UnitDamageTarget(this.caster, this.last, this.dmg, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
call DestroyEffect(AddSpecialEffectTarget(BLOOD_FX, this.target, "origin"))
endif
call SetUnitAnimation(this.worm, this.nextAnim)
set x = WAIT_TIME
if this.nextAnim == UPRISE_ANIM then
set this.nextAnim = DIG_ANIM
set x = WAIT_TIME-1
elseif this.nextAnim == DIG_ANIM then
set this.nextAnim = UPRISE_ANIM
set this.tick = this.tick + 1
set this.target = null
endif
call PauseTimer(t)
call TimerStart(t, x, true, function data.callback)
endif
endmethod
static method create takes unit u, integer lvl, unit c returns data
local data this = data.allocate()
local timer tmr = NewTimer()
local real x
local real y
local integer i = 0
set this.caster = c
set this.worm = u
set this.level = lvl
set this.castX = GetSpellTargetX()
set this.castY = GetSpellTargetY()
set this.aoe = GetAOE(lvl)
set this.p = GetOwningPlayer(u)
set this.dmg = GetDamage(lvl)
set this.max = AttackCount(lvl)
loop
exitwhen i > 360
set x = this.castX + this.aoe * Cos(i*bj_DEGTORAD)
set y = this.castY + this.aoe * Sin(i*bj_DEGTORAD)
call DestroyEffect(AddSpecialEffect(CIRCLE_FX, x, y))
set i = i + CIRCLE_DIST
endloop
call UnitAddAbility(u, LOCUST_ID)
call SetTimerData(tmr, this)
call TimerStart(tmr, 0, true, function data.callback)
return this
endmethod
endstruct
private function Actions takes nothing returns boolean
local unit u
if GetSpellAbilityId() == ABILITY_ID then
set u = GetTriggerUnit()
call data.create(CreateUnit(GetOwningPlayer(u), WORM_DUMMY, 0, 0, 0), GetUnitAbilityLevel(u, ABILITY_ID), u)
set u = null
endif
return false
endfunction
//===========================================================================
private function onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Filter(function Actions))
endfunction
endlibrary