Name | Type | is_array | initial_value |
bm_EffectPoint | effect | No | |
bm_EffectTarget | effect | No | |
bm_Facing | real | No | |
bm_Point | location | No | |
bm_SourcePoint | location | No | |
Resurection_Stone | unit | No | |
temp_Point | location | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
Its important to copy all the contents of the
trigger "BoundSentinel"
trigger "Alloc"
to your map in order for this spell to function properly.
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 500.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Alloc ~~ By Sevion ~~ Version 1.09 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Alloc?
// - Alloc implements an intuitive allocation method for array structs
//
// =Pros=
// - Efficient.
// - Simple.
// - Less overhead than regular structs.
//
// =Cons=
// - Must use array structs (hardly a con).
// - Must manually call OnDestroy.
// - Must use Delegates for inheritance.
// - No default values for variables (use onInit instead).
// - No array members (use another Alloc struct as a linked list or type declaration).
//
// Methods:
// - struct.allocate()
// - struct.deallocate()
//
// These methods are used just as they should be used in regular structs.
//
// Modules:
// - Alloc
// Implements the most basic form of Alloc. Includes only create and destroy
// methods.
//
// Details:
// - Less overhead than regular structs
//
// - Use array structs when using Alloc. Put the implement at the top of the struct.
//
// - Alloc operates almost exactly the same as default structs in debug mode with the exception of onDestroy.
//
// How to import:
// - Create a trigger named Alloc.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Nestharus for the method of allocation and suggestions on further merging.
// - Bribe for suggestions like the static if and method names.
// - PurgeandFire111 for some suggestions like the merging of Alloc and AllocX as well as OnDestroy stuff.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Alloc
module Alloc
private static integer instanceCount = 0
private thistype recycle
static method allocate takes nothing returns thistype
local thistype this
if (thistype(0).recycle == 0) then
debug if (instanceCount == 8190) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to allocate too many instances!")
debug return 0
debug endif
set instanceCount = instanceCount + 1
set this = instanceCount
else
set this = thistype(0).recycle
set thistype(0).recycle = thistype(0).recycle.recycle
endif
debug set this.recycle = -1
return this
endmethod
method deallocate takes nothing returns nothing
debug if (this.recycle != -1) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to deallocate an invalid instance at [" + I2S(this) + "]!")
debug return
debug endif
set this.recycle = thistype(0).recycle
set thistype(0).recycle = this
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library IsTerrainWalkable initializer Init
//*****************************************************************
//* IsTerrainWalkable
//*
//* rewritten in vJass by: Anitarf
//* original implementation: Vexorian
//*
//* A function for checking if a point is pathable for ground
//* units (it does so by attempting to move an item there and
//* checking where it ended up), typically used to stop sliding
//* units before they end up stuck in trees. If the point is not
//* pathable, the function will also determine the nearest point
//* that is (the point where the item ends up).
//*****************************************************************
globals
// this value is how far from a point the item may end up for the point to be considered pathable
private constant real MAX_RANGE = 10.0
// the following two variables are set to the position of the item after each pathing check
// that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
public real X = 0.0
public real Y = 0.0
// END OF CALIBRATION SECTION
// ================================================================
private rect r
private item check
private item array hidden
private integer hiddenMax = 0
endglobals
private function Init takes nothing returns nothing
set check = CreateItem('ciri',0,0)
call SetItemVisible(check,false)
set r = Rect(0.0,0.0,128.0,128.0)
endfunction
private function HideBothersomeItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set hidden[hiddenMax]=GetEnumItem()
call SetItemVisible(hidden[hiddenMax],false)
set hiddenMax=hiddenMax+1
endif
endfunction
// ================================================================
function IsTerrainWalkable takes real x, real y returns boolean
// first, hide any items in the area so they don't get in the way of our item
call MoveRectTo(r, x,y)
call EnumItemsInRect(r,null,function HideBothersomeItem)
// try to move the check item and get it's coordinates
call SetItemPosition(check,x,y)//this unhides the item...
set X = GetItemX(check)
set Y = GetItemY(check)
call SetItemVisible(check,false)//...so we must hide it again
// before returning, unhide any items that got hidden at the start
loop
exitwhen hiddenMax<=0
set hiddenMax=hiddenMax-1
call SetItemVisible(hidden[hiddenMax],true)
set hidden[hiddenMax]=null
endloop
// return pathability status
return (x-X)*(x-X)+(y-Y)*(y-Y) < MAX_RANGE*MAX_RANGE
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
Firstly and Importantly, you must copy paste all the triggers in the (External) Category to your map.
Copy paste all of the contents of the trigger "Chain Fire 100" below to an empty trigger in your map.
Change the values to your needs that are in the frame:
//====================================================================================================
// User-edit data //
//====================================================================================================
Copy this data from the Object Editor:
UNITS:
Custom Units
|
--Neutral Passive
|
--Melee
|
--Units
|-- Chain Fire Dummy
ABILITIES: (Optional)
Custom Abilities
|
--Neutral Hostile
|
--Heroes
|
-- Chain Fire
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Chain Fire Version 1.00
scope ChainFire initializer Init_ChainFire
globals
private constant integer ABILTY_ID = 'A000'
private constant integer CHFIRE_ID = 'c000'
private constant timer cf_timer = CreateTimer()
private constant real CF_TIMER_INTERVAL = 0.01
private constant real STEP_DIST = 16.00
private constant real CHEK_DIST = 24.00
private constant real IB = 64.00
private constant real K = 40.00
private integer index = 0
private integer array temp_dat
private hashtable unit_flag = InitHashtable()
//====================================================================================================
// User-edit data //
//====================================================================================================
// Targets:
private constant boolean GROUND = TRUE // TRUE/FALSE to include/exclude ground units
private constant boolean FLYING = TRUE // TRUE/FALSE to include/exclude flying units
private constant boolean MECHANIC = FALSE // TRUE/FALSE to include/exclude mechanical units
private constant boolean STRUCTURE = FALSE // TRUE/FALSE to include/exclude structure units
// Damage Variables:
private constant boolean D_TABLE = FALSE // TRUE/FALSE to use Table/Formula
// Table:
private constant real array Damage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_CF = 25.00 // changes 'ConstantFactor'
private constant real D_LF = 25.00 // changes 'AbilityLevelFactor'
private constant real D_PLF = 0 // changes 'PrevLevelFactor'
// Area of Effect Variables:
private constant boolean R_TABLE = FALSE // TRUE/FALSE to use Table/Formula
// Table:
private constant real array Area // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real R_CF = 500.0 // changes 'ConstantFactor'
private constant real R_LF = 0 // changes 'AbilityLevelFactor'
private constant real R_PLF = 0 // changes 'PrevLevelFactor'
// Damage Reduction per Jump Variables:
private constant boolean N_TABLE = TRUE // TRUE/FALSE to use Table/Formula
// Table:
private constant real array DmgRed // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real N_CF = 0.2 // changes 'ConstantFactor'
private constant real N_LF = -0.05 // changes 'AbilityLevelFactor'
private constant real N_PLF = 0 // changes 'PrevLevelFactor'
// Jumps Variables:
private constant boolean J_TABLE = FALSE // TRUE/FALSE to use Table/Formula
// Table:
private constant integer array Jumps // set the values in the function named "Spell_Data_Table"
// Formula:
private constant integer J_CF = 2 // changes 'ConstantFactor'
private constant integer J_LF = 2 // changes 'AbilityLevelFactor'
private constant integer J_PLF = 0 // changes 'PrevLevelFactor'
private constant attacktype ATKTYPE = ATTACK_TYPE_NORMAL // Spell Attack Type
private constant damagetype DMGTYPE = DAMAGE_TYPE_FIRE // Fire Damage Type
endglobals
private function Spell_Data_Table takes nothing returns nothing
// If your spell is more than 3 levels just add Damage[]/Range[]/Jumps[] under its appropriate place
// Remember that if you don't add this line the Damage/Range/Jumps will be 0 for level 4+
// Damage Table
set Damage[1] = 50.0
set Damage[2] = 100.0
set Damage[3] = 200.0
// Range Table
set Area[1] = 500.0
set Area[2] = 500.0
set Area[3] = 500.0
// Jumps Table
set Jumps[1] = 4
set Jumps[2] = 6
set Jumps[3] = 8
// Damage Reduction Per Jump Table
set DmgRed[1] = 0.15
set DmgRed[2] = 0.10
set DmgRed[3] = 0.05
//====================================================================================================
// End of User-edit data //
//====================================================================================================
endfunction
private function CF_Damage takes integer al returns real
local real prevDmg
if D_TABLE then
return Damage[al]
else
if al == 1 then
set prevDmg = 0
else
set prevDmg = CF_Damage(al - 1)
endif
return D_CF + D_LF * al + D_PLF * prevDmg
endif
endfunction
private function CF_Area takes integer al returns real
local real prevArea
if R_TABLE then
return Area[al]
else
if al == 1 then
set prevArea = 0
else
set prevArea = CF_Area(al - 1)
endif
return R_CF + R_LF * al + R_PLF * prevArea
endif
endfunction
private function CF_Jump takes integer al returns integer
local integer prevJump
if J_TABLE then
return Jumps[al]
else
if al == 1 then
set prevJump = 0
else
set prevJump = CF_Jump(al - 1)
endif
return J_CF + J_LF * al + J_PLF * prevJump
endif
endfunction
private function CF_DamagReductionPerJump takes integer al returns real
local real prevDr
if N_TABLE then
return DmgRed[al]
else
if al == 1 then
set prevDr = 0
else
set prevDr = CF_DamagReductionPerJump(al - 1)
endif
return N_CF + N_LF * al + N_PLF * prevDr
endif
endfunction
private function IsTargetAllowed takes unit u returns boolean
if IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or/*
*/(not FLYING and IsUnitType(u,UNIT_TYPE_FLYING)) or/*
*/(not GROUND and IsUnitType(u,UNIT_TYPE_GROUND)) or/*
*/(not MECHANIC and IsUnitType(u,UNIT_TYPE_MECHANICAL)) or/*
*/(not STRUCTURE and IsUnitType(u,UNIT_TYPE_STRUCTURE)) then
return false
endif
return true
endfunction
private function GetNearestUnit takes player owner, unit source, real radius, integer dat returns unit
local group g = CreateGroup()
local group temp_g = CreateGroup()
local real locX = GetUnitX(source)
local real locY = GetUnitY(source)
local real dx
local real dy
local unit ref_u
local unit any_u
local unit temp_u
local real dist_ref_u
local real dist_any_u
call GroupEnumUnitsInRange(temp_g,locX,locY,radius,null)
//call GroupRemoveUnit(temp_g,source)
loop
set temp_u = FirstOfGroup(temp_g)
exitwhen temp_u == null
if temp_u != LoadUnitHandle(unit_flag,dat,GetHandleId(temp_u)) and/*
*/ not(IsUnitType(temp_u,UNIT_TYPE_DEAD)) and/*
*/ IsUnitEnemy(temp_u,owner) and/*
*/ IsTargetAllowed(temp_u) then
call GroupAddUnit(g,temp_u)
endif
call GroupRemoveUnit(temp_g,temp_u)
endloop
call DestroyGroup(temp_g)
set ref_u = FirstOfGroup(g)
call GroupRemoveUnit(g,ref_u)
loop
set any_u = FirstOfGroup(g)
exitwhen any_u == null
set dx = GetUnitX(ref_u) - locX
set dy = GetUnitY(ref_u) - locY
set dist_ref_u = SquareRoot(dx*dx + dy*dy)
set dx = GetUnitX(any_u) - locX
set dy = GetUnitY(any_u) - locY
set dist_any_u = SquareRoot(dx*dx + dy*dy)
if dist_ref_u > dist_any_u then
set ref_u = any_u
endif
call GroupRemoveUnit(g,any_u)
endloop
call DestroyGroup(g)
return ref_u
endfunction
private struct data extends array
player owner
unit caster
unit target
unit cfd // Chain Fire Dummy
real clx
real cly
real face
real slope
real stepK // Step Factor
//real MaxZ
real B
real damage
real areaeffect
real dr // Damage Reduction (Per Jump)
integer jump
implement Alloc
static method MoveCF takes nothing returns nothing
local thistype dat
local integer i = 0
local real x // Current X of Chain Fire Missile
local real y // Current Y of Chain Fire Missile
local real v
local real z
local real tx // Target X
local real ty // Target Y
local real tz // Target Z
local real dx
local real dy
local real dv
local real dz
local real rd
local real dist
local unit u
loop // cycle through instances
exitwhen i >= index
set dat = temp_dat[i]
set u = dat.target
set x = GetUnitX(dat.cfd)
set y = GetUnitY(dat.cfd)
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set tz = GetUnitFlyHeight(u) + K
set dx = tx - x
set dy = ty - y
set dat.face = Atan2(dy,dx)
set dist = SquareRoot(dx*dx + dy*dy)
if dist < CHEK_DIST then
call UnitDamageTarget(dat.caster, u,dat.damage, false, true, ATKTYPE, DMGTYPE, null)
call SaveUnitHandle(unit_flag,dat,GetHandleId(u),u)
set dat.jump = dat.jump - 1
set dat.damage = dat.damage * ( 1 - dat.dr)
if dat.jump <= 0 then // end the spell
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
call KillUnit(dat.cfd)
set dat.caster = null
set dat.target = null
set u = null
call FlushChildHashtable( unit_flag , dat )
if index == 0 then
call PauseTimer(cf_timer)
endif
call dat.deallocate()
else // seek the next target
set dat.target = GetNearestUnit(dat.owner,u,dat.areaeffect,dat)
if dat.target == null then
set dat.jump = 0
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
call KillUnit(dat.cfd)
set dat.caster = null
set dat.target = null
set u = null
call FlushChildHashtable( unit_flag , dat )
if index == 0 then
call PauseTimer(cf_timer)
endif
call dat.deallocate()
else
set dx = GetUnitX(dat.target) - tx
set dy = GetUnitY(dat.target) - ty
set dv = SquareRoot( dx*dx + dy*dy )
set dz = GetUnitFlyHeight(dat.target) - tz
set rd = SquareRoot( dv*dv + dz*dz )
set dat.clx = tx
set dat.cly = ty
set dat.B = tz
set dat.slope = dz / dv
set dat.stepK = rd / dv
set dat.face = Atan2(dy,dx)
call SetUnitFacing(dat.cfd,dat.face * bj_RADTODEG)
endif
endif
else
//set x = x + STEP_DIST * Cos(dat.face)
//set y = y + STEP_DIST * Sin(dat.face)
set dx = x - dat.clx
set dy = y - dat.cly
set v = SquareRoot( dx*dx + dy*dy )
set z = (dat.slope * v) + dat.B
call SetUnitX(dat.cfd,x + STEP_DIST * Cos(dat.face) / dat.stepK)
call SetUnitY(dat.cfd,y + STEP_DIST * Sin(dat.face) / dat.stepK)
call SetUnitFlyHeight(dat.cfd,z,0.0)
endif
set i = i + 1
endloop
endmethod
static method StartCF takes unit caster, unit target returns nothing
local thistype dat = thistype.allocate()
local integer al = GetUnitAbilityLevel(caster , ABILTY_ID)
local real cx // Caster X
local real cy // Caster Y
local real cz // Caster Z
local real tx // Target X
local real ty // Target Y
local real tz // Target Z
local real dx
local real dy
local real dv
local real dz
local real rd // Real Distance
set temp_dat[index] = dat
set dat.caster = caster
set dat.target = target
set dat.owner = GetOwningPlayer(caster)
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set dat.clx = cx
set dat.cly = cy
set dat.B = IB
set cz = GetUnitFlyHeight(caster) + dat.B
set tx = GetUnitX(target)
set ty = GetUnitY(target)
set tz = GetUnitFlyHeight(target) + K
set dx = tx - cx
set dy = ty - cy
set dz = tz - cz
set dv = SquareRoot( dx*dx + dy*dy )
set rd = SquareRoot( dv*dv + dz*dz )
set dat.slope = dz / dv
set dat.stepK = rd / dv
set dat.face = Atan2(dy,dx)
set dat.cfd = CreateUnit(GetOwningPlayer(caster),CHFIRE_ID,cx,cy,dat.face*bj_RADTODEG)
call SetUnitFlyHeight(dat.cfd,cz,0.0)
call SetUnitPathing( dat.cfd, false )
set dat.damage = CF_Damage(al)
set dat.areaeffect = CF_Area(al)
set dat.jump = CF_Jump(al)
set dat.dr = CF_DamagReductionPerJump(al)
//call BJDebugMsg(R2S(dat.dr))
if index ==0 then
call TimerStart(cf_timer,CF_TIMER_INTERVAL,true,function thistype.MoveCF)
endif
set index = index + 1
endmethod
endstruct
private function CF_Actions takes nothing returns nothing
if GetSpellAbilityId() == ABILTY_ID then
call data.StartCF(GetTriggerUnit(),GetSpellTargetUnit())
endif
endfunction
private function Init_ChainFire takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t,function CF_Actions)
call Spell_Data_Table()
//Preload the dummy units to reduce lag on first cast
call RemoveUnit(CreateUnit(Player(12),CHFIRE_ID,0.,0.,0.))
call RemoveUnit(CreateUnit(Player(12),CHFIRE_ID,0.,0.,0.))
endfunction
endscope