Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'e000'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ChargedBolt initializer Init requires xebasic
//---------------------------------------------------
// Spell: Charged Bolt v1.0 (2010-01-24)
//
// Implementation instructions:
// Copy this trigger or paste it in yours
// Copy the xebasic library and read the info there
// Copy the universal dummy unit and use the
// imported dummy model as said in xebasic
// Copy the spell ability to your map
// Alter all constants to fit your needs
// Do not forget the ability rawcode and the dummy
//
// Credits:
// Vexorian - JassHelper, xebasic
// PitzerMike, MindWorX - JNGP
// Blizzard - Tooltip inspired
//
// The spell has been created by Eccho
// Give proper credits when used
//---------------------------------------------------
//---------------------------------------------------
// Native including
// If you have this in your code somewhere else,
// make sure to not double define it
//---------------------------------------------------
native UnitAlive takes unit id returns boolean
//---------------------------------------------------
// Configuration section
//---------------------------------------------------
globals
private constant integer ABILITY_ID = 'A000' //Id of the casting ability
private constant integer ABILITY_MAX_LVL = 4 //Max level of the casting ability. Match these with the level value field of the ability in the ability editor
private constant integer MISSILE_QTY_BASE = 3 //Amount of missile bolts released on level 1
private constant integer MISSILE_QTY_INC = 2 //Amount increased on levels > 1, eg, in this case, lvl 2 gives 3+2*(level-1) bolts
private constant integer MISSILE_QTY_MAX = 50 //Due to a JassHelper bug, set this constant to be at least your qty_base+qty_inc*(max_lvl-1). Note that this should be a fixed integer value.
//Self explanatory, hopefully
private constant string MISSILE_ART = "Abilities\\Spells\\Orc\\LightningBolt\\LightningBoltMissile.mdl"
private constant integer MISSILE_SPEED = 500
private constant integer MISSILE_RANGE = 800
private constant integer MISSILE_HEIGHT = 32
private constant real MISSILE_SCALE = 0.5
private constant real MISSILE_VIB_PER_MIN = 6*bj_PI //The min value factor describing how fast the missile vibrates. (see a formula as y = A*sin(kx) where k is this vibration factor.)
private constant real MISSILE_VIB_PER_MAX = 12*bj_PI //The max value factor describing how fast the missile vibrates.
private constant integer MISSILE_VIB_RADIUS = 32 //The altitude factor of the missile vibration. (see the formula above, where A is this radius factor.)
private constant real MISSILE_QTY_SPREAD = bj_PI/16 //The max radian angle possible between the missiles.
private constant real MISSILE_MAX_SPREAD = bj_PI/4 //The max radian angle spread, in which all missiles are moving in. For instance bj_PI/4 means that all missiles move within the caster_facing+-spread
private constant string IMPACT_ART = "Abilities\\Spells\\Items\\AIlb\\AIlbSpecialArt.mdl"
private constant string IMPACT_ATTACH_POINT = "chest"
private constant integer IMPACT_DAMAGE_BASE = 50 //Amount of damage at level 1
private constant integer IMPACT_DAMAGE_INC = 25 //Amount of damage added per each increased level
private constant integer IMPACT_RADIUS = 32 //See this as the missile collision size. Note that the missile will only collide and deal damage to one unit.
//Self explanatory, hopefully
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
// Target filter, used to determinate what enemies the spell will hit.
private function TargetFilter takes unit enemy, player caster, real x, real y returns boolean
return UnitAlive(enemy) and IsUnitEnemy(enemy, caster) and IsUnitType(enemy, UNIT_TYPE_GROUND) and IsUnitInRangeXY(enemy, x, y, IMPACT_RADIUS)
endfunction
//---------------------------------------------------
// Main spell code below
// Don't edit unless you know what you are doing
//---------------------------------------------------
globals
//Preserved in case of JassHelper being fixed
//private constant integer MISSILE_QTY_MAX = (MISSILE_QTY_BASE+(MISSILE_QTY_INC*(ABILITY_MAX_LVL-1)))
private constant real MISSILE_TIME_MAX = 1.0*MISSILE_RANGE/MISSILE_SPEED
private constant group ENUM_GROUP = CreateGroup()
private constant timer ANIM_TIMER = CreateTimer()
endglobals
//Missile struct used upon creating then charged missiles
private struct missile
unit mob
effect gfx
real xvel
real yvel
real vvel
static method create takes player sp, real sx, real sy, real ra returns missile
local missile m = missile.allocate()
set m.mob = CreateUnit(sp, XE_DUMMY_UNITID, sx, sy, ra)
set m.gfx = AddSpecialEffectTarget(MISSILE_ART, m.mob, "origin")
call UnitAddAbility(m.mob, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(m.mob, XE_HEIGHT_ENABLER)
call SetUnitAnimationByIndex(m.mob, 90)
call SetUnitFlyHeight(m.mob, MISSILE_HEIGHT, 0)
call SetUnitScale(m.mob, MISSILE_SCALE, MISSILE_SCALE, MISSILE_SCALE)
set m.xvel = MISSILE_SPEED*Cos(ra)
set m.yvel = MISSILE_SPEED*Sin(ra)
set m.vvel = GetRandomReal(MISSILE_VIB_PER_MIN, MISSILE_VIB_PER_MAX)
return m
endmethod
method onDestroy takes nothing returns nothing
call DestroyEffect(.gfx)
call KillUnit(.mob)
set .mob = null
set .gfx = null
endmethod
endstruct
//Main struct used to handle and run spell data
private struct data
player sp
integer slvl
real sx
real sy
real t1
missile array mis[MISSILE_QTY_MAX]
integer mqty
static data array dat
static integer dqty = 0
static method callback takes nothing returns nothing
local data d
local missile m
local integer i = 0
local integer j
local real x
local real y
local unit u
loop
exitwhen i >= data.dqty
set d = data.dat[i]
set d.t1 = d.t1+XE_ANIMATION_PERIOD
set j = 0
loop
exitwhen j >= d.mqty
set m = d.mis[j]
set x = d.sx+m.xvel*d.t1
set y = d.sy+m.yvel*d.t1
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, XE_MAX_COLLISION_SIZE+IMPACT_RADIUS, null)
loop
set u = FirstOfGroup(ENUM_GROUP)
exitwhen u == null
call GroupRemoveUnit(ENUM_GROUP, u)
exitwhen TargetFilter(u, d.sp, x, y)
endloop
call GroupClear(ENUM_GROUP)
// RectContainsCoords is a basic bj inline for GetRectMinX < x... blabla
if d.t1 <= MISSILE_TIME_MAX and RectContainsCoords(bj_mapInitialPlayableArea, x, y) and u == null then
call SetUnitX(m.mob, x+MISSILE_VIB_RADIUS*Sin(m.vvel*d.t1))
call SetUnitY(m.mob, y+MISSILE_VIB_RADIUS*Sin(m.vvel*d.t1))
set j = j+1
else
if u != null then
call UnitDamageTarget(m.mob, u, IMPACT_DAMAGE_BASE+IMPACT_DAMAGE_INC*(d.slvl-1), true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
call DestroyEffect(AddSpecialEffectTarget(IMPACT_ART, u, IMPACT_ATTACH_POINT))
endif
call m.destroy()
set d.mqty = d.mqty-1
set d.mis[j] = d.mis[d.mqty]
endif
endloop
if d.mqty == 0 then
call d.destroy()
set data.dqty = data.dqty-1
set data.dat[i] = data.dat[data.dqty]
if data.dqty == 0 then
call PauseTimer(ANIM_TIMER)
endif
else
set i = i+1
endif
endloop
set u = null
endmethod
static method create takes unit su, real nx, real ny returns data
local data d = data.allocate()
local integer i = 0
local real ra
local real spr
local real ramin
set d.sx = GetUnitX(su)
set d.sy = GetUnitY(su)
set ra = Atan2(ny-d.sy, nx-d.sx)
set d.sp = GetOwningPlayer(su)
set d.slvl = GetUnitAbilityLevel(su, ABILITY_ID)
set d.mqty = MISSILE_QTY_BASE+MISSILE_QTY_INC*(d.slvl-1)
set spr = MISSILE_QTY_SPREAD*(d.mqty-1)
if spr > MISSILE_MAX_SPREAD then
set spr = MISSILE_MAX_SPREAD
endif
set ramin = ra-spr/2
set spr = spr/(d.mqty-1)
loop
exitwhen i >= d.mqty
set d.mis[i] = missile.create(d.sp, d.sx, d.sy, ramin+spr*i)
set i = i+1
endloop
set d.t1 = 0
set data.dat[data.dqty] = d
if data.dqty == 0 then
call TimerStart(ANIM_TIMER, XE_ANIMATION_PERIOD, true, function data.callback)
endif
set data.dqty = data.dqty+1
return d
endmethod
endstruct
private function Evaluate takes nothing returns boolean
if GetSpellAbilityId() == ABILITY_ID then
call data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Filter(function Evaluate))
set t = null
endfunction
endlibrary