- Joined
- Jun 20, 2014
- Messages
- 552
Im more screwed man. Mine is like the most common.
constant function AoP_DummiesOwner takes nothing returns player
return Player(PLAYER_NEUTRAL_PASSIVE)
endfunction
constant function AoP_FlyAbilityID takes nothing returns integer
return 'Amrf'
endfunction
constant function AoP_FrameUpdate takes nothing returns real
return 1 / 32.
endfunction
function AoP_IsUnitAlive takes unit u returns boolean
return GetUnitTypeId(u) != 0 and not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction
function AoP_IsUnitChanneling takes unit u returns boolean
return AoP_IsUnitAlive(u) and GetUnitCurrentOrder(u) == udg_AoP_AbilityOrder
endfunction
function AoP_GetMagnitude2D takes real x, real y returns real
return SquareRoot(x*x+y*y)
endfunction
function AoP_GetMagnitude3D takes real x, real y, real z returns real
return SquareRoot(x*x+y*y+z*z)
endfunction
function AoP_GetAngle takes real x, real y returns real
return Atan2(y, x)
endfunction
function AoP_GetAngleZ takes real distance2D, real z returns real
return Atan2(z, distance2D)
endfunction
function AoP_GetParabolaHeight takes real y0, real y1, real h, real d, real x returns real
local real A = (2*(y0+y1)-4*h)/(d*d)
local real B = (y1-y0-A*d*d)/d
return A*x*x + B*x + y0
endfunction
function AoP_GetTerrainZ takes real x, real y returns real
call MoveLocation(udg_AoP_ZLocator, x, y)
return GetLocationZ(udg_AoP_ZLocator)
endfunction
function AoP_GetUnitZAlt takes unit u, real x, real y returns real
return GetUnitFlyHeight(u) + AoP_GetTerrainZ(x, y)
endfunction
function AoP_UnitApplyFly takes unit u returns nothing
if UnitAddAbility(u, AoP_FlyAbilityID()) and UnitRemoveAbility(u, AoP_FlyAbilityID()) then
endif
endfunction
function AoP_SetUnitZAlt takes unit u, real z, real x, real y returns nothing
call SetUnitFlyHeight(u, z - AoP_GetTerrainZ(x, y), 0)
endfunction
function AoP_SetUnitPosition takes unit u, real x, real y returns nothing
call SetUnitX(u, x)
call SetUnitY(u, y)
endfunction
function AoP_Destroy takes integer index returns nothing
set udg_AoP_Recycle[udg_AoP_Recyclable] = index
set udg_AoP_Recyclable = udg_AoP_Recyclable + 1
set udg_AoP_Next[udg_AoP_Prev[index]] = udg_AoP_Next[index]
set udg_AoP_Prev[udg_AoP_Next[index]] = udg_AoP_Prev[index]
if udg_AoP_Next[0] == 0 then
call PauseTimer(udg_AoP_Timer)
endif
endfunction
function AoP_Create takes nothing returns integer
local integer newIndex
if udg_AoP_Recyclable == 0 then
set udg_AoP_MaxIndex = udg_AoP_MaxIndex + 1
set newIndex = udg_AoP_MaxIndex
else
set udg_AoP_Recyclable = udg_AoP_Recyclable - 1
set newIndex = udg_AoP_Recycle[udg_AoP_Recyclable]
endif
set udg_AoP_Next[newIndex] = 0
set udg_AoP_Next[udg_AoP_Prev[0]] = newIndex
set udg_AoP_Prev[newIndex] = udg_AoP_Prev[0]
set udg_AoP_Prev[0] = newIndex
return newIndex
endfunction
function AoP_CreateParticle takes string model, real x, real y, real z, real size, real dur returns nothing
local integer pIndex = AoP_Create()
set udg_AoP_Particle[pIndex] = CreateUnit(AoP_DummiesOwner(), udg_AoP_Dummy, x, y, 0)
call AoP_SetUnitPosition(udg_AoP_Particle[pIndex], x, y)
call AoP_UnitApplyFly(udg_AoP_Particle[pIndex])
call AoP_SetUnitZAlt(udg_AoP_Particle[pIndex], z, x, y)
call SetUnitScale(udg_AoP_Particle[pIndex], size, 0, 0)
set udg_AoP_AttachedModel[pIndex] = AddSpecialEffectTarget(model, udg_AoP_Particle[pIndex], "origin")
set udg_AoP_realTimer[pIndex] = dur
set udg_AoP_Stage[pIndex] = 5
endfunction
function AoP_CreateParticleTarget takes string model, unit u, real size, real dur returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
call AoP_CreateParticle(model, x, y, AoP_GetUnitZAlt(u, x, y), size, dur)
endfunction
function AoP_StartSummoningEffect takes real x, real y, integer lvl returns nothing
local integer eIndex = AoP_Create()
local integer i = 0
loop
set i = i + 1
exitwhen udg_AoP_SummoningStartSFX[i] == null
call DestroyEffect(AddSpecialEffect(udg_AoP_SummoningStartSFX[i], x, y))
endloop
set udg_AoP_TargetX[eIndex] = x
set udg_AoP_TargetY[eIndex] = y
set udg_AoP_Level[eIndex] = lvl
set udg_AoP_Dist[eIndex] = udg_AoP_sGroundSFXStart
set udg_AoP_realTimer[eIndex] = 0
set udg_AoP_Stage[eIndex] = 4
endfunction
function AoP_Periodic takes nothing returns nothing
local real x
local real y
local real z
local integer i
local integer index = 0
loop
set index = udg_AoP_Next[index]
exitwhen index == 0
if udg_AoP_Stage[index] == 1 then
if AoP_IsUnitChanneling(udg_AoP_Caster[index]) then
if udg_AoP_Dist2[index] < udg_AoP_Dist[index] then
set udg_AoP_Dist2[index] = udg_AoP_Dist2[index] + udg_AoP_Speed[index]
set udg_AoP_Dist[0] = udg_AoP_Dist2[index] - udg_AoP_Dist[index] // Opposite to angle
set x = udg_AoP_TargetX[index] + udg_AoP_Dist[0] * Cos(udg_AoP_Angle[index])
set y = udg_AoP_TargetY[index] + udg_AoP_Dist[0] * Sin(udg_AoP_Angle[index])
set z = AoP_GetParabolaHeight(udg_AoP_SourceZ[index], udg_AoP_TargetZ[index], udg_AoP_MaxHeight[index], udg_AoP_Dist[index], udg_AoP_Dist2[index])
call SetUnitX(udg_AoP_Missile[index], x)
call SetUnitY(udg_AoP_Missile[index], y)
call AoP_SetUnitZAlt(udg_AoP_Missile[index], z, x, y)
else
call DestroyEffect(udg_AoP_AttachedModel[index])
call KillUnit(udg_AoP_Missile[index])
call AoP_StartSummoningEffect(udg_AoP_TargetX[index], udg_AoP_TargetY[index], udg_AoP_Level[index])
set udg_AoP_SummoningEffect[index] = CreateUnit(udg_AoP_Owner[index], udg_AoP_Dummy, udg_AoP_TargetX[index], udg_AoP_TargetY[index], 0)
call AoP_SetUnitPosition(udg_AoP_SummoningEffect[index], udg_AoP_TargetX[index], udg_AoP_TargetY[index])
call AoP_UnitApplyFly(udg_AoP_SummoningEffect[index])
call SetUnitFlyHeight(udg_AoP_SummoningEffect[index], udg_AoP_sSFXHeight, 0)
call SetUnitScale(udg_AoP_SummoningEffect[index], 0, 0, 0)
set udg_AoP_AttachedModel[index] = AddSpecialEffectTarget(udg_AoP_SummoningSFX, udg_AoP_SummoningEffect[index], "origin")
set udg_AoP_Dist[index] = 0
set udg_AoP_Stage[index] = 2
endif
else
// --- When caster stopped channeling
call DestroyEffect(udg_AoP_AttachedModel[index])
call KillUnit(udg_AoP_Missile[index])
call AoP_CreateParticleTarget(udg_AoP_mOrbCancelSFX, udg_AoP_Missile[index], udg_AoP_mOrbCancelSFXSize, udg_AoP_mOrbCancelSFXDeath)
call AoP_Destroy(index)
endif
elseif udg_AoP_Stage[index] == 2 then
if AoP_IsUnitChanneling(udg_AoP_Caster[index]) then
if udg_AoP_Dist[index] < udg_AoP_sSFXFinalSize[udg_AoP_Level[index]] then
else
endif
else
// --- When caster stopped channeling
call AoP_Destroy(index)
endif
elseif udg_AoP_Stage[index] == 4 then
if udg_AoP_Dist[index] < udg_AoP_sGroundSFXArea then
set udg_AoP_Dist[index] = udg_AoP_Dist[index] + udg_AoP_sGroundSFXSpeed
set udg_AoP_realTimer[index] = udg_AoP_realTimer[index] + AoP_FrameUpdate()
if udg_AoP_realTimer[index] >= udg_AoP_sGroundSFXInterval then
set udg_AoP_Angle[0] = GetRandomReal(-bj_PI, bj_PI)
set i = R2I(4*udg_AoP_sGroundSFXDensity)
set udg_AoP_Dist[0] = 360 / i
loop
exitwhen i <= 0
set udg_AoP_Angle[0] = udg_AoP_Angle[0] + udg_AoP_Dist[0]
set x = udg_AoP_TargetX[index] + udg_AoP_Dist[index] * Cos(udg_AoP_Angle[0])
set y = udg_AoP_TargetY[index] + udg_AoP_Dist[index] * Sin(udg_AoP_Angle[0])
call DestroyEffect(AddSpecialEffect(udg_AoP_sGroundSFX, x, y))
set i = i - 1
endloop
set udg_AoP_realTimer[index] = 0
endif
else
call AoP_Destroy(index)
endif
elseif udg_AoP_Stage[index] == 5 then
set udg_AoP_realTimer[index] = udg_AoP_realTimer[index] - AoP_FrameUpdate()
if udg_AoP_realTimer[index] <= 0 then
call DestroyEffect(udg_AoP_AttachedModel[index])
call KillUnit(udg_AoP_Particle[index])
call AoP_Destroy(index)
endif
endif
endloop
endfunction
function AoP_onEffect takes nothing returns boolean
local real sx
local real sy
local real stx
local real sty
local integer cIndex
if GetSpellAbilityId() == udg_AoP_Ability then
set cIndex = AoP_Create()
set udg_AoP_Caster[cIndex] = GetTriggerUnit()
set udg_AoP_Owner[cIndex] = GetTriggerPlayer()
set udg_AoP_Level[cIndex] = GetUnitAbilityLevel(udg_AoP_Caster[cIndex], udg_AoP_Ability)
set sx = GetUnitX(udg_AoP_Caster[cIndex])
set sy = GetUnitY(udg_AoP_Caster[cIndex])
set udg_AoP_SourceZ[cIndex] = AoP_GetTerrainZ(sx, sy) + udg_AoP_mOrbSpawnHeight
set udg_AoP_TargetX[cIndex] = GetSpellTargetX()
set udg_AoP_TargetY[cIndex] = GetSpellTargetY()
set udg_AoP_TargetZ[cIndex] = AoP_GetTerrainZ(udg_AoP_TargetX[cIndex], udg_AoP_TargetY[cIndex])
set stx = udg_AoP_TargetX[cIndex] - sx
set sty = udg_AoP_TargetY[cIndex] - sy
set udg_AoP_Angle[cIndex] = AoP_GetAngle(stx, sty)
set udg_AoP_TargetX[0] = sx + udg_AoP_mOrbSpawnOffset * Cos(udg_AoP_Angle[cIndex])
set udg_AoP_TargetY[0] = sy + udg_AoP_mOrbSpawnOffset * Sin(udg_AoP_Angle[cIndex])
set udg_AoP_Dist[cIndex] = AoP_GetMagnitude2D(stx, sty) - udg_AoP_mOrbSpawnOffset
set udg_AoP_Dist2[cIndex] = 0
set udg_AoP_Speed[cIndex] = udg_AoP_Dist[cIndex] / udg_AoP_mOrbTravelTime
set udg_AoP_MaxHeight[cIndex] = udg_AoP_Dist[cIndex] * udg_AoP_mOrbArc + udg_AoP_TargetZ[cIndex]
set udg_AoP_Missile[cIndex] = CreateUnit(AoP_DummiesOwner(), udg_AoP_Dummy, udg_AoP_TargetX[0], udg_AoP_TargetY[0], udg_AoP_Angle[cIndex]*bj_RADTODEG)
call AoP_UnitApplyFly(udg_AoP_Missile[cIndex])
call AoP_SetUnitZAlt(udg_AoP_Missile[cIndex], udg_AoP_SourceZ[cIndex], udg_AoP_TargetX[0], udg_AoP_TargetY[0])
call SetUnitScale(udg_AoP_Missile[cIndex], udg_AoP_mOrbSize, 0, 0)
set udg_AoP_AttachedModel[cIndex] = AddSpecialEffectTarget(udg_AoP_ManaOrbModel, udg_AoP_Missile[cIndex], "origin")
set udg_AoP_Stage[cIndex] = 1
if udg_AoP_Prev[cIndex] == 0 then
call TimerStart(udg_AoP_Timer, AoP_FrameUpdate(), true, function AoP_Periodic)
endif
endif
return false
endfunction
function AoP_Initialize takes nothing returns nothing
local integer i = 0
loop
set i = i + 1
exitwhen i > udg_AoP_AbilityLevels
endloop
set udg_AoP_mOrbTravelTime = udg_AoP_mOrbTravelTime / AoP_FrameUpdate()
set udg_AoP_sGroundSFXSpeed = udg_AoP_sGroundSFXSpeed * AoP_FrameUpdate()
if udg_AoP_ZLocator == null then
set udg_AoP_ZLocator = Location(0, 0)
endif
endfunction
//===========================================================================
function InitTrig_Ancient_of_Power takes nothing returns nothing
set gg_trg_Ancient_of_Power = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_Ancient_of_Power, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_Ancient_of_Power, Condition(function AoP_onEffect))
endfunction
One question though - MUST I summon a unit under any circumstances when casting the ability or is it ok if there are additional conditions to be met?
So, yes, the summoning must have clear relation to the spell.Create a spell that summons one or many units in a unique way.
Yeah, I agree. I personaly also prefer seeing concept/ideas/thoughts or maybe a pic over having pure code as WIP.I'd like some visual updates as well!
library Devourer
globals
private constant real TIMEOUT = 0.03125
private constant integer ABIL_ID = 0
private constant integer REQUIRED_TREES = 6
private constant integer REQUIRED_TREES_PER_LVL = -1
private constant real TREE_RADIUS_CHECK = 256
private constant real TREE_RADIUS_CHECK_PER_LVL = 0
private constant real SPAWN_DELAY = 6
private constant real SPAWN_DELAY_PER_LVL = -1
private constant integer BROODMOTHER_1 = 0
private constant integer BROODMOTHER_2 = 0
private constant integer BROODMOTHER_3 = 0
private constant real TIMED_LIFE = 10
private constant real TIMED_LIFE_PER_LVL = 10
private constant real SOUL_DROP_RADIUS = 800
private constant real SOUL_DROP_RADIUS_PER_LVL = 0
private constant real SOUL_DROP_DURATION = 10
private constant real SOUL_DROP_DURATION_PER_LVL = 0
private constant real SOUL_FEED_RADIUS = 500
private constant real SOUL_FEED_RADIUS_PER_LVL = 100
private constant real TIMED_LIFE_BONUS = 0
private constant real TIMED_LIFE_BONUS_PER_LVL = 1
private constant real MANA_FEED_RADIUS = 1000
private constant real MANA_FEED_RADIUS = 0
private constant real MANA_RELEASE_REQ = 450
private constant real MANA_RELEASE_REQ_PER_LVL = -50
private constant real MANA_RELEASE_DMG = 256
private constant real MANA_RELEASE_DMG_PER_LVL = 0
private constant attacktype MANA_RELEASE_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype MANA_RELEASE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
private constant real MANA_RELEASE_CD = 0
private constant real MANA_RELEASE_CD_PER_LVL = 0
private constant boolean COLLECT_MANA_ON_CD = false
private constant real SWARM_SPAWN_OFFSET = 128
private constant real SWARM_SPAWN_OFFSET_PER_LVL = 0
private constant real SWARM_TRAVEL_DIST = 768
private constant real SWARM_TRAVEL_DIST_PER_LVL = 0
private constant real SWARM_RADIUS = 128
private constant real SWARM_RADIUS_PER_LVL = 0
private constant real SWARM_DAMAGE = 0
private constant real SWARM_DAMAGE_PER_LVL = 75
private constant attacktype SWARM_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype SWARM_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
private constant real SWARM_CD = 12
private constant real SWARM_CD_PER_LVL = -2
endglobals
endlibrary
Open them with Magos.
Here's another WIP:
![]()
Judges haven't weighed-in on this matter though, but I think one "Main" ability (ability on your hero) is fair since allowing multiple intrinsically allows you to do much more complicated thingsIcemanBo said:It's defined: "Create a spell", which means one spell.
Given that information you can do everything you want of course and try to argue.
Though, the judgement might share or might not share your thoughts of concept.
...
Judges haven't weighed-in on this matter though, but I think one "Main" ability (ability on your hero) is fair since allowing multiple intrinsically allows you to do much more complicated things
Judges haven't weighed-in on this matter though
It's defined: "Create a spell", which means one spell.
Given that information you can do everything you want of course and try to argue.
Though, the judgement might share or might not share your thoughts of concept.
Mind to elaborate how the result will look?
1) Do you want to have a combination like defend/undefend?
2) Do you to want to trigger multiple spells that the hero will have at same time.
If it's 1), I could see it being argued. For example if the removal of one has an critical effect on your spell concept. Like defend/undefend.
But otherwise: simple switching between spells to define which unit to summon next, would be no good idea if you ask me.
If it's 2), then spontaneously I would not recommend to follow such a concept. It might be risky it won't count as "one spell", which is defined in the contest template.
You can use any enhancing system (xe, IsDestructableTree, Damage detection systems, etc.), as long as the judge(s) and/or host give(s) consent to its usage.
it doesn't mean that it is stricted to system, it states that you can use any enchancing system, as long as the judges/host permitted you to do so.
The vast majority of the contest time is still left (been 1 week out of 4 thus far) - you'd hardly have to rush - especially since you can see a few of us have finished within the first week and we weren't rushing XD
scope ElementalChaos initializer Init
//================================================================================
//============================Configurable Part===================================
//================================================================================
globals
/*
GENERAL Configuration
*/
//The spell id of the spell
private constant integer SPELL_ID = 'A000'
//Dummy unit id
private constant integer DUMMY_UNIT_ID = 'h000'
//Interval time between energy packs to release the next energy pack
private constant real SPHERE_PACK_RELEASE_INTERVAL = 0.5
//Timer updating time constant
private constant real TIMER_TIMEOUT = 0.03125
//How long does the dummy unit get removed so that the attached effect could be fully played
private constant real DUMMY_DEATH_TIME = 2.
//The attachement path of the effect on the dummy units
private constant string ATTACHMENT_PATH = "origin"
//Attack type of the damage
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
//Damage type of the damage
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
//Weapon type of the damage
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
//Gravitational force determines how strong the verticle force that pulling the missile to the ground
private constant real GRAVITATIONAL_FORCE = 150.
/*
SPHERE Configuration (the generating sphere at start)
*/
//Sphere effect model path
private constant string SPHERE_EFFECT_PATH = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
//Color adjustment - red color component of sphere
private constant integer SPHERE_COLOR_RED = 70
//Color adjustment - green color component of sphere
private constant integer SPHERE_COLOR_GREEN = 200
//Color adjustment - blue color component of sphere
private constant integer SPHERE_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere
private constant integer SPHERE_TRANSPARENCY = 255
//Initial scale of the sphere before generating start
private constant real SPHERE_INITIAL_SCALE = 0.
/*
SPHERE_PARTICLE Configuration (those little particles moving toward the sphere)
*/
//Sphere particle effect stop spawn timing before sphere fully generated
private constant real SPHERE_PARTICLE_STOP_TIMING = 1.
//Sphere particle maximum scale
private constant real SPHERE_PARTICLE_MAX_SCALE = 2.5
//Sphere particle minimum scale
private constant real SPHERE_PARTICLE_MIN_SCALE = 1.0
//Sphere particle maximum distance from sphere
private constant real SPHERE_PARTICLE_MAX_DIST = 300.
//Sphere particle minimum distance from sphere
private constant real SPHERE_PARTICLE_MIN_DIST = 200.
//Sphere particle maximum height distance from shpere
private constant real SPHERE_PARTICLE_MAX_HEIGHT = 200.
//Sphere particle minimum height distance from sphere
private constant real SPHERE_PARTICLE_MIN_HEIGHT = 100.
//Sphere particle maximum speed moving toward sphere (horizontal)
//(verticle) speed is dependent on the (horizontal) speed and height (distance) between particle and sphere
private constant real SPHERE_PARTICLE_MAX_SPEED = 32.
//Sphere particle minimum speed moving toward sphere (horizontal)
private constant real SPHERE_PARTICLE_MIN_SPEED = 16.
//Sphere particle dissapear (remove) distance between sphere and sphere particle
private constant real SPHERE_PARTICLE_END_DIST = 25.
//Color adjustment - red color component of sphere particle
private constant integer SPHERE_PARTICLE_COLOR_RED = 255
//Color adjustment - green color component of sphere particle
private constant integer SPHERE_PARTICLE_COLOR_GREEN = 255
//Color adjustment - blue color component of sphere particle
private constant integer SPHERE_PARTICLE_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere particle
private constant integer SPHERE_PARTICLE_TRANSPARENCY = 255
//Sphere particle effect model path
private constant string SPHERE_PARTICLE_EFFECT_PATH = "war3mapImported\\Stars_lightning_.MDX"
/*
SPHERE_ENERGY_PACK Configuraration (The sphere/orb effect moves away from the sphere)
*/
//Minimum distance to traval from sphere
private constant real SPHERE_ENERGY_PACK_MIN_DIST = 150.
//Maximum distance to traval from sphere
private constant real SPHERE_ENERGY_PACK_MAX_DIST = 256.
//Minimum height to travel from sphere
private constant real SPHERE_ENERGY_PACK_MIN_HEIGHT = 100.
//Maximum height to travel from sphere
private constant real SPHERE_ENERGY_PACK_MAX_HEIGHT = 200.
//Minimum speed when travelling
private constant real SPHERE_ENERGY_PACK_MIN_SPEED = 2.
//Maximum speed when travelling
private constant real SPHERE_ENERGY_PACK_MAX_SPEED = 4.
//Minimum scale for the effect (unit)
private constant real SPHERE_ENERGY_PACK_MIN_SCALE = .5
//Maximum scale for the effect (unit)
private constant real SPHERE_ENERGY_PACK_MAX_SCALE = 1.
//Color adjustment - red color component of sphere energy pack
private constant integer SPHERE_ENERGY_PACK_COLOR_RED = 70
//Color adjustment - green color component of sphere energy pack
private constant integer SPHERE_ENERGY_PACK_COLOR_GREEN = 200
//Color adjustment - blue color component of sphere energy pack
private constant integer SPHERE_ENERGY_PACK_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere energy pack
private constant integer SPHERE_ENERGY_PACK_TRANSPARENCY = 255
//Sphere energy pack effect path
private constant string SPHERE_ENERGY_PACK_EFFECT_PATH = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
//minimum time before energy pack could explode
private constant real SPHERE_ENERGY_PACK_EXPLODE_MIN_TIME = 5.
//maximum time for energy to explode
private constant real SPHERE_ENERGY_PACK_EXPLODE_MAX_TIME = 6.
//Scale of the explode effect (unit)
private constant real SPHERE_ENERGY_PACK_EXPLODE_SCALE = 0.5
//Color adjustment - red color component of sphere energy pack explode effect
private constant integer SPHERE_ENERGY_PACK_EXPLODE_COLOR_RED = 50
//Color adjustment - green color component of sphere energy pack explode effect
private constant integer SPHERE_ENERGY_PACK_EXPLODE_COLOR_GREEN = 50
//Color adjustment - blue color component of sphere energy pack explode effect
private constant integer SPHERE_ENERGY_PACK_EXPLODE_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere energy pack explode effect
private constant integer SPHERE_ENERGY_PACK_EXPLODE_TRANSPARENCY = 255
//exploe effect of the energy pack
private constant string SPHERE_ENERGY_PACK_EXPLODE_PATH = "Units\\NightElf\\Wisp\\WispExplode.mdl"
/*
SPHERE_MISSILE Configuration (missile that shoots out when energy pack exploded)
*/
//The minimum time taken for the missile to land at ground
private constant real SPHERE_MISSILE_MIN_HEIGHT = 50.
//The maximum time taken for the missile to land at ground
private constant real SPHERE_MISSILE_MAX_HEIGHT = 150.
//Scaling of the effect (unit)
private constant real SPHERE_MISSILE_SCALE = 1.
//Color adjustment - red color component of missile
private constant integer SPHERE_MISSILE_COLOR_RED = 255
//Color adjustment - green color component of missile
private constant integer SPHERE_MISSILE_COLOR_GREEN = 255
//Color adjustment - blue color component of missile
private constant integer SPHERE_MISSILE_COLOR_BLUE = 255
//Transparency adjustment - transparency of missile
private constant integer SPHERE_MISSILE_TRANSPARENCY = 255
//Missile effect attached to the unit
private constant string SPHERE_MISSILE_EFFECT_PATH = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
//Scaling of the effect (unit) after landed
private constant real SPHERE_MISSILE_LANDED_SCALE = 1.
//Color adjustment - red color component of missile after landed
private constant integer SPHERE_MISSILE_LANDED_COLOR_RED = 255
//Color adjustment - green color component of missile after landed
private constant integer SPHERE_MISSILE_LANDED_COLOR_GREEN = 255
//Color adjustment - blue color component of missile after landed
private constant integer SPHERE_MISSILE_LANDED_COLOR_BLUE = 255
//Transparency adjustment - transparency of missile after landed
private constant integer SPHERE_MISSILE_LANDED_TRANSPARENCY = 255
//Missile effect attach to the unit after landed ground
private constant string SPHERE_MISSILE_LANDED_EFFECT_PATH = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
/*
Level Data Configuration (data value will be affected according to spell lv)
All of them are configurable at function Init
*/
//Sphere height
private real array SPHERE_HEIGHT
//Sphere maximum scale
private real array SPHERE_MAX_SCALE
//Sphere generates time
private real array SPHERE_GENERATE_TIME
//Sphere energy pack release amount
private integer array SPHERE_RELEASE_PACK_MAX
//Sphere missile minimum spawn
private integer array SPHERE_ENERGY_PACK_MIN_MISSILE
//Sphere missile maximum spawn
private integer array SPHERE_ENERGY_PACK_MAX_MISSILE
//Sphere missile minimum travelling distance from shpere
private real array SPHERE_MISSILE_MIN_DIST
//Sphere missile maximum travelling distance from sphere
private real array SPHERE_MISSILE_MAX_DIST
//The damage deal to enemy after missile landed on the ground
private real array SPHERE_FIRST_DAMAGE
//The AoE of the SPHERE_FIRST_DAMAGE
private real array SPHERE_FIRST_DAMAGE_AOE
//Sphere summons unit type after missile landed on the ground
private integer array SPHERE_FIRST_SUMMON
//SPHERE_FIRST_SUMMON time life
private real array SPHERE_FIRST_SUMMONS_TIME_LIFE
//SPHERE_FIRST_SUMMON scale
private real array SPHERE_FIRST_SUMMONS_SCALE
//Red color component of SPHERE_FIRST_SUMMON
private integer array SPHERE_FIRST_SUMMONS_COLOR_RED
//Green color component of SPHERE_FIRST_SUMMON
private integer array SPHERE_FIRST_SUMMONS_COLOR_GREEN
//Blue color component of SPHERE_FIRST_SUMMON
private integer array SPHERE_FIRST_SUMMONS_COLOR_BLUE
//Transparency of SPHERE_FIRST_SUMMON
private integer array SPHERE_FIRST_SUMMONS_TRANSPARENCY
endglobals
private function Init takes nothing returns nothing
//[1] indicates Lv1, [2] indicates Lv2 and [3] indicates Lv3
//Arrays are used instead of globals for preventing overwhelming globals as well as unnessasary coding.
//Setting sphere height
set SPHERE_HEIGHT[1] = 300.
set SPHERE_HEIGHT[2] = 300.
set SPHERE_HEIGHT[3] = 300.
//Setting sphere maximum scale
set SPHERE_MAX_SCALE[1] = 2.0
set SPHERE_MAX_SCALE[2] = 2.5
set SPHERE_MAX_SCALE[3] = 2.5
//Setting sphere generates time
set SPHERE_GENERATE_TIME[1] = 3.0
set SPHERE_GENERATE_TIME[2] = 3.0
set SPHERE_GENERATE_TIME[3] = 3.0
//The energy pack released from the sphere
set SPHERE_RELEASE_PACK_MAX[1] = 3
set SPHERE_RELEASE_PACK_MAX[2] = 4
set SPHERE_RELEASE_PACK_MAX[3] = 5
//Minimum missle could be spawn after energy pack explode
set SPHERE_ENERGY_PACK_MIN_MISSILE[1] = 2
set SPHERE_ENERGY_PACK_MIN_MISSILE[2] = 3
set SPHERE_ENERGY_PACK_MIN_MISSILE[3] = 3
//Maximum missile could be spawn after energy pack explode
set SPHERE_ENERGY_PACK_MAX_MISSILE[1] = 3
set SPHERE_ENERGY_PACK_MAX_MISSILE[2] = 4
set SPHERE_ENERGY_PACK_MAX_MISSILE[3] = 5
//Minimum travel distance from shpere (horizontal)
set SPHERE_MISSILE_MIN_DIST[1] = 100.
set SPHERE_MISSILE_MIN_DIST[2] = 100.
set SPHERE_MISSILE_MIN_DIST[3] = 100.
//Maximum travel distance from sphere (horizontal)
//Usually should cap at AoE of the spell
set SPHERE_MISSILE_MAX_DIST[1] = 600.
set SPHERE_MISSILE_MAX_DIST[2] = 600.
set SPHERE_MISSILE_MAX_DIST[3] = 600.
//Setting the damage amount after the missile landed the ground
set SPHERE_FIRST_DAMAGE[1] = 10.
set SPHERE_FIRST_DAMAGE[2] = 20.
set SPHERE_FIRST_DAMAGE[3] = 30.
//Setting the AoE of the SPHERE_FIRST_DAMAGE
set SPHERE_FIRST_DAMAGE_AOE[1] = 128.
set SPHERE_FIRST_DAMAGE_AOE[2] = 160.
set SPHERE_FIRST_DAMAGE_AOE[3] = 192.
//Setting the unit-id of the first summons type after missile landed
//Using : Water Elemental (war3 default unit)
set SPHERE_FIRST_SUMMON[1] = 'hwat'
set SPHERE_FIRST_SUMMON[2] = 'hwt2'
set SPHERE_FIRST_SUMMON[3] = 'hwt3'
//Scaling of the summons unit
set SPHERE_FIRST_SUMMONS_SCALE[1] = 0.6
set SPHERE_FIRST_SUMMONS_SCALE[2] = 0.6
set SPHERE_FIRST_SUMMONS_SCALE[3] = 0.6
//Color adjustment - red color componenet of summons
set SPHERE_FIRST_SUMMONS_COLOR_RED[1] = 255
set SPHERE_FIRST_SUMMONS_COLOR_RED[2] = 255
set SPHERE_FIRST_SUMMONS_COLOR_RED[2] = 255
//Color adjustment - green color componenet of summons
set SPHERE_FIRST_SUMMONS_COLOR_GREEN[1] = 255
set SPHERE_FIRST_SUMMONS_COLOR_GREEN[2] = 255
set SPHERE_FIRST_SUMMONS_COLOR_GREEN[3] = 255
//Color adjustment - blue color componenet of summons
set SPHERE_FIRST_SUMMONS_COLOR_BLUE[1] = 255
set SPHERE_FIRST_SUMMONS_COLOR_BLUE[2] = 255
set SPHERE_FIRST_SUMMONS_COLOR_BLUE[3] = 255
//Transparency adjustment - transparency of the summons
set SPHERE_FIRST_SUMMONS_TRANSPARENCY[1] = 255
set SPHERE_FIRST_SUMMONS_TRANSPARENCY[2] = 255
set SPHERE_FIRST_SUMMONS_TRANSPARENCY[3] = 255
//Apply the time life for the SPHERE_FIRST_SUMMON unit type
set SPHERE_FIRST_SUMMONS_TIME_LIFE[1] = 10.
set SPHERE_FIRST_SUMMONS_TIME_LIFE[2] = 10.
set SPHERE_FIRST_SUMMONS_TIME_LIFE[3] = 10.
endfunction
//Unit filter before damaging
native UnitAlive takes unit id returns boolean
private function EC_UnitFilter takes player p, unit u returns boolean
return IsUnitEnemy(u, p) and UnitAlive(u)
endfunction
//================================================================================
//==========================End Configurable Part=================================
//================================================================================
//================================================================================
//===========================Non-Configurable Part================================
//================================================================================
globals
//Sphere's phases
private constant integer SPHERE_GENERATING_PHASE = 0
private constant integer SPHERE_RELEASING_PHASE = 1
private constant integer SPHERE_AWAITING_PHASE = 2
private constant integer SPHERE_LANDING_PHASE = 3
//A constant used by SphereMissile struct for increasing verticle velocity every TIMER_TIMEOUT
private constant real SPHERE_MISSILE_CONSTANT = GRAVITATIONAL_FORCE*TIMER_TIMEOUT*TIMER_TIMEOUT
endglobals
private function UnitAddRemoveCrowForm takes unit u returns nothing
if UnitAddAbility(u, 'Amrf') then
call UnitRemoveAbility(u, 'Amrf')
endif
endfunction
//================================================================================
//=========================End Non-Configurable Part==============================
//================================================================================
private keyword ElementalChaos
//-----------------------------------------------------------------
//-----------------------------------------------------------------
//A linked list created by myself
private module LinkedList
static method create takes nothing returns thistype
local thistype this = instanceCount + 1
set instanceCount = this
if thistype(0).next == 0 then
set thistype(0).next = this
set lastNode = this
else
set lastNode.next = this
set this.prev = lastNode
set lastNode = this
endif
set this.next = 0
set thistype(0).prev = this
return this
endmethod
method destroy takes nothing returns nothing
set this.prev.next = this.next
set this.next.prev = this.prev
if this.next == 0 and this.prev == 0 then
set instanceCount = 0
//call instanceClear if exists
static if thistype.instanceClear.exists then
call thistype.instanceClear()
endif
elseif lastNode == this then
set lastNode = this.prev
endif
endmethod
endmodule
//-----------------------------------------------------------------
//-----------------------------------------------------------------
private struct SphereMissile extends array
//==========Sphere Side Effect Globals============
static timer timerSM = CreateTimer()
private static group g = CreateGroup()
player owner //Owner of the missile
unit dummy
effect e
integer spellLv
real x
real y
real speedX //speed value travelling along x-axis
real speedY //speed value travelling along y-axis
real speedv
real height
ElementalChaos ec
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerSM)
endmethod
implement LinkedList
static method onPeriodic takes nothing returns nothing
local thistype this = thistype(0).next
local unit u = null
loop
exitwhen this == 0
//Move the unit horizontally
set this.x = this.x + this.speedX
set this.y = this.y + this.speedY
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
//Move the unit vertically
//v = u + at
//u = this.speedv , a = -GRAVITATIONAL_FORCE, t = TIMER_TIMEOUT
set this.speedv = this.speedv + SPHERE_MISSILE_CONSTANT
set this.height = this.height - this.speedv
call SetUnitFlyHeight(this.dummy, this.height, 0.)
//Check if missile reached ground
if this.height <= 0 then
//Destroy missile effect
call DestroyEffect(this.e)
//Damage the units caught in it
call GroupEnumUnitsInRange(g, this.x, this.y, SPHERE_FIRST_DAMAGE_AOE[this.spellLv], null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if EC_UnitFilter(this.owner, u) then
call UnitDamageTarget(this.dummy, u, SPHERE_FIRST_DAMAGE[this.spellLv], true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
endif
call GroupRemoveUnit(g, u)
endloop
//Spawn the summons out
set u = CreateUnit(this.owner, SPHERE_FIRST_SUMMON[this.spellLv], this.x, this.y, GetRandomReal(0, 360))
//Add the summons to unit group
call GroupAddUnit(ec.unitGroup, u)
//Apply time life (Summons)
call UnitApplyTimedLife(u, 'BTLF', SPHERE_FIRST_SUMMONS_TIME_LIFE[this.spellLv])
//Modify unit scale (Summons)
call SetUnitScale(u, SPHERE_FIRST_SUMMONS_SCALE[this.spellLv], SPHERE_FIRST_SUMMONS_SCALE[this.spellLv], SPHERE_FIRST_SUMMONS_SCALE[this.spellLv])
//Modify unit color (Summons)
call SetUnitVertexColor(u, SPHERE_FIRST_SUMMONS_COLOR_RED[this.spellLv], SPHERE_FIRST_SUMMONS_COLOR_GREEN[this.spellLv], SPHERE_FIRST_SUMMONS_COLOR_BLUE[this.spellLv], SPHERE_FIRST_SUMMONS_TRANSPARENCY[this.spellLv])
//Modify unit scale (dummy)
call SetUnitScale(this.dummy, SPHERE_MISSILE_LANDED_SCALE, SPHERE_MISSILE_LANDED_SCALE, SPHERE_MISSILE_LANDED_SCALE)
//Modify unit color (dummy)
call SetUnitVertexColor(this.dummy, SPHERE_MISSILE_LANDED_COLOR_RED, SPHERE_MISSILE_LANDED_COLOR_GREEN, SPHERE_MISSILE_LANDED_COLOR_BLUE, SPHERE_MISSILE_LANDED_TRANSPARENCY)
//Add landed effect
call DestroyEffect(AddSpecialEffectTarget(SPHERE_MISSILE_LANDED_EFFECT_PATH, this.dummy, ATTACHMENT_PATH))
//Apply time life (dummy)
call UnitApplyTimedLife(this.dummy, 'BTLF', DUMMY_DEATH_TIME)
call this.destroy()
set this.owner = null
set this.dummy = null
set this.e = null
endif
set this = this.next
endloop
endmethod
endstruct
private struct SphereEnergyPack extends array
//==========Sphere Side Effect Globals============
static timer timerSEP = CreateTimer()
player owner
unit dummy
effect e //effect attached to dummy
integer missileCount //Number of missile spawn after energy pack explode
integer spellLv
real x //y coordinate of energy pack
real y //x coordinate of energy pack
real height //height of energy pack from sphere
real speedh //speed horizontal (constant)
real speedv //speed verticle (non-constant)
real remainingTime //time taken for energy pack explode
real destinationX //energy's pack destination x-coordinate
real destinationY //energy's pack destination y-coordinate
real destinationDistance //distance between energy pack and destination of energy pack
ElementalChaos ec
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerSEP)
endmethod
implement LinkedList
static method onPeriodic takes nothing returns nothing
local SphereMissile sm
local thistype this = thistype(0).next
local integer i
local real angle
local real r
local real time
loop
exitwhen this == 0
set angle = Atan2(this.destinationY-GetUnitY(this.dummy), this.destinationX-GetUnitX(this.dummy))
//move the energy pack towards destinationX/Y (horizontally)
set this.destinationDistance = this.destinationDistance - this.speedh
set this.x = this.x + this.speedh * Cos(angle)
set this.y = this.y + this.speedh * Sin(angle)
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
//move the energy pack towards destiantionHeight (vertically)
set this.height = this.height + this.speedv
call SetUnitFlyHeight(this.dummy, this.height, 0.)
//Decrease the explode time by TIMER_TIMEOUT seconds
set this.remainingTime = this.remainingTime - TIMER_TIMEOUT
//Check if energy pack reaches its destination OR remainingTime <= 0
if this.destinationDistance <= 0 or this.remainingTime <= 0 then
//destroy the energy pack effect
call DestroyEffect(this.e)
//change the unit (effect) color
call SetUnitVertexColor(this.dummy, SPHERE_ENERGY_PACK_EXPLODE_COLOR_RED, SPHERE_ENERGY_PACK_EXPLODE_COLOR_GREEN, SPHERE_ENERGY_PACK_EXPLODE_COLOR_BLUE, SPHERE_ENERGY_PACK_EXPLODE_TRANSPARENCY)
//change the unit scale
call SetUnitScale(this.dummy, SPHERE_ENERGY_PACK_EXPLODE_SCALE, SPHERE_ENERGY_PACK_EXPLODE_SCALE, SPHERE_ENERGY_PACK_EXPLODE_SCALE)
//add the explode effect to the dummy
call DestroyEffect(AddSpecialEffectTarget(SPHERE_ENERGY_PACK_EXPLODE_PATH, this.dummy, ATTACHMENT_PATH))
//add life time to the dummy
call UnitApplyTimedLife(this.dummy, 'BTLF', DUMMY_DEATH_TIME)
set i = 0
loop
set i = i + 1
//Create a new missile
set sm = SphereMissile.create()
//Setting up missile requires data
set sm.height = GetRandomReal(SPHERE_MISSILE_MIN_HEIGHT, SPHERE_MISSILE_MAX_HEIGHT)
set sm.spellLv = this.spellLv
set sm.owner = this.owner
set sm.ec = this.ec
set sm.x = this.x
set sm.y = this.y
set sm.dummy = CreateUnit(this.owner, DUMMY_UNIT_ID, this.y, this.x, 0.)
call SetUnitX(sm.dummy, this.x)
call SetUnitY(sm.dummy, this.y)
//Kinemtic formula is used : v*v = u*u + 2as
//Check if height is positive
if sm.height >= 0 then
//Rearrange : u = SquareRoot(v*v - 2as)
//a = -GRAVITATIONAL_FORCE (against gravitational force), v = 0
//So : u = -1 * SquareRoot(2*GRAVITATIONAL_FORCE*s)
set sm.speedv = -1 * SquareRoot(2*GRAVITATIONAL_FORCE*sm.height)
else
//a = GRAVITATIONAL_FORCE, u = 0
set sm.speedv = 0.
endif
//Set the height to the height where it should be
set sm.height = this.height + sm.height
//To obtain the total time taken for the motion, t1 = upward motion (if present), t2 = downward motion
//Calculation are in unit : PER SECOND
//For t1 : use v = u + at
//v = 0, u = sm.speedv, a = -GRAVITATIONAL_FORCE, t1 = ?
//So : t1 = -u/a
//For t2 : use s = ut + (1/2)at*t
//s = sm.height (total height), u = 0, a = GRAVITATIONAL_FORCE, t2 = ?
//So : t2 = SquareRoot(2s/a)
// t2 = SquareRoot((2*sm.height)/GRAVITATIONAL_FORCE)
set time = (-sm.speedv/GRAVITATIONAL_FORCE) + SquareRoot((2*sm.height)/GRAVITATIONAL_FORCE)
//Covert sm.speedv's unit from PER SECOND to PER TIMER_TIMEOUT
set sm.speedv = sm.speedv * TIMER_TIMEOUT
//let r = distance
//speedh = r/time
//Cos(angle) = constant, Sin(value) = constant
//so : (r/time) * Cos/Sin(angle) unit : PER SECOND
//Convert PER SECOND to PER TIMER_TIMEOUT :
// (r/time) * Cos/Sin(angle) * TIMER_TIMEOUT
set r = GetRandomReal(SPHERE_MISSILE_MIN_DIST[this.spellLv], SPHERE_MISSILE_MAX_DIST[this.spellLv])
set angle = GetRandomReal(-bj_PI, bj_PI)
set sm.speedX = (r/time) * Cos(angle) * TIMER_TIMEOUT
set sm.speedY = (r/time) * Sin(angle) * TIMER_TIMEOUT
//Setting up dummy's data
call SetUnitScale(sm.dummy, SPHERE_MISSILE_SCALE, SPHERE_MISSILE_SCALE, SPHERE_MISSILE_SCALE)
call UnitAddRemoveCrowForm(sm.dummy)
call SetUnitFlyHeight(sm.dummy, this.height, 0.)
call SetUnitVertexColor(sm.dummy, SPHERE_MISSILE_COLOR_RED, SPHERE_MISSILE_COLOR_GREEN, SPHERE_MISSILE_COLOR_BLUE, SPHERE_MISSILE_TRANSPARENCY)
set sm.e = AddSpecialEffectTarget(SPHERE_MISSILE_EFFECT_PATH, sm.dummy, ATTACHMENT_PATH)
if sm == 1 then
call TimerStart(SphereMissile.timerSM, TIMER_TIMEOUT, true, function SphereMissile.onPeriodic)
endif
exitwhen i == this.missileCount //exitwhen created missile == number of missile gonna be fired
endloop
call this.destroy()
set this.owner = null
set this.dummy = null
set this.e = null
endif
set this = this.next
endloop
endmethod
endstruct
private struct SphereParticleEffect extends array
//==========Sphere Side Effect Globals============
static timer timerSPE = CreateTimer()
unit dummy
effect e //effect attached to dummy
real x //y coordinate of particle
real y //x coordinate of particle
real distance //distance between sphere and particle
real height //height between sphere and particle
real speedh //speed horizontal (constant)
real speedv //speed verticle (constant)
real destinationHeight //sphere height
real destinationX //sphere x coordinate
real destinationY //sphere y coordinate
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerSPE)
endmethod
implement LinkedList
static method onPeriodic takes nothing returns nothing
local thistype this = thistype(0).next
local real angle
local real x2
loop
exitwhen this == 0
if this.distance >= SPHERE_PARTICLE_END_DIST then
set this.distance = this.distance - this.speedh
set x2 = GetUnitX(this.dummy)-this.destinationX
set angle = Atan2(GetUnitY(this.dummy)-this.destinationY, x2)
//move the particle toward sphere (verticle)
set this.height = this.height - this.speedv
call SetUnitFlyHeight(this.dummy, this.destinationHeight + this.height * Cos(Atan2(this.destinationHeight-this.height,x2)), 0.)
//move the particle toward sphere (horizontal)
set this.x = this.x - this.speedh * Cos(angle)
set this.y = this.y - this.speedh * Sin(angle)
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
else
call DestroyEffect(this.e)
call RemoveUnit(this.dummy)
call this.destroy()
set this.e = null
set this.dummy = null
endif
set this = this.next
endloop
endmethod
endstruct
private struct ElementalChaos extends array
//============Elemental Chaos Globals=============
private static timer timerEC = CreateTimer() //Elemental Chaos Timer
private unit triggerUnit
private unit sphereDummy
private effect sphereEffect
public group unitGroup
private player owner
private integer spellLv
private integer phase //spell phase
private integer releasedPack //amount of energy pack released from sphere
private real targetX //spell target X
private real targetY //spell target Y
private real spellDuration
private real sphereScale
private real sphereScaleConst //sphere scale increasement constant every TIMER_TIMEOUT seconds
private real packReleaseTimePassed //energy pack release interval, increases every TIMER_TIMEOUT seconds
private real sphereGenerateTime
private real sphereHeight
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
//instanceClear will be called if no more instance is running
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerEC)
endmethod
implement LinkedList
private static method onPeriodic takes nothing returns nothing
local SphereParticleEffect particleEffect
local SphereEnergyPack energyPack
local thistype this = thistype(0).next
local real rand
loop
exitwhen this == 0
set spellDuration = spellDuration + TIMER_TIMEOUT
//phase 0 indicates sphere generating phase
if this.phase == SPHERE_GENERATING_PHASE then
//=================================================================================
//Generate Effect of the sphere (the effect shows when sphere is generating)
//=================================================================================
//if (sphere generate time - passed spell timing) >= SPHERE_PARTICLE_STOP_TIMING
if (this.sphereGenerateTime-this.spellDuration) >= SPHERE_PARTICLE_STOP_TIMING then
set particleEffect = SphereParticleEffect.create()
//Setting up particle data
set particleEffect.destinationHeight = this.sphereHeight
set particleEffect.destinationX = this.targetX
set particleEffect.destinationY = this.targetY
set particleEffect.distance = GetRandomReal(SPHERE_PARTICLE_MIN_DIST, SPHERE_PARTICLE_MAX_DIST)
set particleEffect.height = GetRandomReal(SPHERE_PARTICLE_MIN_HEIGHT, SPHERE_PARTICLE_MAX_HEIGHT)
set particleEffect.speedh = GetRandomReal(SPHERE_PARTICLE_MIN_SPEED, SPHERE_PARTICLE_MAX_SPEED)
//speedv = height / time
//time = ditance / speedh
set particleEffect.speedv = particleEffect.height/(particleEffect.distance/particleEffect.speedh)
set particleEffect.x = this.targetX + particleEffect.distance * Cos(GetRandomReal(-bj_PI, bj_PI))
set particleEffect.y = this.targetY + particleEffect.distance * Sin(GetRandomReal(-bj_PI, bj_PI))
set particleEffect.dummy = CreateUnit(this.owner, DUMMY_UNIT_ID, particleEffect.x, particleEffect.y, 0)
set particleEffect.e = AddSpecialEffectTarget(SPHERE_PARTICLE_EFFECT_PATH, particleEffect.dummy, ATTACHMENT_PATH)
//Set unit scale randomly
set rand = GetRandomReal(SPHERE_PARTICLE_MIN_SCALE, SPHERE_PARTICLE_MAX_SCALE)
call SetUnitScale(particleEffect.dummy, rand, rand, rand)
//add and remove crow form
call UnitAddRemoveCrowForm(particleEffect.dummy)
//rendomly obtain a number so that height can be appear lower or higher thn the sphere
if GetRandomInt(0, 1) == 1 then
set particleEffect.height = particleEffect.height * -1
endif
//set unit height
call SetUnitFlyHeight(particleEffect.dummy, this.sphereHeight + particleEffect.height, 0)
//adjust the color compoennt of the unit
call SetUnitVertexColor(particleEffect.dummy, SPHERE_PARTICLE_COLOR_RED, SPHERE_PARTICLE_COLOR_GREEN, SPHERE_PARTICLE_COLOR_BLUE, SPHERE_PARTICLE_TRANSPARENCY)
if particleEffect == 1 then
call TimerStart(SphereParticleEffect.timerSPE, TIMER_TIMEOUT, true, function SphereParticleEffect.onPeriodic)
endif
//Setting sphere scale, reason it is inside the block is to prevent sphere increasing scale when no more particle effects
//are generated.
set this.sphereScale = this.sphereScale + this.sphereScaleConst
call SetUnitScale(this.sphereDummy, this.sphereScale, this.sphereScale, this.sphereScale)
endif
//=================================================================================
//===========================End Sphere Generate Effect============================
//=================================================================================
//Check if the spell duration >= sphere generate time
if this.spellDuration >= this.sphereGenerateTime then
//Sphere generate phase ends, switch spell phase to 1
set this.phase = SPHERE_RELEASING_PHASE
endif
elseif this.phase == SPHERE_RELEASING_PHASE then
//If released energy pack count < SPHERE_RELEASE_PACK_MAX[spellLv]
if this.releasedPack < SPHERE_RELEASE_PACK_MAX[this.spellLv] then
/************************************************************************************************************/
/**/if this.packReleaseTimePassed >= SPHERE_PACK_RELEASE_INTERVAL then
/**/ set this.packReleaseTimePassed = 0.
/**/ set this.releasedPack = this.releasedPack + 1
/**/
/**/ set energyPack = SphereEnergyPack.create()
/**/
/**/ //Setting up energy pack data
/**/ set energyPack.destinationDistance = GetRandomReal(SPHERE_ENERGY_PACK_MIN_DIST, SPHERE_ENERGY_PACK_MAX_DIST) //final distance between sphere and energy pack
/**/ set energyPack.speedh = GetRandomReal(SPHERE_ENERGY_PACK_MIN_SPEED, SPHERE_ENERGY_PACK_MAX_SPEED)
/**/ //Height/time
/**/ set energyPack.speedv = GetRandomReal(SPHERE_ENERGY_PACK_MIN_HEIGHT, SPHERE_ENERGY_PACK_MAX_HEIGHT)/*
*//(energyPack.destinationDistance/energyPack.speedh) //formula same as particleEffect
/**/ set energyPack.remainingTime = GetRandomReal(SPHERE_ENERGY_PACK_EXPLODE_MIN_TIME, SPHERE_ENERGY_PACK_EXPLODE_MAX_TIME)
/**/ set energyPack.missileCount = GetRandomInt(SPHERE_ENERGY_PACK_MIN_MISSILE[this.spellLv], SPHERE_ENERGY_PACK_MAX_MISSILE[this.spellLv])
/**/ set energyPack.spellLv = this.spellLv
/**/ set energyPack.x = this.targetX
/**/ set energyPack.y = this.targetY
/**/ set energyPack.height = this.sphereHeight
/**/ set energyPack.owner = this.owner
/**/ set energyPack.ec = this
/**/
/**/ //create a unit to obtain the destinationX and destinationY
/**/ set rand = GetRandomReal(-bj_PI, bj_PI)
/**/ set energyPack.dummy = CreateUnit(this.owner, DUMMY_UNIT_ID, this.targetX + energyPack.destinationDistance * Cos(rand), this.targetY + energyPack.destinationDistance * Sin(rand), 0)
/**/ set energyPack.destinationX = GetUnitX(energyPack.dummy)
/**/ set energyPack.destinationY = GetUnitY(energyPack.dummy)
/**/ set energyPack.e = AddSpecialEffectTarget(SPHERE_ENERGY_PACK_EFFECT_PATH, energyPack.dummy, ATTACHMENT_PATH)
/**/
/**/ //Move the dummy unit back where it should originally be
/**/ call SetUnitX(energyPack.dummy, this.targetX)
/**/ call SetUnitY(energyPack.dummy, this.targetY)
/**/
/**/ //Set unit scale
/**/ set rand = GetRandomReal(SPHERE_ENERGY_PACK_MIN_SCALE, SPHERE_ENERGY_PACK_MAX_SCALE)
/**/ call SetUnitScale(energyPack.dummy, rand, rand, rand)
/**/
/**/ //set unit color
/**/ call SetUnitVertexColor(energyPack.dummy, SPHERE_ENERGY_PACK_COLOR_RED, SPHERE_ENERGY_PACK_COLOR_GREEN, SPHERE_ENERGY_PACK_COLOR_BLUE, SPHERE_ENERGY_PACK_TRANSPARENCY)
/**/
/**/ //Set unit fly height
/**/ call UnitAddRemoveCrowForm(energyPack.dummy)
/**/ call SetUnitFlyHeight(energyPack.dummy, this.sphereHeight, 0.)
/**/
/**/ if energyPack == 1 then
/**/ call TimerStart(SphereEnergyPack.timerSEP, TIMER_TIMEOUT, true, function SphereEnergyPack.onPeriodic)
/**/ endif
/**/else
/**/ //increase the time passed by TIMER_TIMEOUT seconds
/**/ set this.packReleaseTimePassed = this.packReleaseTimePassed + TIMER_TIMEOUT
/**/endif
/************************************************************************************************************/
else
set this.phase = SPHERE_AWAITING_PHASE
endif
endif
set this = this.next
endloop
endmethod
private static method onCast takes nothing returns boolean
local thistype this
if GetSpellAbilityId() == SPELL_ID then
set this = thistype.create() //create a new instance
//Storing data
set this.triggerUnit = GetTriggerUnit()
set this.owner = GetOwningPlayer(this.triggerUnit)
set this.targetX = GetSpellTargetX()
set this.targetY = GetSpellTargetY()
set this.unitGroup = CreateGroup()
set this.spellLv = GetUnitAbilityLevel(this.triggerUnit, SPELL_ID)
set this.phase = SPHERE_GENERATING_PHASE
set this.sphereGenerateTime = SPHERE_GENERATE_TIME[this.spellLv]
set this.sphereHeight = SPHERE_HEIGHT[this.spellLv]
set this.sphereScale = SPHERE_INITIAL_SCALE
//sphereScaleConst is dependent on SPHERE_MAX_SCALE, SPHERE_GENERATE_TIME, TIMER_TIMEOUT and SHPERE_PARTICLE_STOP_TIMING
//(this.sphereGenerateTime-SPHERE_PARTICLE_STOP_TIMING) : as sphere stop increase scale if no more particle is generating
// divide by TIMER_TIMEOUT : to obtain the overall running times
// SPHERE_MAX_SCALE[this.spellLv] : obtain the maximum scale of the sphere
// maximum scale / running times : so that a constant is obtained for scale increasement every TIMER_TIMEOUT
set this.sphereScaleConst = SPHERE_MAX_SCALE[this.spellLv]/((this.sphereGenerateTime-SPHERE_PARTICLE_STOP_TIMING)/TIMER_TIMEOUT)
set this.spellDuration = 0.
set this.packReleaseTimePassed = 0.
set this.releasedPack = 0
set this.sphereDummy = CreateUnit(this.owner, DUMMY_UNIT_ID, this.targetX, this.targetY, 0.)
//Add and remove crow form
call UnitAddRemoveCrowForm(this.sphereDummy)
//Set the unit height
call SetUnitFlyHeight(this.sphereDummy, this.sphereHeight, 0.)
//Setting the sphere to initial scale value
call SetUnitScale(this.sphereDummy, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE)
//Setting the sphere color
call SetUnitVertexColor(this.sphereDummy, SPHERE_COLOR_RED, SPHERE_COLOR_GREEN, SPHERE_COLOR_BLUE, SPHERE_TRANSPARENCY)
//Add the sphere effect to the unit
set this.sphereEffect = AddSpecialEffectTarget(SPHERE_EFFECT_PATH, this.sphereDummy, ATTACHMENT_PATH)
if this == 1 then
call TimerStart(timerEC, TIMER_TIMEOUT, true, function thistype.onPeriodic)
endif
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerAddCondition(t, Condition(function thistype.onCast))
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
set t = null
endmethod
endstruct
endscope
Is Unit Indexer allowed? I prefer that instead of using Dynamic Indexing, that is if it's permitted.
Is Unit Indexer allowed? I prefer that instead of using Dynamic Indexing, that is if it's permitted.
...But what should not happen is that your final result will mainly just be the usage of other's work.
Unit Indexer should definitely be allowed, but using that will narrow your code down. So it's up to you to use it. Personally, if a system is not that necessary I'd prefer to code it into my own code for my own spell's specific functionality instead of using the system as a whole.
He meant using external library could shorten your code length.What do you mean it will narrow the code down?
He meant using external library could shorten your code length.
Personally, I think using external library wouldn't be a problem. As long as your code do show out what you got.
Summons a formless ectoplasm that can shift into any non-hero and non-ancient unit. Ectoplasm can trick the enemies by its form, moving and attacking like others creep. If ectoplasm can get a new form before the lifetime gone, it will permanent until it dies. When dies, ectoplasm will damage around its and sending a virus which slowing and damaging per second to enemies on radius 300 AOE for 5 seconds.
Level 1 - Deals 100 damage, 60 per second.
Level 2 - Deals 200 damage, 60 per second.
Level 3 - Deals 300 damage, 60 per second.
Cooldown : 60/55/50
Mana Cost : 150/175/200
Ectoplasm Lifetime : 20 seconds
At last, my second WIP of summoning spell. I still using basic effect, so I want to make a simple but cool effect when summoning and taking form. I also edit the tooltip, the spell will like this:
Is anyone know how to make .gif image in better way? Because mine in original size is 10 MB, and can't be upload to this siteSo i must upload it in other website....
Oh, you mean similar to your StageID tutorial ? Hm, will think about that, thanks!
scope Devourer initializer Init
globals
/*
* Timeout for all timers running in this spell
*/
private constant real TIMEOUT = 0.03125
/*
* The ability id of the spell
*/
private constant integer ABIL_ID = 0
/*
* rawcode id for the creeps (sfx units)
*/
private constant integer CREEP_ID = 0
//------------------------------------------------------
/*
* Spell on cast
*/
//------------------------------------------------------
/*
* Required trees for summoning
*/
private constant integer REQUIRED_TREES = 6
private constant integer REQUIRED_TREES_PER_LVL = -1
/*
* Radius for checking the required trees
*/
private constant real TREE_RADIUS_CHECK = 256
private constant real TREE_RADIUS_CHECK_PER_LVL = 0
/*
* Spawn delay of the devourer
*/
private constant real SPAWN_DELAY = 6
private constant real SPAWN_DELAY_PER_LVL = -1
/*
* How long the the devourer last
*/
private constant real TIMED_LIFE = 10
private constant real TIMED_LIFE_PER_LVL = 10
//------------------------------------------------------
/*
* Souls configurables
*/
//------------------------------------------------------
/*
* souls drop when unit dies in the given radius
*/
private constant real SOUL_DROP_RADIUS = 800
private constant real SOUL_DROP_RADIUS_PER_LVL = 0
/*
* How long does the souls last
*/
private constant real SOUL_DROP_DURATION = 10
private constant real SOUL_DROP_DURATION_PER_LVL = 0
/*
* Required distance to activate to souls
*/
private constant real SOUL_FEED_RADIUS = 500
private constant real SOUL_FEED_RADIUS_PER_LVL = 100
/*
* Speed of the souls
*/
private constant real SOUL_SPEED = 1000
/*
* Bonus life duration added when souls reaches the devourer
*/
private constant real TIMED_LIFE_BONUS = 0
private constant real TIMED_LIFE_BONUS_PER_LVL = 1
//------------------------------------------------------
/*
* Mana Release/Shockwave Configurables
*/
//------------------------------------------------------
/*
* Consumes all mana that are lost within the given range
*/
private constant real MANA_FEED_RADIUS = 1000
private constant real MANA_FEED_RADIUS_PER_LVL = 0
/*
* Required mana to release the shockwave
*/
private constant real MANA_RELEASE_REQ = 450
private constant real MANA_RELEASE_REQ_PER_LVL = -50
/*
* How large the shockwave is
*/
private constant real MANA_RELEASE_RADIUS = 600
private constant real MANA_RELEASE_RADIUS_PER_LVL = 0
/*
* How long does the shockwave grow
* from it's release point to it's max radius
*/
private constant real MANA_RELEASE_GROW_TIME = 2
/*
* Damage dealt to the affected units
*/
private constant real MANA_RELEASE_DMG = 256
private constant real MANA_RELEASE_DMG_PER_LVL = 0
private constant attacktype MANA_RELEASE_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype MANA_RELEASE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Is the shockwave locked to the devourer?
* locked = follows the devourer's position instead
* of the position of release.
*/
private constant boolean MANA_RELEASED_LOCKED = true
/*
* Cooldown of the shockwave
*/
private constant real MANA_RELEASE_CD = 0
private constant real MANA_RELEASE_CD_PER_LVL = 0
/*
* Does the devourer collect mana even
* if the shockwave is on cooldown?
*/
private constant boolean COLLECT_MANA_ON_CD = false
//------------------------------------------------------
/*
* Swarm configurables
*/
//------------------------------------------------------
/*
* Starting polar offset of the swarms
*/
private constant real SWARM_SPAWN_OFFSET = 128
private constant real SWARM_SPAWN_OFFSET_PER_LVL = 0
/*
* How far doest the swarm travel
* (from the offset)
*/
private constant real SWARM_TRAVEL_DIST = 768
private constant real SWARM_TRAVEL_DIST_PER_LVL = 0
/*
* Radius of the swarm
*/
private constant real SWARM_RADIUS = 128
private constant real SWARM_RADIUS_PER_LVL = 0
/*
* Damage dealt by the swarm
*/
private constant real SWARM_DAMAGE = 0
private constant real SWARM_DAMAGE_PER_LVL = 75
private constant attacktype SWARM_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype SWARM_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Swarm cooldown
*/
private constant real SWARM_CD = 12
private constant real SWARM_CD_PER_LVL = -2
/*
* Swarm speed
*/
private constant real SWARM_SPEED = 1024
//------------------------------------------------------
/*
* On Cast Effects
*/
//------------------------------------------------------
/*
* Portal Fields
*/
/*
* Model of the portal
*/
private constant string PORTAL_MODEL = ""
/*
* Height of the portal
*/
private constant real PORTAL_Z = 64
/*
* Starting appearance of the portal
*/
private constant real PORTAL_START_SCALE = 0
private constant integer PORTAL_START_ALPHA = 255
private constant integer PORTAL_START_RED = 255
private constant integer PORTAL_START_GREEN = 255
private constant integer PORTAL_START_BLUE = 255
/*
* End appearance of the portal
*/
private constant real PORTAL_END_SCALE = 2
private constant integer PORTAL_END_ALPHA = 255
private constant integer PORTAL_END_RED = 255
private constant integer PORTAL_END_GREEN = 255
private constant integer PORTAL_END_BLUE = 255
/*
* Portal shard fields
*/
/*
* Show the portal shards?
*/
private constant boolean SHOW_SHARDS = true
/*
* Model of the shards
*/
private constant string SHARD_MODEL = ""
/*
* Start appearance of the shards
*/
private constant real SHARD_START_SCALE = 1
private constant integer SHARD_START_ALPHA = 255
private constant integer SHARD_START_RED = 255
private constant integer SHARD_START_GREEN = 255
private constant integer SHARD_START_BLUE = 255
/*
* End appearance of the shards
*/
private constant real SHARD_END_SCALE = 0
private constant integer SHARD_END_ALPHA = 255
private constant integer SHARD_END_RED = 255
private constant integer SHARD_END_GREEN = 255
private constant integer SHARD_END_BLUE = 255
/*
* Spawn distance of the shards
*/
private constant real SHARD_DISTANCE = 500
private constant real SHARD_DISTANCE_VAR = 125
/*
* Spawn angular variation of the shards
*/
private constant real SHARD_ANGLE = 0
private constant real SHARD_ANGLE_VAR = bj_PI
private constant real SHARD_Z_ANGLE = bj_PI/2
private constant real SHARD_Z_ANGLE_VAR = SHARD_Z_ANGLE
/*
* How often shards spawn
*/
private constant real SHARD_PERIOD = 0.0625
/*
* How many shards spawn per period
*/
private constant real SHARD_COUNT = 2
/*
* Speed of the shards
*/
private constant real SHARD_SPEED = 500
private constant real SHARD_SPEED_VAR = 125
//------------------------------------------------------
/*
* On Spawn effects
*/
private constant string ON_SPAWN_SFX = ""
/*
* Show creep nova
*/
private constant boolean SHOW_CREEP_NOVA = true
/*
* Start appearance of the nova creeps
*/
private constant real NCREEP_START_SCALE = 1
private constant integer NCREEP_START_ALPHA = 200
private constant integer NCREEP_START_RED = 0
private constant integer NCREEP_START_GREEN = 0
private constant integer NCREEP_START_BLUE = 0
/*
* End appearance of the nova creeps
*/
private constant real NCREEP_END_SCALE = 1
private constant integer NCREEP_END_ALPHA = 0
private constant integer NCREEP_END_RED = 0
private constant integer NCREEP_END_GREEN = 0
private constant integer NCREEP_END_BLUE = 0
/*
* Number of creeps released
*/
private constant integer NCREEP_COUNT = 32
/*
* Speed of the creeps
*/
private constant real NCREEP_SPEED = 375
private constant real NCREEP_SPEED_VAR = 125
/*
* Creep life time
*/
private constant real NCREEP_TIME = 1.25
/*
* Animation played by the creep
*/
private constant string NCREEP_ANIM = "walk"
/*
* Nova creep height
*/
private constant real NCREEP_Z = 0
//------------------------------------------------------
/*
* Devourer appearances
*/
private constant real DEVOURER_SCALE = 0.75
private constant real DEVOURER_SCALE_PER_LVL = 0.25
private constant integer DEVOURER_ALPHA = 200
private constant integer DEVOURER_RED = 100
private constant integer DEVOURER_GREEN = 100
private constant integer DEVOURER_BLUE = 100
private constant real DEVOURER_Z = 0
private constant string DEVOURER_DEATH_SFX = ""
//------------------------------------------------------
/*
* Shadow Trail fields
*/
private constant boolean SHOW_TRAIL = true
/*
* Trail period
*/
private constant real TRAIL_PERIOD = 0.09375
/*
* Trail duration
*/
private constant real TRAIL_DURATION = 1.5
/*
* Trail animation
*/
private constant string TRAIL_ANIM = "walk"
//------------------------------------------------------
/*
* Aura fields
*/
private constant boolean SHOW_AURA = true
/*
* Aura creep spawn period
*/
private constant real AURA_SPAWN_PERIOD = 0.125
/*
* Number of creep spawn
*/
private constant integer ACREEP_COUNT = 1
/*
* Start appearance of the aura creeps
*/
private constant real ACREEP_START_SCALE = 0.25
private constant integer ACREEP_START_ALPHA = 200
private constant integer ACREEP_START_RED = 0
private constant integer ACREEP_START_GREEN = 0
private constant integer ACREEP_START_BLUE = 0
/*
* End appearance of the aura creeps
*/
private constant real ACREEP_END_SCALE = 0.25
private constant integer ACREEP_END_ALPHA = 0
private constant integer ACREEP_END_RED = 0
private constant integer ACREEP_END_GREEN = 0
private constant integer ACREEP_END_BLUE = 0
/*
* Aura creep speed
*/
private constant real ACREEP_SPEED = 200
private constant real ACREEP_SPEED_VAR = 50
/*
* Aura creep duration
*/
private constant real ACREEP_TIME = 1.25
/*
* Aura creep animation
*/
private constant string ACREEP_ANIM = "walk"
/*
* Aura creep height
*/
private constant real ACREEP_Z = 0
//------------------------------------------------------
/*
* Souls
*/
private constant string SOUL_MODEL = ""
private constant real SOUL_SCALE = 1
private constant integer SOUL_ALPHA = 128
private constant integer SOUL_RED = 255
private constant integer SOUL_GREEN = 255
private constant integer SOUL_BLUE = 255
//------------------------------------------------------
/*
* Mana feed
*/
private constant string MANA_FEED_SFX = ""
private constant string MANA_FEED_ATTACH = ""
//------------------------------------------------------
/*
* Shockwave fields
*/
private constant string MANA_RELEASE_SFX = ""
/*
* Number of shockwave segments
*/
private constant integer WAVE_COUNT = 12
/*
* Shockwave appearance
*/
private constant string WAVE_MODEL = ""
private constant real WAVE_SCALE = 1.25
private constant integer WAVE_ALPHA = 255
private constant integer WAVE_RED = 255
private constant integer WAVE_GREEN = 255
private constant integer WAVE_BLUE = 255
private constant real WAVE_Z = 32
//------------------------------------------------------
/*
* Swarm fields
*/
private constant string SWARM_RELEASE_SFX = ""
private constant string SWARM_MODEL = ""
/*
* Swarm appearance
*/
private constant real SWARM_SCALE = 1
private constant integer SWARM_RED = 255
private constant integer SWARM_GREEN = 255
private constant integer SWARM_BLUE = 255
private constant integer SWARM_ALPHA = 255
/*
* Number of swarm segment
*/
private constant integer SWARM_COUNT = 5
/*
* Arc rate of the Swarm
*/
private constant real SWARM_ARC = 0.25
endglobals
/*
* Setup Devourer IDs
*/
globals
private integer array DEVOURER_ID
endglobals
private function Init takes nothing returns nothing
set DEVOURER_ID[1] = 0
set DEVOURER_ID[2] = 0
set DEVOURER_ID[3] = 0
endfunction
/*
* For checking alive units
*/
native UnitAlive takes unit u returns boolean
/*
* For calculating Values with respect to levels
*/
private function GetLevelValueR takes real base, real increment, integer level returns real
return base + increment*level
endfunction
private function GetLevelValueI takes integer base, integer increment, integer level returns integer
return base + increment*level
endfunction
/*
* For calculating value variance
*/
private function GetVarianceR takes real base, real variance returns real
if variance == 0 then
return base
endif
return GetRandomReal(base - variance, base + variance)
endfunction
private function GetVarianceI takes integer base, integer variance returns integer
if variance == 0 then
return base
endif
return GetRandomInt(base - variance, base + variance)
endfunction
/*
* For calculating value over time
*/
private function LinearR takes real a, real b, real t returns real
return a + (b - a)*t
endfunction
private function LinearI takes integer a, integer b, real t returns integer
return R2I(LinearR(I2R(a), I2R(b), t))
endfunction
/**************************************
*
* For getting the location surface z
*
**************************************/
static if (not LIBRARY_ZLibrary) then
globals
private constant location p = Location(0.0, 0.0)
endglobals
function GetSurfaceZ takes real x, real y returns real
call MoveLocation(p, x, y)
return GetLocationZ(p)
endfunction
endif
/*
* For handling lists and periods
*/
//! textmacro DEVOURER_LIST_TIMER
private static thistype array next
private static thistype array prev
private static constant timer t = CreateTimer()
private static method insert takes real timeout, code c returns thistype
local thistype this = allocate()
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
if prev[this] == 0 then
call TimerStart(t, timeout, true, c)
endif
return this
endmethod
private method remove takes nothing returns nothing
call deallocate()
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
endmethod
//! endtextmacro
/*
* Structs for holding appearance values and position values
*/
private struct Appearance
integer red
integer green
integer blue
integer alpha
real scale
endstruct
private struct Vector
real x
real y
real z
endstruct
/*
* Struct for timed appearances (fade, color change, etc.)
*/
private struct TimedAppearance
//! runtextmacro DEVOURER_LIST_TIMER()
private unit u
private Appearance start
private Appearance end
private real current
private real max
method destroy takes nothing returns nothing
call remove()
set u = null
call start.destroy()
call end.destroy()
set current = 0
set max = 0
endmethod
private static method period takes nothing returns nothing
local thistype this = next[0]
local real pct
local integer a
local integer r
local integer g
local integer b
local real s
loop
exitwhen 0 == this
if current < max then
set current = current + TIMEOUT
set pct = current/max
set a = LinearI(start.alpha, end.alpha, pct)
set r = LinearI(start.red, end.red, pct)
set g = LinearI(start.green, end.green, pct)
set b = LinearI(start.blue, end.blue, pct)
set s = LinearR(start.scale, end.scale, pct)
call SetUnitVertexColor(u, r, g, b, a)
call SetUnitScale(u, s, 0, 0)
else
call destroy()
endif
set this = next[this]
endloop
endmethod
static method add takes unit temp, Appearance startColor, Appearance endColor, real maxTime returns nothing
local thistype this = insert(TIMEOUT, function thistype.period)
set start = startColor
set end = endColor
set current = 0
set max = maxTime
set u = temp
endmethod
endstruct
/*
* Struct for effect dummies
*/
private struct Particle
private unit u
private effect mdl
static method create takes string sfx, real x, real y, real z, real face returns thistype
local thistype this = allocate()
set u = GetRecycledUnit(x, y, false, face)
call SetUnitFlyHeight(u, z - GetSurfaceZ(x, y), 0)
set mdl = AddSpecialEffectTarget(sfx, u, "origin")
return this
endmethod
method destroy takes nothing returns nothing
call AddRecycleTimer(u, 2.0)
call DestroyEffect(mdl)
set u = null
set mdl = null
endmethod
endstruct
/*
* Projectile motion
*/
module Missile
private Vector offset
private unit target
private real size
private real speed
private real missileTime
private method destroyMissile takes nothing returns nothing
call onImpact(target)
set target = null
set speed = 0
set missileTime = 0
endmethod
private method move takes nothing returns nothing
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local real uz = GetSurfaceZ(ux, uy) + GetUnitFlyHeight(u)
local real tx
local real ty
local real tz
local real dx
local real dy
local real dz
local real a2
local real a3
if UnitAlive(target) then
set tx = GetUnitX(target)
set ty = GetUnitY(target)
set tz = GetSurfaceZ(tx, ty) + GetUnitFlyHeight(target)
set dx = tx - ux
set dy = ty - uy
set dz = tz - uz
if GetMagnitude3d(dx, dy, dz) <= size then
call destroyMissile()
endif
set a2 = Atan2(dy, dx)
set a3 = Atan2(dz, GetMagnitude2d(dx, dy))
elseif missileTime > 0 then
set missileTime = missileTime - TIMEOUT
set ux = GetBoundedX(ux + offset.x)
set uy = GetBoundedY(uy + offset.y)
call SetUnitX(u, ux)
call SetUnitY(u, uy)
call SetUnitFlyHeight(u, (uz + offset.z) - GetSurfaceZ(ux, uy), 0)
else
call destroyMissile()
endif
endmethod
method setTarget takes unit temp returns nothing
set target = temp
set size = GetUnitCollision(temp)
endmethod
endmodule
/*
* Struct for creeps
*/
private struct Creep
//! runtextmacro DEVOURER_LIST_TIMER()
private unit u
method onImpact takes unit temp returns nothing
endmethod
implement Missile
endstruct
private struct Soul
//! runtextmacro DEVOURER_LIST_TIMER()
private unit u
method onImpact takes unit temp returns nothing
endmethod
implement Missile
endstruct
private struct Swarm
//! runtextmacro DEVOURER_LIST_TIMER()
endstruct
private struct Shockwave
//! runtextmacro DEVOURER_LIST_TIMER()
endstruct
private struct Devourer
//! runtextmacro DEVOURER_LIST_TIMER()
endstruct
private struct SummonDevourer
//! runtextmacro DEVOURER_LIST_TIMER()
endstruct
endscope
Well, that's not what I meant XD
I meant as in something like the ability reacting differently each cast
like activate to start collecting orbs from corpses, activate again to detonate orbs, activate again to go back to collecting, etc.
StageIDs would be a method you could use to implement it but not what I meant strictly speaking XD
WIP# 2
Probably going to make a test effect tomorrow and share a GIF
JASS:scope Devourer initializer Init globals /* * Timeout for all timers running in this spell */ private constant real TIMEOUT = 0.03125 /* * The ability id of the spell */ private constant integer ABIL_ID = 0 /* * rawcode id for the creeps (sfx units) */ private constant integer CREEP_ID = 0 //------------------------------------------------------ /* * Spell on cast */ //------------------------------------------------------ /* * Required trees for summoning */ private constant integer REQUIRED_TREES = 6 private constant integer REQUIRED_TREES_PER_LVL = -1 /* * Radius for checking the required trees */ private constant real TREE_RADIUS_CHECK = 256 private constant real TREE_RADIUS_CHECK_PER_LVL = 0 /* * Spawn delay of the devourer */ private constant real SPAWN_DELAY = 6 private constant real SPAWN_DELAY_PER_LVL = -1 /* * How long the the devourer last */ private constant real TIMED_LIFE = 10 private constant real TIMED_LIFE_PER_LVL = 10 //------------------------------------------------------ /* * Souls configurables */ //------------------------------------------------------ /* * souls drop when unit dies in the given radius */ private constant real SOUL_DROP_RADIUS = 800 private constant real SOUL_DROP_RADIUS_PER_LVL = 0 /* * How long does the souls last */ private constant real SOUL_DROP_DURATION = 10 private constant real SOUL_DROP_DURATION_PER_LVL = 0 /* * Required distance to activate to souls */ private constant real SOUL_FEED_RADIUS = 500 private constant real SOUL_FEED_RADIUS_PER_LVL = 100 /* * Speed of the souls */ private constant real SOUL_SPEED = 1000 /* * Bonus life duration added when souls reaches the devourer */ private constant real TIMED_LIFE_BONUS = 0 private constant real TIMED_LIFE_BONUS_PER_LVL = 1 //------------------------------------------------------ /* * Mana Release/Shockwave Configurables */ //------------------------------------------------------ /* * Consumes all mana that are lost within the given range */ private constant real MANA_FEED_RADIUS = 1000 private constant real MANA_FEED_RADIUS_PER_LVL = 0 /* * Required mana to release the shockwave */ private constant real MANA_RELEASE_REQ = 450 private constant real MANA_RELEASE_REQ_PER_LVL = -50 /* * How large the shockwave is */ private constant real MANA_RELEASE_RADIUS = 600 private constant real MANA_RELEASE_RADIUS_PER_LVL = 0 /* * How long does the shockwave grow * from it's release point to it's max radius */ private constant real MANA_RELEASE_GROW_TIME = 2 /* * Damage dealt to the affected units */ private constant real MANA_RELEASE_DMG = 256 private constant real MANA_RELEASE_DMG_PER_LVL = 0 private constant attacktype MANA_RELEASE_ATKTYPE = ATTACK_TYPE_CHAOS private constant damagetype MANA_RELEASE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL /* * Is the shockwave locked to the devourer? * locked = follows the devourer's position instead * of the position of release. */ private constant boolean MANA_RELEASED_LOCKED = true /* * Cooldown of the shockwave */ private constant real MANA_RELEASE_CD = 0 private constant real MANA_RELEASE_CD_PER_LVL = 0 /* * Does the devourer collect mana even * if the shockwave is on cooldown? */ private constant boolean COLLECT_MANA_ON_CD = false //------------------------------------------------------ /* * Swarm configurables */ //------------------------------------------------------ /* * Starting polar offset of the swarms */ private constant real SWARM_SPAWN_OFFSET = 128 private constant real SWARM_SPAWN_OFFSET_PER_LVL = 0 /* * How far doest the swarm travel * (from the offset) */ private constant real SWARM_TRAVEL_DIST = 768 private constant real SWARM_TRAVEL_DIST_PER_LVL = 0 /* * Radius of the swarm */ private constant real SWARM_RADIUS = 128 private constant real SWARM_RADIUS_PER_LVL = 0 /* * Damage dealt by the swarm */ private constant real SWARM_DAMAGE = 0 private constant real SWARM_DAMAGE_PER_LVL = 75 private constant attacktype SWARM_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS private constant damagetype SWARM_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL /* * Swarm cooldown */ private constant real SWARM_CD = 12 private constant real SWARM_CD_PER_LVL = -2 /* * Swarm speed */ private constant real SWARM_SPEED = 1024 //------------------------------------------------------ /* * On Cast Effects */ //------------------------------------------------------ /* * Portal Fields */ /* * Model of the portal */ private constant string PORTAL_MODEL = "" /* * Height of the portal */ private constant real PORTAL_Z = 64 /* * Starting appearance of the portal */ private constant real PORTAL_START_SCALE = 0 private constant integer PORTAL_START_ALPHA = 255 private constant integer PORTAL_START_RED = 255 private constant integer PORTAL_START_GREEN = 255 private constant integer PORTAL_START_BLUE = 255 /* * End appearance of the portal */ private constant real PORTAL_END_SCALE = 2 private constant integer PORTAL_END_ALPHA = 255 private constant integer PORTAL_END_RED = 255 private constant integer PORTAL_END_GREEN = 255 private constant integer PORTAL_END_BLUE = 255 /* * Portal shard fields */ /* * Show the portal shards? */ private constant boolean SHOW_SHARDS = true /* * Model of the shards */ private constant string SHARD_MODEL = "" /* * Start appearance of the shards */ private constant real SHARD_START_SCALE = 1 private constant integer SHARD_START_ALPHA = 255 private constant integer SHARD_START_RED = 255 private constant integer SHARD_START_GREEN = 255 private constant integer SHARD_START_BLUE = 255 /* * End appearance of the shards */ private constant real SHARD_END_SCALE = 0 private constant integer SHARD_END_ALPHA = 255 private constant integer SHARD_END_RED = 255 private constant integer SHARD_END_GREEN = 255 private constant integer SHARD_END_BLUE = 255 /* * Spawn distance of the shards */ private constant real SHARD_DISTANCE = 500 private constant real SHARD_DISTANCE_VAR = 125 /* * Spawn angular variation of the shards */ private constant real SHARD_ANGLE = 0 private constant real SHARD_ANGLE_VAR = bj_PI private constant real SHARD_Z_ANGLE = bj_PI/2 private constant real SHARD_Z_ANGLE_VAR = SHARD_Z_ANGLE /* * How often shards spawn */ private constant real SHARD_PERIOD = 0.0625 /* * How many shards spawn per period */ private constant real SHARD_COUNT = 2 /* * Speed of the shards */ private constant real SHARD_SPEED = 500 private constant real SHARD_SPEED_VAR = 125 //------------------------------------------------------ /* * On Spawn effects */ private constant string ON_SPAWN_SFX = "" /* * Show creep nova */ private constant boolean SHOW_CREEP_NOVA = true /* * Start appearance of the nova creeps */ private constant real NCREEP_START_SCALE = 1 private constant integer NCREEP_START_ALPHA = 200 private constant integer NCREEP_START_RED = 0 private constant integer NCREEP_START_GREEN = 0 private constant integer NCREEP_START_BLUE = 0 /* * End appearance of the nova creeps */ private constant real NCREEP_END_SCALE = 1 private constant integer NCREEP_END_ALPHA = 0 private constant integer NCREEP_END_RED = 0 private constant integer NCREEP_END_GREEN = 0 private constant integer NCREEP_END_BLUE = 0 /* * Number of creeps released */ private constant integer NCREEP_COUNT = 32 /* * Speed of the creeps */ private constant real NCREEP_SPEED = 375 private constant real NCREEP_SPEED_VAR = 125 /* * Creep life time */ private constant real NCREEP_TIME = 1.25 /* * Animation played by the creep */ private constant string NCREEP_ANIM = "walk" /* * Nova creep height */ private constant real NCREEP_Z = 0 //------------------------------------------------------ /* * Devourer appearances */ private constant real DEVOURER_SCALE = 0.75 private constant real DEVOURER_SCALE_PER_LVL = 0.25 private constant integer DEVOURER_ALPHA = 200 private constant integer DEVOURER_RED = 100 private constant integer DEVOURER_GREEN = 100 private constant integer DEVOURER_BLUE = 100 private constant real DEVOURER_Z = 0 private constant string DEVOURER_DEATH_SFX = "" //------------------------------------------------------ /* * Shadow Trail fields */ private constant boolean SHOW_TRAIL = true /* * Trail period */ private constant real TRAIL_PERIOD = 0.09375 /* * Trail duration */ private constant real TRAIL_DURATION = 1.5 /* * Trail animation */ private constant string TRAIL_ANIM = "walk" //------------------------------------------------------ /* * Aura fields */ private constant boolean SHOW_AURA = true /* * Aura creep spawn period */ private constant real AURA_SPAWN_PERIOD = 0.125 /* * Number of creep spawn */ private constant integer ACREEP_COUNT = 1 /* * Start appearance of the aura creeps */ private constant real ACREEP_START_SCALE = 0.25 private constant integer ACREEP_START_ALPHA = 200 private constant integer ACREEP_START_RED = 0 private constant integer ACREEP_START_GREEN = 0 private constant integer ACREEP_START_BLUE = 0 /* * End appearance of the aura creeps */ private constant real ACREEP_END_SCALE = 0.25 private constant integer ACREEP_END_ALPHA = 0 private constant integer ACREEP_END_RED = 0 private constant integer ACREEP_END_GREEN = 0 private constant integer ACREEP_END_BLUE = 0 /* * Aura creep speed */ private constant real ACREEP_SPEED = 200 private constant real ACREEP_SPEED_VAR = 50 /* * Aura creep duration */ private constant real ACREEP_TIME = 1.25 /* * Aura creep animation */ private constant string ACREEP_ANIM = "walk" /* * Aura creep height */ private constant real ACREEP_Z = 0 //------------------------------------------------------ /* * Souls */ private constant string SOUL_MODEL = "" private constant real SOUL_SCALE = 1 private constant integer SOUL_ALPHA = 128 private constant integer SOUL_RED = 255 private constant integer SOUL_GREEN = 255 private constant integer SOUL_BLUE = 255 //------------------------------------------------------ /* * Mana feed */ private constant string MANA_FEED_SFX = "" private constant string MANA_FEED_ATTACH = "" //------------------------------------------------------ /* * Shockwave fields */ private constant string MANA_RELEASE_SFX = "" /* * Number of shockwave segments */ private constant integer WAVE_COUNT = 12 /* * Shockwave appearance */ private constant string WAVE_MODEL = "" private constant real WAVE_SCALE = 1.25 private constant integer WAVE_ALPHA = 255 private constant integer WAVE_RED = 255 private constant integer WAVE_GREEN = 255 private constant integer WAVE_BLUE = 255 private constant real WAVE_Z = 32 //------------------------------------------------------ /* * Swarm fields */ private constant string SWARM_RELEASE_SFX = "" private constant string SWARM_MODEL = "" /* * Swarm appearance */ private constant real SWARM_SCALE = 1 private constant integer SWARM_RED = 255 private constant integer SWARM_GREEN = 255 private constant integer SWARM_BLUE = 255 private constant integer SWARM_ALPHA = 255 /* * Number of swarm segment */ private constant integer SWARM_COUNT = 5 /* * Arc rate of the Swarm */ private constant real SWARM_ARC = 0.25 endglobals /* * Setup Devourer IDs */ globals private integer array DEVOURER_ID endglobals private function Init takes nothing returns nothing set DEVOURER_ID[1] = 0 set DEVOURER_ID[2] = 0 set DEVOURER_ID[3] = 0 endfunction /* * For checking alive units */ native UnitAlive takes unit u returns boolean /* * For calculating Values with respect to levels */ private function GetLevelValueR takes real base, real increment, integer level returns real return base + increment*level endfunction private function GetLevelValueI takes integer base, integer increment, integer level returns integer return base + increment*level endfunction /* * For calculating value variance */ private function GetVarianceR takes real base, real variance returns real if variance == 0 then return base endif return GetRandomReal(base - variance, base + variance) endfunction private function GetVarianceI takes integer base, integer variance returns integer if variance == 0 then return base endif return GetRandomInt(base - variance, base + variance) endfunction /* * For calculating value over time */ private function LinearR takes real a, real b, real t returns real return a + (b - a)*t endfunction private function LinearI takes integer a, integer b, real t returns integer return R2I(LinearR(I2R(a), I2R(b), t)) endfunction /************************************** * * For getting the location surface z * **************************************/ static if (not LIBRARY_ZLibrary) then globals private constant location p = Location(0.0, 0.0) endglobals function GetSurfaceZ takes real x, real y returns real call MoveLocation(p, x, y) return GetLocationZ(p) endfunction endif /* * For handling lists and periods */ //! textmacro DEVOURER_LIST_TIMER private static thistype array next private static thistype array prev private static constant timer t = CreateTimer() private static method insert takes real timeout, code c returns thistype local thistype this = allocate() set next[this] = 0 set prev[this] = prev[0] set next[prev[0]] = this set prev[0] = this if prev[this] == 0 then call TimerStart(t, timeout, true, c) endif return this endmethod private method remove takes nothing returns nothing call deallocate() set next[prev[this]] = next[this] set prev[next[this]] = prev[this] endmethod //! endtextmacro /* * Structs for holding appearance values and position values */ private struct Appearance integer red integer green integer blue integer alpha real scale endstruct private struct Vector real x real y real z endstruct /* * Struct for timed appearances (fade, color change, etc.) */ private struct TimedAppearance //! runtextmacro DEVOURER_LIST_TIMER() private unit u private Appearance start private Appearance end private real current private real max method destroy takes nothing returns nothing call remove() set u = null call start.destroy() call end.destroy() set current = 0 set max = 0 endmethod private static method period takes nothing returns nothing local thistype this = next[0] local real pct local integer a local integer r local integer g local integer b local real s loop exitwhen 0 == this if current < max then set current = current + TIMEOUT set pct = current/max set a = LinearI(start.alpha, end.alpha, pct) set r = LinearI(start.red, end.red, pct) set g = LinearI(start.green, end.green, pct) set b = LinearI(start.blue, end.blue, pct) set s = LinearR(start.scale, end.scale, pct) call SetUnitVertexColor(u, r, g, b, a) call SetUnitScale(u, s, 0, 0) else call destroy() endif set this = next[this] endloop endmethod static method add takes unit temp, Appearance startColor, Appearance endColor, real maxTime returns nothing local thistype this = insert(TIMEOUT, function thistype.period) set start = startColor set end = endColor set current = 0 set max = maxTime set u = temp endmethod endstruct /* * Struct for effect dummies */ private struct Particle private unit u private effect mdl static method create takes string sfx, real x, real y, real z, real face returns thistype local thistype this = allocate() set u = GetRecycledUnit(x, y, false, face) call SetUnitFlyHeight(u, z - GetSurfaceZ(x, y), 0) set mdl = AddSpecialEffectTarget(sfx, u, "origin") return this endmethod method destroy takes nothing returns nothing call AddRecycleTimer(u, 2.0) call DestroyEffect(mdl) set u = null set mdl = null endmethod endstruct /* * Projectile motion */ module Missile private Vector offset private unit target private real size private real speed private real missileTime private method destroyMissile takes nothing returns nothing call onImpact(target) set target = null set speed = 0 set missileTime = 0 endmethod private method move takes nothing returns nothing local real ux = GetUnitX(u) local real uy = GetUnitY(u) local real uz = GetSurfaceZ(ux, uy) + GetUnitFlyHeight(u) local real tx local real ty local real tz local real dx local real dy local real dz local real a2 local real a3 if UnitAlive(target) then set tx = GetUnitX(target) set ty = GetUnitY(target) set tz = GetSurfaceZ(tx, ty) + GetUnitFlyHeight(target) set dx = tx - ux set dy = ty - uy set dz = tz - uz if GetMagnitude3d(dx, dy, dz) <= size then call destroyMissile() endif set a2 = Atan2(dy, dx) set a3 = Atan2(dz, GetMagnitude2d(dx, dy)) elseif missileTime > 0 then set missileTime = missileTime - TIMEOUT set ux = GetBoundedX(ux + offset.x) set uy = GetBoundedY(uy + offset.y) call SetUnitX(u, ux) call SetUnitY(u, uy) call SetUnitFlyHeight(u, (uz + offset.z) - GetSurfaceZ(ux, uy), 0) else call destroyMissile() endif endmethod method setTarget takes unit temp returns nothing set target = temp set size = GetUnitCollision(temp) endmethod endmodule /* * Struct for creeps */ private struct Creep //! runtextmacro DEVOURER_LIST_TIMER() private unit u method onImpact takes unit temp returns nothing endmethod implement Missile endstruct private struct Soul //! runtextmacro DEVOURER_LIST_TIMER() private unit u method onImpact takes unit temp returns nothing endmethod implement Missile endstruct private struct Swarm //! runtextmacro DEVOURER_LIST_TIMER() endstruct private struct Shockwave //! runtextmacro DEVOURER_LIST_TIMER() endstruct private struct Devourer //! runtextmacro DEVOURER_LIST_TIMER() endstruct private struct SummonDevourer //! runtextmacro DEVOURER_LIST_TIMER() endstruct endscope
I feel like there are more constants than actual code lol.
scope Devourer initializer Init
globals
/*
* Timeout for all timers running in this spell
*/
private constant real TIMEOUT = 0.03125
/*
* The ability id of the spell
*/
private constant integer ABIL_ID = 0
/*
* rawcode id for the creeps (sfx units)
*/
private constant integer CREEP_ID = 'nspb'
/*
* rawcode id of dummies
*/
private constant integer DUMMY_ID = 'dumi'
//------------------------------------------------------
/*
* Spell on cast
*/
//------------------------------------------------------
/*
* Required trees for summoning
*/
private constant integer REQUIRED_TREES = 6
private constant integer REQUIRED_TREES_PER_LVL = -1
/*
* Radius for checking the required trees
*/
private constant real TREE_RADIUS_CHECK = 256
private constant real TREE_RADIUS_CHECK_PER_LVL = 0
/*
* Spawn delay of the devourer
*/
private constant real SPAWN_DELAY = 6
private constant real SPAWN_DELAY_PER_LVL = -1
/*
* How long the the devourer last
*/
private constant real TIMED_LIFE = 10
private constant real TIMED_LIFE_PER_LVL = 10
//------------------------------------------------------
/*
* Souls configurables
*/
//------------------------------------------------------
/*
* souls drop when unit dies in the given radius
*/
private constant real SOUL_DROP_RADIUS = 800
private constant real SOUL_DROP_RADIUS_PER_LVL = 0
/*
* How long does the souls last
*/
private constant real SOUL_DROP_DURATION = 10
private constant real SOUL_DROP_DURATION_PER_LVL = 0
/*
* Required distance to activate to souls
*/
private constant real SOUL_FEED_RADIUS = 500
private constant real SOUL_FEED_RADIUS_PER_LVL = 100
/*
* Speed of the souls
*/
private constant real SOUL_SPEED = 1000
/*
* Bonus life duration added when souls reaches the devourer
*/
private constant real TIMED_LIFE_BONUS = 0
private constant real TIMED_LIFE_BONUS_PER_LVL = 1
//------------------------------------------------------
/*
* Mana Release/Shockwave Configurables
*/
//------------------------------------------------------
/*
* Consumes all mana that are lost within the given range
*/
private constant real MANA_FEED_RADIUS = 1000
private constant real MANA_FEED_RADIUS_PER_LVL = 0
/*
* Required mana to release the shockwave
*/
private constant real MANA_RELEASE_REQ = 450
private constant real MANA_RELEASE_REQ_PER_LVL = -50
/*
* How large the shockwave is
*/
private constant real MANA_RELEASE_RADIUS = 600
private constant real MANA_RELEASE_RADIUS_PER_LVL = 0
/*
* How long does the shockwave grow
* from it's release point to it's max radius
*/
private constant real MANA_RELEASE_GROW_TIME = 2
/*
* Damage dealt to the affected units
*/
private constant real MANA_RELEASE_DMG = 256
private constant real MANA_RELEASE_DMG_PER_LVL = 0
private constant attacktype MANA_RELEASE_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype MANA_RELEASE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Is the shockwave locked to the devourer?
* locked = follows the devourer's position instead
* of the position of release.
*/
private constant boolean MANA_RELEASED_LOCKED = true
/*
* Cooldown of the shockwave
*/
private constant real MANA_RELEASE_CD = 0
private constant real MANA_RELEASE_CD_PER_LVL = 0
/*
* Does the devourer collect mana even
* if the shockwave is on cooldown?
*/
private constant boolean COLLECT_MANA_ON_CD = false
//------------------------------------------------------
/*
* Swarm configurables
*/
//------------------------------------------------------
/*
* Starting polar offset of the swarms
*/
private constant real SWARM_SPAWN_OFFSET = 128
private constant real SWARM_SPAWN_OFFSET_PER_LVL = 0
/*
* How far doest the swarm travel
* (from the offset)
*/
private constant real SWARM_TRAVEL_DIST = 768
private constant real SWARM_TRAVEL_DIST_PER_LVL = 0
/*
* Radius of the swarm
*/
private constant real SWARM_RADIUS = 128
private constant real SWARM_RADIUS_PER_LVL = 0
/*
* Damage dealt by the swarm
*/
private constant real SWARM_DAMAGE = 0
private constant real SWARM_DAMAGE_PER_LVL = 75
private constant attacktype SWARM_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
private constant damagetype SWARM_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Swarm cooldown
*/
private constant real SWARM_CD = 12
private constant real SWARM_CD_PER_LVL = -2
/*
* Swarm speed
*/
private constant real SWARM_SPEED = 1024
//------------------------------------------------------
/*
* On Cast Effects
*/
//------------------------------------------------------
/*
* Portal Fields
*/
/*
* Model of the portal
*/
private constant string PORTAL_MODEL = "Abilities\\Spells\\Undead\\Possession\\PossessionTarget.mdl"
/*
* Height of the portal
*/
private constant real PORTAL_Z = 64
/*
* Starting appearance of the portal
*/
private constant real PORTAL_START_SCALE = 0
private constant integer PORTAL_START_ALPHA = 255
private constant integer PORTAL_START_RED = 255
private constant integer PORTAL_START_GREEN = 255
private constant integer PORTAL_START_BLUE = 255
/*
* End appearance of the portal
*/
private constant real PORTAL_END_SCALE = 3
private constant integer PORTAL_END_ALPHA = 255
private constant integer PORTAL_END_RED = 255
private constant integer PORTAL_END_GREEN = 255
private constant integer PORTAL_END_BLUE = 255
/*
* Portal shard fields
*/
/*
* Show the portal shards?
*/
private constant boolean SHOW_SHARDS = true
/*
* Model of the shards
*/
private constant string SHARD_MODEL = "Abilities\\Spells\\Undead\\Possession\\PossessionTarget.mdl"
/*
* Start appearance of the shards
*/
private constant real SHARD_START_SCALE = 1
private constant integer SHARD_START_ALPHA = 255
private constant integer SHARD_START_RED = 255
private constant integer SHARD_START_GREEN = 255
private constant integer SHARD_START_BLUE = 255
/*
* End appearance of the shards
*/
private constant real SHARD_END_SCALE = 0
private constant integer SHARD_END_ALPHA = 255
private constant integer SHARD_END_RED = 255
private constant integer SHARD_END_GREEN = 255
private constant integer SHARD_END_BLUE = 255
/*
* Spawn distance of the shards
*/
private constant real SHARD_DISTANCE = 500
private constant real SHARD_DISTANCE_VAR = 125
/*
* Spawn angular variation of the shards
*/
private constant real SHARD_ANGLE = 0
private constant real SHARD_ANGLE_VAR = bj_PI
private constant real SHARD_Z_ANGLE = bj_PI/2
private constant real SHARD_Z_ANGLE_VAR = SHARD_Z_ANGLE
/*
* How often shards spawn
*/
private constant real SHARD_PERIOD = 0.0625
/*
* How many shards spawn per period
*/
private constant integer SHARD_COUNT = 2
/*
* Speed of the shards
*/
private constant real SHARD_SPEED = 500
private constant real SHARD_SPEED_VAR = 125
//------------------------------------------------------
/*
* On Spawn effects
*/
private constant string ON_SPAWN_SFX = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl"
/*
* Show creep nova
*/
private constant boolean SHOW_CREEP_NOVA = true
/*
* Start appearance of the nova creeps
*/
private constant real NCREEP_START_SCALE = 1
private constant integer NCREEP_START_ALPHA = 200
private constant integer NCREEP_START_RED = 0
private constant integer NCREEP_START_GREEN = 0
private constant integer NCREEP_START_BLUE = 0
/*
* End appearance of the nova creeps
*/
private constant real NCREEP_END_SCALE = 1
private constant integer NCREEP_END_ALPHA = 0
private constant integer NCREEP_END_RED = 0
private constant integer NCREEP_END_GREEN = 0
private constant integer NCREEP_END_BLUE = 0
/*
* Number of creeps released
*/
private constant integer NCREEP_COUNT = 32
/*
* Speed of the creeps
*/
private constant real NCREEP_SPEED = 375
private constant real NCREEP_SPEED_VAR = 125
/*
* Creep life time
*/
private constant real NCREEP_TIME = 1.25
/*
* Animation played by the creep
*/
private constant string NCREEP_ANIM = "walk"
/*
* Nova creep height
*/
private constant real NCREEP_Z = 0
//------------------------------------------------------
/*
* Devourer appearances
*/
private constant real DEVOURER_SCALE = 0.75
private constant real DEVOURER_SCALE_PER_LVL = 0.25
private constant integer DEVOURER_ALPHA = 200
private constant integer DEVOURER_RED = 100
private constant integer DEVOURER_GREEN = 100
private constant integer DEVOURER_BLUE = 100
private constant real DEVOURER_Z = 0
private constant string DEVOURER_DEATH_SFX = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl"
//------------------------------------------------------
/*
* Shadow Trail fields
*/
private constant boolean SHOW_TRAIL = true
/*
* Trail period
*/
private constant real TRAIL_PERIOD = 0.09375
/*
* Trail duration
*/
private constant real TRAIL_DURATION = 1.5
/*
* Trail animation
*/
private constant string TRAIL_ANIM = "walk"
//------------------------------------------------------
/*
* Aura fields
*/
private constant boolean SHOW_AURA = true
/*
* Aura creep spawn period
*/
private constant real AURA_SPAWN_PERIOD = 0.125
/*
* Number of creep spawn
*/
private constant integer ACREEP_COUNT = 1
/*
* Start appearance of the aura creeps
*/
private constant real ACREEP_START_SCALE = 0.25
private constant integer ACREEP_START_ALPHA = 200
private constant integer ACREEP_START_RED = 0
private constant integer ACREEP_START_GREEN = 0
private constant integer ACREEP_START_BLUE = 0
/*
* End appearance of the aura creeps
*/
private constant real ACREEP_END_SCALE = 0.25
private constant integer ACREEP_END_ALPHA = 0
private constant integer ACREEP_END_RED = 0
private constant integer ACREEP_END_GREEN = 0
private constant integer ACREEP_END_BLUE = 0
/*
* Aura creep speed
*/
private constant real ACREEP_SPEED = 200
private constant real ACREEP_SPEED_VAR = 50
/*
* Aura creep duration
*/
private constant real ACREEP_TIME = 1.25
/*
* Aura creep animation
*/
private constant string ACREEP_ANIM = "walk"
/*
* Aura creep height
*/
private constant real ACREEP_Z = 0
//------------------------------------------------------
/*
* Souls
*/
private constant string SOUL_MODEL = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl"
private constant real SOUL_SCALE = 1
private constant integer SOUL_ALPHA = 128
private constant integer SOUL_RED = 255
private constant integer SOUL_GREEN = 255
private constant integer SOUL_BLUE = 255
//------------------------------------------------------
/*
* Mana feed
*/
private constant string MANA_FEED_SFX = "Abilities\\Spells\\Undead\\AbsorbMana\\AbsorbManaBirthMissile.mdl"
private constant string MANA_FEED_ATTACH = "head"
//------------------------------------------------------
/*
* Shockwave fields
*/
private constant string MANA_RELEASE_SFX = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl"
/*
* Number of shockwave segments
*/
private constant integer WAVE_COUNT = 12
/*
* Shockwave appearance
*/
private constant string WAVE_MODEL = "Abilities\\Spells\\Undead\\OrbOfDeath\\AnnihilationMissile.mdl"
private constant real WAVE_SCALE = 1.25
private constant integer WAVE_ALPHA = 255
private constant integer WAVE_RED = 255
private constant integer WAVE_GREEN = 255
private constant integer WAVE_BLUE = 255
private constant real WAVE_Z = 32
//------------------------------------------------------
/*
* Swarm fields
*/
private constant string SWARM_RELEASE_SFX = ""
private constant string SWARM_MODEL = ""
/*
* Swarm appearance
*/
private constant real SWARM_SCALE = 1
private constant integer SWARM_RED = 255
private constant integer SWARM_GREEN = 255
private constant integer SWARM_BLUE = 255
private constant integer SWARM_ALPHA = 255
/*
* Number of swarm segment
*/
private constant integer SWARM_COUNT = 5
/*
* Arc rate of the Swarm
*/
private constant real SWARM_ARC = 0.25
endglobals
/*
* Setup Devourer IDs
*/
globals
private integer array DEVOURER_ID
endglobals
private function SetupDevourerId takes nothing returns nothing
set DEVOURER_ID[1] = 0
set DEVOURER_ID[2] = 0
set DEVOURER_ID[3] = 0
endfunction
/*
* For checking alive units
*/
native UnitAlive takes unit u returns boolean
/*
* For calculating Values with respect to levels
*/
private function GetLevelValueR takes real base, real increment, integer level returns real
return base + increment*level
endfunction
private function GetLevelValueI takes integer base, integer increment, integer level returns integer
return base + increment*level
endfunction
/*
* For calculating value variance
*/
private function GetVarianceR takes real base, real variance returns real
if variance == 0 then
return base
endif
return GetRandomReal(base - variance, base + variance)
endfunction
private function GetVarianceI takes integer base, integer variance returns integer
if variance == 0 then
return base
endif
return GetRandomInt(base - variance, base + variance)
endfunction
/*
* For calculating value over time
*/
private function LinearR takes real a, real b, real t returns real
return a + (b - a)*t
endfunction
private function LinearI takes integer a, integer b, real t returns integer
return R2I(LinearR(I2R(a), I2R(b), t))
endfunction
/**************************************
*
* For getting the location surface z
*
**************************************/
static if (not LIBRARY_ZLibrary) then
globals
private constant location p = Location(0.0, 0.0)
endglobals
function GetSurfaceZ takes real x, real y returns real
call MoveLocation(p, x, y)
return GetLocationZ(p)
endfunction
endif
/*
* For handling lists and periods
*/
//! textmacro DEVOURER_LIST_TIMER
private static thistype array next
private static thistype array prev
private static constant timer t = CreateTimer()
private static method insert takes code c returns thistype
// allocate new instance
local thistype this = allocate()
// insert it to the list
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
// check if list contains only a single instance
if prev[this] == 0 then
// if true, start timer
call TimerStart(t, TIMEOUT, true, c)
endif
// return instance
return this
endmethod
private method remove takes nothing returns nothing
// dealloc instance
call deallocate()
// remove isntance from list
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
endmethod
//! endtextmacro
/*
* Structs for holding appearance values and position values
*/
private struct Appearance
integer red
integer green
integer blue
integer alpha
real scale
endstruct
private struct Vector
real x
real y
real z
endstruct
/*
* Struct for timed appearances (fade, color change, etc.)
*/
private struct TimedAppearance
//! runtextmacro DEVOURER_LIST_TIMER()
private unit u
// start and end appearances
private Appearance start
private Appearance end
// timer
private real current
private real max
method destroy takes nothing returns nothing
call remove()
set u = null
call start.destroy()
call end.destroy()
set current = 0
set max = 0
endmethod
private static method period takes nothing returns nothing
local thistype this = next[0]
local real pct
local integer a
local integer r
local integer g
local integer b
local real s
loop
exitwhen 0 == this
if current < max then
set current = current + TIMEOUT
set pct = current/max
set a = LinearI(start.alpha, end.alpha, pct)
set r = LinearI(start.red, end.red, pct)
set g = LinearI(start.green, end.green, pct)
set b = LinearI(start.blue, end.blue, pct)
set s = LinearR(start.scale, end.scale, pct)
call SetUnitVertexColor(u, r, g, b, a)
call SetUnitScale(u, s, 0, 0)
else
call destroy()
endif
set this = next[this]
endloop
endmethod
static method add takes unit temp, Appearance startColor, Appearance endColor, real maxTime returns nothing
local thistype this = insert(function thistype.period)
set start = startColor
set end = endColor
set current = 0
set max = maxTime
set u = temp
endmethod
endstruct
/*
* Struct for effect dummies
*/
private struct Particle
readonly unit u
readonly Vector pos
private effect mdl
static method create takes string sfx, real x, real y, real z, real face returns thistype
local thistype this = allocate()
set u = GetRecycledUnit(x, y, false, face)
set pos = Vector.create()
set pos.x = x
set pos.y = y
set pos.z = GetSurfaceZ(x, y) + z
call SetUnitFlyHeight(u, z, 0)
set mdl = AddSpecialEffectTarget(sfx, u, "origin")
return this
endmethod
method destroy takes nothing returns nothing
call AddRecycleTimer(u, 2.0)
call DestroyEffect(mdl)
set u = null
set mdl = null
endmethod
endstruct
/*
* Projectile motion
*/
//! textmacro DEV_PROJECTILE
private Vector offset
private unit target
private real size
private real speed
private real missileTime
private boolean started
private method destroyMissile takes nothing returns nothing
if started then
call onImpact(target)
set target = null
set speed = 0
set missileTime = 0
set started = false
endif
endmethod
private method move takes nothing returns nothing
local real ux
local real uy
local real uz
local real tx
local real ty
local real tz
local real dx
local real dy
local real dz
local real a2
local real a3
if started then
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set uz = GetSurfaceZ(ux, uy) + GetUnitFlyHeight(u)
if UnitAlive(target) then
set tx = GetUnitX(target)
set ty = GetUnitY(target)
set tz = GetSurfaceZ(tx, ty) + GetUnitFlyHeight(target)
set dx = tx - ux
set dy = ty - uy
set dz = tz - uz
if GetMagnitude3D(dx, dy, dz) <= size then
call destroyMissile()
endif
set a2 = Atan2(dy, dx)
set a3 = Atan2(dz, GetMagnitude2D(dx, dy))
set ux = ux + speed*Cos(a2)*Cos(a3)
set uy = uy + speed*Sin(a2)*Cos(a3)
set uz = uz + speed*Sin(a3)
set ux = GetBoundedX(ux)
set uy = GetBoundedY(uy)
set uz = uz - GetSurfaceZ(ux, uy)
call SetUnitX(u, ux)
call SetUnitY(u, uy)
call SetUnitFlyHeight(u, uz, 0)
call SetUnitFacing(u, a2*bj_RADTODEG)
if GetUnitTypeId(u) == DUMMY_ID then
call SetUnitAnimationByIndex(u, R2I(a3*bj_RADTODEG + 90.5))
endif
elseif missileTime > 0 then
set missileTime = missileTime - TIMEOUT
set ux = GetBoundedX(ux + offset.x)
set uy = GetBoundedY(uy + offset.y)
call SetUnitX(u, ux)
call SetUnitY(u, uy)
call SetUnitFlyHeight(u, (uz + offset.z) - GetSurfaceZ(ux, uy), 0)
else
call destroyMissile()
endif
endif
endmethod
private method setTarget takes unit temp returns nothing
set target = temp
set size = GetUnitCollision(temp)
endmethod
private method setTime takes real time returns nothing
set missileTime = time
endmethod
private method start takes real vel, real angle2d, real angle3d returns nothing
set offset = Vector.create()
set speed = vel*TIMEOUT
set offset.x = speed*Cos(angle2d)*Cos(angle3d)
set offset.y = speed*Sin(angle2d)*Cos(angle3d)
set offset.z = speed*Sin(angle3d)
set started = true
endmethod
//! endtextmacro
/*
* Struct for creeps
*/
private struct Creep
//! runtextmacro DEVOURER_LIST_TIMER()
private unit u
private boolean isNova
method onImpact takes unit temp returns nothing
call remove()
call RemoveUnit(u)
endmethod
//! runtextmacro DEV_PROJECTILE()
private static method period takes nothing returns nothing
local thistype this = next[0]
loop
exitwhen 0 == this
call move()
if isNova then
call SetUnitAnimation(u, NCREEP_ANIM)
else
call SetUnitAnimation(u, ACREEP_ANIM)
endif
set this = next[this]
endloop
endmethod
static method spawn takes player p, real x, real y, boolean aura returns nothing
local thistype this = insert(function thistype.period)
local Appearance start = Appearance.create()
local Appearance end = Appearance.create()
local real angle2d = GetRandomReal(-bj_PI, bj_PI)
local real velocity
local real time
local real z
if aura then
set start.red = ACREEP_START_RED
set start.green = ACREEP_START_GREEN
set start.blue = ACREEP_START_BLUE
set start.alpha = ACREEP_START_ALPHA
set start.scale = ACREEP_START_SCALE
set end.red = ACREEP_END_RED
set end.green = ACREEP_END_GREEN
set end.blue = ACREEP_END_BLUE
set end.alpha = ACREEP_END_ALPHA
set end.scale = ACREEP_END_SCALE
set velocity = GetVarianceR(ACREEP_SPEED, ACREEP_SPEED_VAR)
set time = ACREEP_TIME
set z = ACREEP_Z
else
set start.red = NCREEP_START_RED
set start.green = NCREEP_START_GREEN
set start.blue = NCREEP_START_BLUE
set start.alpha = NCREEP_START_ALPHA
set start.scale = NCREEP_START_SCALE
set end.red = NCREEP_END_RED
set end.green = NCREEP_END_GREEN
set end.blue = NCREEP_END_BLUE
set end.alpha = NCREEP_END_ALPHA
set end.scale = NCREEP_END_SCALE
set velocity = GetVarianceR(NCREEP_SPEED, NCREEP_SPEED_VAR)
set time = NCREEP_TIME
set z = NCREEP_Z
endif
set isNova = not aura
set u = CreateUnit(p, CREEP_ID, x, y, angle2d*bj_RADTODEG)
call PauseUnit(u, true)
call UnitAddAbility(u, 'Aloc')
if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
endif
call SetUnitFlyHeight(u, z, 0)
call setTime(time)
call start(velocity, angle2d, 0)
call TimedAppearance.add(u, start, end, time)
endmethod
endstruct
private struct Soul
//! runtextmacro DEVOURER_LIST_TIMER()
private unit u
private unit owner
method onImpact takes unit temp returns nothing
endmethod
//! runtextmacro DEV_PROJECTILE()
endstruct
private struct Swarm
//! runtextmacro DEVOURER_LIST_TIMER()
private Table particles
endstruct
private struct Shockwave
//! runtextmacro DEVOURER_LIST_TIMER()
private Table particles
endstruct
private struct Devourer
//! runtextmacro DEVOURER_LIST_TIMER()
endstruct
private struct Shards
//! runtextmacro DEVOURER_LIST_TIMER()
private unit u
private unit owner
method onImpact takes unit temp returns nothing
endmethod
//! runtextmacro DEV_PROJECTILE()
endstruct
private struct SummonDevourer
//! runtextmacro DEVOURER_LIST_TIMER()
private Particle portal
private real time
private real spawnTime
private real spawnTimer
private integer level
method destroy takes nothing returns nothing
endmethod
private static method period takes nothing returns nothing
local thistype this = next[0]
local integer i
loop
exitwhen 0 == this
if time > 0 then
set time = time - TIMEOUT
if SHOW_SHARDS then
set spawnTime = spawnTime - TIMEOUT
if spawnTime >= 0 then
set spawnTimer = SHARD_PERIOD
set i = SHARD_COUNT
loop
exitwhen i == 0
set i = i - 1
endloop
endif
endif
endif
set this = next[this]
endloop
endmethod
static method onCast takes nothing returns boolean
local thistype this
local unit caster = GetTriggerUnit()
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
local Appearance start
local Appearance end
// if <cond> then
set this = insert(function thistype.period)
set level = GetUnitAbilityLevel(caster, ABIL_ID)
set portal = Particle.create(PORTAL_MODEL, x, y, PORTAL_Z, 270)
set time = GetLevelValueR(SPAWN_DELAY, SPAWN_DELAY_PER_LVL, level)
set start.red = PORTAL_START_RED
set start.green = PORTAL_START_GREEN
set start.blue = PORTAL_START_BLUE
set start.alpha = PORTAL_START_ALPHA
set start.scale = PORTAL_START_SCALE
set end.red = PORTAL_END_RED
set end.green = PORTAL_END_GREEN
set end.blue = PORTAL_END_BLUE
set end.alpha = PORTAL_END_ALPHA
set end.scale = PORTAL_END_SCALE
call TimedAppearance.add(portal.u, start, end, time)
if SHOW_SHARDS then
set spawnTime = SHARD_PERIOD
endif
return false
endmethod
endstruct
private function Init takes nothing returns nothing
call SetupDevourerId()
call RegisterSpellEffectEvent(ABIL_ID, function SummonDevourer.onCast)
endfunction
endscope
constant function AoP_DummiesOwner takes nothing returns player
return Player(PLAYER_NEUTRAL_PASSIVE)
endfunction
constant function AoP_FlyAbilityID takes nothing returns integer
return 'Amrf'
endfunction
constant function AoP_FrameUpdate takes nothing returns real
return 1 / 32.
endfunction
function AoP_IsUnitAlive takes unit u returns boolean
return GetUnitTypeId(u) != 0 and not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction
function AoP_IsUnitChanneling takes unit u returns boolean
return AoP_IsUnitAlive(u) and GetUnitCurrentOrder(u) == udg_AoP_AbilityOrder
endfunction
function AoP_GetMagnitude2D takes real x, real y returns real
return SquareRoot(x*x+y*y)
endfunction
function AoP_GetMagnitude3D takes real x, real y, real z returns real
return SquareRoot(x*x+y*y+z*z)
endfunction
function AoP_GetAngle takes real x, real y returns real
return Atan2(y, x)
endfunction
function AoP_GetAngleZ takes real distance2D, real z returns real
return Atan2(z, distance2D)
endfunction
function AoP_GetParabolaHeight takes real y0, real y1, real h, real d, real x returns real
local real A = (2*(y0+y1)-4*h)/(d*d)
local real B = (y1-y0-A*d*d)/d
return A*x*x + B*x + y0
endfunction
function AoP_GetTerrainZ takes real x, real y returns real
call MoveLocation(udg_AoP_ZLocator, x, y)
return GetLocationZ(udg_AoP_ZLocator)
endfunction
function AoP_GetUnitZAlt takes unit u, real x, real y returns real
return GetUnitFlyHeight(u) + AoP_GetTerrainZ(x, y)
endfunction
function AoP_UnitApplyFly takes unit u returns nothing
if UnitAddAbility(u, AoP_FlyAbilityID()) and UnitRemoveAbility(u, AoP_FlyAbilityID()) then
endif
endfunction
function AoP_SetUnitZAlt takes unit u, real z, real x, real y returns nothing
call SetUnitFlyHeight(u, z - AoP_GetTerrainZ(x, y), 0)
endfunction
function AoP_SetUnitPosition takes unit u, real x, real y returns nothing
call SetUnitX(u, x)
call SetUnitY(u, y)
endfunction
function AoP_Destroy takes integer index returns nothing
set udg_AoP_Recycle[udg_AoP_Recyclable] = index
set udg_AoP_Recyclable = udg_AoP_Recyclable + 1
set udg_AoP_Next[udg_AoP_Prev[index]] = udg_AoP_Next[index]
set udg_AoP_Prev[udg_AoP_Next[index]] = udg_AoP_Prev[index]
if udg_AoP_Next[0] == 0 then
call PauseTimer(udg_AoP_Timer)
endif
endfunction
function AoP_Create takes nothing returns integer
local integer newIndex
if udg_AoP_Recyclable == 0 then
set udg_AoP_MaxIndex = udg_AoP_MaxIndex + 1
set newIndex = udg_AoP_MaxIndex
else
set udg_AoP_Recyclable = udg_AoP_Recyclable - 1
set newIndex = udg_AoP_Recycle[udg_AoP_Recyclable]
endif
set udg_AoP_Next[newIndex] = 0
set udg_AoP_Next[udg_AoP_Prev[0]] = newIndex
set udg_AoP_Prev[newIndex] = udg_AoP_Prev[0]
set udg_AoP_Prev[0] = newIndex
return newIndex
endfunction
function AoP_CreateParticle takes string model, real x, real y, real z, real size, real dur returns nothing
local integer pIndex = AoP_Create()
set udg_AoP_Particle[pIndex] = CreateUnit(AoP_DummiesOwner(), udg_AoP_Dummy, x, y, 0)
call AoP_SetUnitPosition(udg_AoP_Particle[pIndex], x, y)
call AoP_UnitApplyFly(udg_AoP_Particle[pIndex])
call AoP_SetUnitZAlt(udg_AoP_Particle[pIndex], z, x, y)
call SetUnitScale(udg_AoP_Particle[pIndex], size, 0, 0)
set udg_AoP_AttachedModel[pIndex] = AddSpecialEffectTarget(model, udg_AoP_Particle[pIndex], "origin")
set udg_AoP_realTimer[pIndex] = dur
set udg_AoP_Stage[pIndex] = 5
endfunction
function AoP_CreateParticleTarget takes string model, unit u, real size, real dur returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
call AoP_CreateParticle(model, x, y, AoP_GetUnitZAlt(u, x, y), size, dur)
endfunction
function AoP_StartSummoningEffect takes real x, real y, integer lvl returns nothing
local integer eIndex = AoP_Create()
local integer i = 0
loop
set i = i + 1
exitwhen udg_AoP_SummoningStartSFX[i] == null
call DestroyEffect(AddSpecialEffect(udg_AoP_SummoningStartSFX[i], x, y))
endloop
set udg_AoP_TargetX[eIndex] = x
set udg_AoP_TargetY[eIndex] = y
set udg_AoP_Level[eIndex] = lvl
set udg_AoP_Dist[eIndex] = udg_AoP_sGroundSFXStart
set udg_AoP_Dist2[eIndex] = udg_AoP_sEdgeSFXStart[udg_AoP_Level[eIndex]]
set udg_AoP_Angle[eIndex] = GetRandomReal(-bj_PI, bj_PI)
set udg_AoP_realTimer[eIndex] = 0
set udg_AoP_realTimer2[eIndex] = 0
set udg_AoP_Stage[eIndex] = 4
endfunction
function AoP_Periodic takes nothing returns nothing
local real x
local real y
local real z
local integer i
local integer index = 0
loop
set index = udg_AoP_Next[index]
exitwhen index == 0
if udg_AoP_Stage[index] == 1 then
if AoP_IsUnitChanneling(udg_AoP_Caster[index]) then
if udg_AoP_Dist2[index] < udg_AoP_Dist[index] then
set udg_AoP_Dist2[index] = udg_AoP_Dist2[index] + udg_AoP_Speed[index]
set udg_AoP_Dist[0] = udg_AoP_Dist2[index] - udg_AoP_Dist[index] // Opposite to angle
set x = udg_AoP_TargetX[index] + udg_AoP_Dist[0] * Cos(udg_AoP_Angle[index])
set y = udg_AoP_TargetY[index] + udg_AoP_Dist[0] * Sin(udg_AoP_Angle[index])
set z = AoP_GetParabolaHeight(udg_AoP_SourceZ[index], udg_AoP_TargetZ[index], udg_AoP_MaxHeight[index], udg_AoP_Dist[index], udg_AoP_Dist2[index])
call SetUnitX(udg_AoP_Missile[index], x)
call SetUnitY(udg_AoP_Missile[index], y)
call AoP_SetUnitZAlt(udg_AoP_Missile[index], z, x, y)
else
call DestroyEffect(udg_AoP_AttachedModel[index])
call KillUnit(udg_AoP_Missile[index])
call AoP_StartSummoningEffect(udg_AoP_TargetX[index], udg_AoP_TargetY[index], udg_AoP_Level[index])
set udg_AoP_SummoningEffect[index] = CreateUnit(udg_AoP_Owner[index], udg_AoP_Dummy, udg_AoP_TargetX[index], udg_AoP_TargetY[index], 0)
call AoP_SetUnitPosition(udg_AoP_SummoningEffect[index], udg_AoP_TargetX[index], udg_AoP_TargetY[index])
call AoP_UnitApplyFly(udg_AoP_SummoningEffect[index])
call SetUnitFlyHeight(udg_AoP_SummoningEffect[index], udg_AoP_sSFXHeight, 0)
call SetUnitScale(udg_AoP_SummoningEffect[index], 0, 0, 0)
set udg_AoP_AttachedModel[index] = AddSpecialEffectTarget(udg_AoP_SummoningSFX, udg_AoP_SummoningEffect[index], "origin")
set udg_AoP_Dist[index] = 0
set udg_AoP_Stage[index] = 2
endif
else
// --- When caster stopped channeling
call DestroyEffect(udg_AoP_AttachedModel[index])
call KillUnit(udg_AoP_Missile[index])
call AoP_CreateParticleTarget(udg_AoP_mOrbCancelSFX, udg_AoP_Missile[index], udg_AoP_mOrbCancelSFXSize, udg_AoP_mOrbCancelSFXDeath)
call AoP_Destroy(index)
endif
elseif udg_AoP_Stage[index] == 2 then
if AoP_IsUnitChanneling(udg_AoP_Caster[index]) then
if udg_AoP_Dist[index] < udg_AoP_sSFXFinalSize[udg_AoP_Level[index]] then
else
endif
else
// --- When caster stopped channeling
call AoP_Destroy(index)
endif
elseif udg_AoP_Stage[index] == 4 then
if udg_AoP_Dist[index] < udg_AoP_sGroundSFXArea[udg_AoP_Level[index]] then
set udg_AoP_Dist[index] = udg_AoP_Dist[index] + udg_AoP_sGroundSFXSpeed
set udg_AoP_realTimer[index] = udg_AoP_realTimer[index] - AoP_FrameUpdate()
if udg_AoP_realTimer[index] <= 0 then
set udg_AoP_Angle[0] = GetRandomReal(-bj_PI, bj_PI)
set i = udg_AoP_sGroundSFXCount
loop
exitwhen i <= 0
set udg_AoP_Angle[0] = udg_AoP_Angle[0] + udg_AoP_sGroundSFXGap
set x = udg_AoP_TargetX[index] + udg_AoP_Dist[index] * Cos(udg_AoP_Angle[0])
set y = udg_AoP_TargetY[index] + udg_AoP_Dist[index] * Sin(udg_AoP_Angle[0])
call DestroyEffect(AddSpecialEffect(udg_AoP_sGroundSFX, x, y))
set i = i - 1
endloop
set udg_AoP_realTimer[index] = udg_AoP_sGroundSFXInterval
endif
endif
if udg_AoP_Dist2[index] < udg_AoP_sEdgeSFXArea[udg_AoP_Level[index]] then
set udg_AoP_Dist2[index] = udg_AoP_Dist2[index] + udg_AoP_sEdgeSFXSpeed
else
set udg_AoP_Dist2[index] = udg_AoP_sEdgeSFXArea[udg_AoP_Level[index]]
endif
set udg_AoP_Angle[index] = udg_AoP_Angle[index] + udg_AoP_sEdgeSFXSpinSpeed
set udg_AoP_realTimer2[index] = udg_AoP_realTimer2[index] - AoP_FrameUpdate()
if udg_AoP_realTimer2[index] <= 0 then
set udg_AoP_Angle[0] = udg_AoP_Angle[index]
set i = udg_AoP_sEdgeSFXCount
loop
exitwhen i <= 0
set udg_AoP_Angle[0] = udg_AoP_Angle[0] + udg_AoP_sEdgeSFXGap
set x = udg_AoP_TargetX[index] + udg_AoP_Dist2[index] * Cos(udg_AoP_Angle[0])
set y = udg_AoP_TargetY[index] + udg_AoP_Dist2[index] * Sin(udg_AoP_Angle[0])
call DestroyEffect(AddSpecialEffect(udg_AoP_sEdgeSFX, x, y))
if udg_AoP_Dist2[index] >= udg_AoP_sEdgeSFXArea[udg_AoP_Level[index]] then
endif
set i = i - 1
endloop
set udg_AoP_realTimer2[index] = udg_AoP_sEdgeSFXInterval
endif
elseif udg_AoP_Stage[index] == 5 then
set udg_AoP_realTimer[index] = udg_AoP_realTimer[index] - AoP_FrameUpdate()
if udg_AoP_realTimer[index] <= 0 then
call DestroyEffect(udg_AoP_AttachedModel[index])
call KillUnit(udg_AoP_Particle[index])
call AoP_Destroy(index)
endif
endif
endloop
endfunction
function AoP_onEffect takes nothing returns boolean
local real sx
local real sy
local real stx
local real sty
local integer cIndex
if GetSpellAbilityId() == udg_AoP_Ability then
set cIndex = AoP_Create()
set udg_AoP_Caster[cIndex] = GetTriggerUnit()
set udg_AoP_Owner[cIndex] = GetTriggerPlayer()
set udg_AoP_Level[cIndex] = GetUnitAbilityLevel(udg_AoP_Caster[cIndex], udg_AoP_Ability)
set sx = GetUnitX(udg_AoP_Caster[cIndex])
set sy = GetUnitY(udg_AoP_Caster[cIndex])
set udg_AoP_SourceZ[cIndex] = AoP_GetTerrainZ(sx, sy) + udg_AoP_mOrbSpawnHeight
set udg_AoP_TargetX[cIndex] = GetSpellTargetX()
set udg_AoP_TargetY[cIndex] = GetSpellTargetY()
set udg_AoP_TargetZ[cIndex] = AoP_GetTerrainZ(udg_AoP_TargetX[cIndex], udg_AoP_TargetY[cIndex])
set stx = udg_AoP_TargetX[cIndex] - sx
set sty = udg_AoP_TargetY[cIndex] - sy
set udg_AoP_Angle[cIndex] = AoP_GetAngle(stx, sty)
set udg_AoP_TargetX[0] = sx + udg_AoP_mOrbSpawnOffset * Cos(udg_AoP_Angle[cIndex])
set udg_AoP_TargetY[0] = sy + udg_AoP_mOrbSpawnOffset * Sin(udg_AoP_Angle[cIndex])
set udg_AoP_Dist[cIndex] = AoP_GetMagnitude2D(stx, sty) - udg_AoP_mOrbSpawnOffset
set udg_AoP_Dist2[cIndex] = 0
set udg_AoP_Speed[cIndex] = udg_AoP_Dist[cIndex] / udg_AoP_mOrbTravelTime
set udg_AoP_MaxHeight[cIndex] = udg_AoP_Dist[cIndex] * udg_AoP_mOrbArc + udg_AoP_TargetZ[cIndex]
set udg_AoP_Missile[cIndex] = CreateUnit(AoP_DummiesOwner(), udg_AoP_Dummy, udg_AoP_TargetX[0], udg_AoP_TargetY[0], udg_AoP_Angle[cIndex]*bj_RADTODEG)
call AoP_UnitApplyFly(udg_AoP_Missile[cIndex])
call AoP_SetUnitZAlt(udg_AoP_Missile[cIndex], udg_AoP_SourceZ[cIndex], udg_AoP_TargetX[0], udg_AoP_TargetY[0])
call SetUnitScale(udg_AoP_Missile[cIndex], udg_AoP_mOrbSize, 0, 0)
set udg_AoP_AttachedModel[cIndex] = AddSpecialEffectTarget(udg_AoP_ManaOrbModel, udg_AoP_Missile[cIndex], "origin")
set udg_AoP_Stage[cIndex] = 1
if udg_AoP_Prev[cIndex] == 0 then
call TimerStart(udg_AoP_Timer, AoP_FrameUpdate(), true, function AoP_Periodic)
endif
endif
return false
endfunction
function AoP_Initialize takes nothing returns nothing
local integer i = 0
loop
set i = i + 1
exitwhen i > udg_AoP_AbilityLevels
endloop
set udg_AoP_mOrbTravelTime = udg_AoP_mOrbTravelTime / AoP_FrameUpdate()
set udg_AoP_sGroundSFXCount = R2I(4*udg_AoP_sGroundSFXDensity)
set udg_AoP_sGroundSFXGap = 360 / udg_AoP_sGroundSFXCount * bj_DEGTORAD
set udg_AoP_sGroundSFXSpeed = udg_AoP_sGroundSFXSpeed * AoP_FrameUpdate()
set udg_AoP_sEdgeSFXCount = R2I(4*udg_AoP_sEdgeSFXDensity)
set udg_AoP_sEdgeSFXGap = 360 / udg_AoP_sEdgeSFXCount * bj_DEGTORAD
set udg_AoP_sEdgeSFXSpeed = udg_AoP_sEdgeSFXSpeed * AoP_FrameUpdate()
set udg_AoP_sEdgeSFXSpinSpeed = udg_AoP_sEdgeSFXSpinSpeed * AoP_FrameUpdate() * bj_DEGTORAD
if udg_AoP_ZLocator == null then
set udg_AoP_ZLocator = Location(0, 0)
endif
endfunction
//===========================================================================
function InitTrig_Ancient_of_Power takes nothing returns nothing
set gg_trg_Ancient_of_Power = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_Ancient_of_Power, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_Ancient_of_Power, Condition(function AoP_onEffect))
endfunction