//TESH.scrollpos=0
//TESH.alwaysfold=0
library A
function SetUnitZ takes unit u , real z returns nothing
call SetUnitFlyHeight(u,z,0.0)
endfunction
endlibrary
Name | Type | is_array | initial_value |
BlueUnits | group | No | |
Hero | unit | No | |
StartLocation | location | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Meteor Swarm Version 1.00
scope MS initializer Init_MeteorSwarm
globals
public constant integer DUMMYID = 'e000'
public constant integer ABIL_ID = 'A000'
private constant timer tim = CreateTimer()
private integer array temp_dat
private integer index = 0
private constant integer INT_MIN = 10 // Minimum Interval Between Meteors Threshold
private constant integer INT_MAX = 100 // Maximum Interval Between Meteors Threshold
private constant real SCALE_MIN = 0.80 // Minimum Meteor Scale Threshold
private constant real SCALE_MAX = 3.50 // Maximum Meteor Scale Threshold
private constant real H_MIN = 750.00 // Minimum Meteor Height Threshold
private constant real H_MAX = 1000.00 // Maximum Meteor Height Threshold
private constant real M_DIST = 1000.00
private constant real SPEED = 0.01
private constant real STEP = 16.00
private constant real CHECK_DIST = 32.00
private hashtable mht = InitHashtable() // Meteor Hashtable
private hashtable mvht = InitHashtable() // Meteor Variables Hashtable
private constant group damage_group = CreateGroup()
//bj_Constants
private constant attacktype SPELL = ConvertAttackType(0)
private constant damagetype FIRE = ConvertDamageType(8)
private constant unittype DEAD = ConvertUnitType(1)
private constant unittype UT_STRUCTURE = ConvertUnitType(2)
private constant unittype UT_FLYING = ConvertUnitType(3)
private constant unittype UT_GROUND = ConvertUnitType(4)
private constant unittype MAGICIMMUNE = ConvertUnitType(26)
private constant real PI = 3.141592654
private constant real DTR = PI / 180.0
private constant real RTD = 180.0 / PI
// End bj_Constants
//====================================================================================================
// User-edit data //
//====================================================================================================
// Make sure to change the dummy spell text to be appropriate with the following variables.
// General Formula: ConstantFactor + AbilityLevel * AbilityLevelFactor + PrevLevelFactor * PrevLevelValue
// The following variable can be changed according to the desired results
// Number of Meteors Variables:
private constant boolean NUM_TABLE = true // true/false to use Table/Formula
// Table:
private constant integer array nMeteors // set the values in the function named "Spell_Data_Table"
// Formula:
private constant integer NUM_CF = 25 // changes 'ConstantFactor'
private constant integer NUM_LF = 5 // changes 'AbilityLevelFactor'
private constant real NUM_PLF = 0 // changes 'PrevLevelFactor'
// Minimum Damage Variables:
private constant boolean D_MIN_TABLE = true // true/false to use Table/Formula
// Table:
private constant real array MinDmg // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MIN_CF = 25.00 // changes 'ConstantFactor'
private constant real D_MIN_LF = 25.00 // changes 'AbilityLevelFactor'
private constant real D_MIN_PLF = 0 // changes 'PrevLevelFactor'
// Maximum Damage Variables:
private constant boolean D_MAX_TABLE = true // true/false to use Table/Formula
// Table:
private constant real array MaxDmg // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MAX_CF = 50.00 // changes 'ConstantFactor'
private constant real D_MAX_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real D_MAX_PLF = 0 // changes 'PrevLevelFactor'
// Area of Effect (AoE) Variables:
// Grand Area of Effect:
private constant boolean GR_TABLE = true // true/false to use Table/Formula
// Table:
private constant real array AreaGrand // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real GR_CF = 500.00 // changes 'ConstantFactor'
private constant real GR_LF = 0.00 // changes 'AbilityLevelFactor'
private constant real GR_PLF = 0 // changes 'PrevLevelFactor'
// Maximum AOE per Meteor:
private constant boolean MAXR_TABLE = true // true/false to use Table/Formula
// Table:
private constant real array MaxArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MAXR_CF = 128.00 // changes 'ConstantFactor'
private constant real MAXR_LF = 0.00 // changes 'AbilityLevelFactor'
private constant real MAXR_PLF = 0 // changes 'PrevLevelFactor'
// Minimum AOE per Meteor:
private constant boolean MINR_TABLE = true // true/false to use Table/Formula
// Table:
private constant real array MinArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MINR_CF = 96.00 // changes 'ConstantFactor'
private constant real MINR_LF = 0.00 // changes 'AbilityLevelFactor'
private constant real MINR_PLF = 0 // changes 'PrevLevelFactor'
// Targets:
private constant boolean FRIENDLY = true // true/false to harm enemy/all units
private constant boolean GROUND = true // true/false to include/exclude ground units
private constant boolean FLYING = false // true/false to include/exclude flying units
private constant boolean STRUCTURE = true // true/false to include/exclude structure units
endglobals
function Spell_Data_Table takes nothing returns nothing
// Number of Meteors Table
set nMeteors[1] = 30
set nMeteors[2] = 35
set nMeteors[3] = 40
set nMeteors[4] = 45
set nMeteors[5] = 50
// Minimum Damage Table
set MinDmg[1] = 100.00
set MinDmg[2] = 125.00
set MinDmg[3] = 150.00
set MinDmg[4] = 175.00
set MinDmg[5] = 200.00
// Maximum Damage Table
set MaxDmg[1] = 300.00
set MaxDmg[2] = 350.00
set MaxDmg[3] = 400.00
set MaxDmg[4] = 450.00
set MaxDmg[5] = 500.00
// Grand Area of Effect
set AreaGrand[1] = 800.00
set AreaGrand[2] = 850.00
set AreaGrand[3] = 900.00
set AreaGrand[4] = 950.00
set AreaGrand[5] = 1000.00
// Maximum Area of Effect per Meteor
set MaxArea[1] = 160.00
set MaxArea[2] = 170.00
set MaxArea[3] = 180.00
set MaxArea[4] = 190.00
set MaxArea[5] = 200.00
// Minimum Area of Effect per Meteor
set MinArea[1] = 110.00
set MinArea[2] = 120.00
set MinArea[3] = 130.00
set MinArea[4] = 140.00
set MinArea[5] = 150.00
//====================================================================================================
// End of User-edit data //
//====================================================================================================
endfunction
function MeteorsNumber takes integer al returns integer
local integer prevNMet
if NUM_TABLE then
return nMeteors[al]
else
if al == 1 then
set prevNMet = 0
else
set prevNMet = MeteorsNumber(al - 1)
endif
return NUM_CF + NUM_LF * al + R2I(NUM_PLF * I2R(prevNMet))
endif
endfunction
function MinDamage takes integer al returns real
local real prevMinDmg
if D_MIN_TABLE then
return MinDmg[al]
else
if al == 1 then
set prevMinDmg = 0
else
set prevMinDmg = MinDamage(al - 1)
endif
return D_MIN_CF + D_MIN_LF * al + D_MIN_PLF * prevMinDmg
endif
endfunction
function MaxDamage takes integer al returns real
local real prevMaxDmg
if D_MAX_TABLE then
return MaxDmg[al]
else
if al == 1 then
set prevMaxDmg = 0
else
set prevMaxDmg = MaxDamage(al - 1)
endif
return D_MAX_CF + D_MAX_LF * al + D_MAX_PLF * prevMaxDmg
endif
endfunction
function GrandArea takes integer al returns real
local real prevGrArea
if GR_TABLE then
return AreaGrand[al]
else
if al == 1 then
set prevGrArea = 0
else
set prevGrArea = GrandArea(al - 1)
endif
return GR_CF + GR_LF * al + GR_PLF * prevGrArea
endif
endfunction
function MinAreaPerMeteor takes integer al returns real
local real prevMinA
if MINR_TABLE then
return MinArea[al]
else
if al == 1 then
set prevMinA = 0
else
set prevMinA = MinAreaPerMeteor(al - 1)
endif
return MINR_CF + MINR_LF * al + MINR_PLF * prevMinA
endif
endfunction
function MaxAreaPerMeteor takes integer al returns real
local real prevMaxA
if MAXR_TABLE then
return MaxArea[al]
else
if al == 1 then
set prevMaxA = 0
else
set prevMaxA = MaxAreaPerMeteor(al - 1)
endif
return MAXR_CF + MAXR_LF * al + MAXR_PLF * prevMaxA
endif
endfunction
private struct Meteor extends array
unit caster
unit meteor
unit owner
real TX
real TY
real slope
real const
real face
real grandarea
real minarea
real maxarea
real mindamage
real maxdamage
integer meteors
integer ibem // Interval between each meteor
integer cfm // Current Falling Meteors
implement Alloc
static method MatchingUnit takes unit u , player owner returns boolean
if IsUnitType(u,DEAD) or/*
*/ IsUnitType(u,MAGICIMMUNE) or/*
*/( FRIENDLY and IsUnitAlly(u,owner)) or/*
*/(not FLYING and IsUnitType(u,UT_FLYING)) or/*
*/(not GROUND and IsUnitType(u,UT_GROUND)) or/*
*/(not STRUCTURE and IsUnitType(u,UT_STRUCTURE)) then
return false
endif
return true
endmethod
static method Damage takes integer this , real centreX , real centreY , real radius , real damage returns nothing
local group temp_g = CreateGroup()
local thistype dat = this
local unit tu
call GroupEnumUnitsInRange(temp_g,centreX,centreY,radius,null)
loop
set tu = FirstOfGroup(temp_g)
exitwhen tu == null
if MatchingUnit(tu,GetOwningPlayer(dat.caster)) then
call UnitDamageTarget(dat.caster,tu,damage,false,true,SPELL,FIRE,null)
endif
call GroupRemoveUnit(temp_g,tu)
endloop
call DestroyGroup(temp_g)
endmethod
static method fall takes nothing returns nothing
local thistype dat
local integer i = 0
local integer j = 0
local unit u
local unit nu
local unit tu // Target Unit
local real x
local real y
local real z
local real ix
local real iy
//local real iz
local real tx
local real ty
local real dx
local real dy
local real dv
local real face
local real slope
local real const
local real ratio
local real ntx
local real nty
local real nface
local real nix
local real niy
local real niz
local real rndr
local real rnda
local real bound
local real scale
local real nratio
local real act_rad
local real act_dmg
loop
exitwhen i >= index
set dat = temp_dat[i]
//===========================================================================
set dat.ibem = dat.ibem - 1
if dat.ibem <= 0 then
set dat.ibem = GetRandomInt(INT_MIN,INT_MAX)
set dat.cfm = dat.cfm + 1
if dat.cfm <= dat.meteors then
// launch another meteor
set bound = dat.grandarea - dat.maxarea
set rndr = GetRandomReal(0.0,bound)
set rnda = GetRandomReal(1.0,360.0)
set ntx = dat.TX + rndr*Cos(rnda*DTR)
set nty = dat.TY + rndr*Sin(rnda*DTR)
set nface = GetRandomInt(1,360)
//set nface = dat.face
set nix = ntx - M_DIST * Cos(nface*DTR)
set niy = nty - M_DIST * Sin(nface*DTR)
set niz = GetRandomReal(H_MIN,H_MAX)
set nu = CreateUnit(GetOwningPlayer(dat.caster),DUMMYID,nix,niy,nface)
call SetUnitPathing(nu,false)
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
set nratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(nu,scale,scale,scale)
call SetUnitFlyHeight(nu,niz,0.0)
call SaveUnitHandle(mht,dat,dat.cfm,nu)
call SaveBoolean(mht,dat,dat.cfm,false)
call SaveReal(mht , dat, dat.cfm,ntx)
call SaveReal(mht , dat,-dat.cfm,nty)
call SaveReal(mht ,-dat, dat.cfm,nix)
call SaveReal(mht ,-dat,-dat.cfm,niy)
call SaveReal(mvht, dat, dat.cfm,nface)
call SaveReal(mvht, dat,-dat.cfm,nratio)
call SaveReal(mvht,-dat, dat.cfm,-niz/M_DIST)
call SaveReal(mvht,-dat,-dat.cfm,niz)
endif
endif
set j = 1
loop
//exitwhen j > dat.meteors
exitwhen j > dat.cfm
set u = LoadUnitHandle(mht,dat,j)
if not(LoadBoolean(mht,dat,j)) then
set tx = LoadReal(mht, dat, j)
set ty = LoadReal(mht, dat,-j)
set ix = LoadReal(mht,-dat, j)
set iy = LoadReal(mht,-dat,-j)
set x = GetUnitX(u)
set y = GetUnitY(u)
set dx = tx - x
set dy = ty - y
set ratio = LoadReal(mvht,dat,-j)
set act_rad = ratio * (dat.maxarea - dat.minarea ) + dat.minarea
set act_dmg = ratio * (dat.maxdamage - dat.mindamage) + dat.mindamage
if SquareRoot( dx*dx + dy*dy ) < CHECK_DIST or GetUnitFlyHeight(u) < CHECK_DIST then
call KillUnit(u)
call SaveBoolean(mht,dat,j,true)
// Shake camera
// Do Damage stuff here
call Damage(dat,tx,ty,act_rad,act_dmg)
if IsUnitType(LoadUnitHandle(mht,dat,dat.meteors),DEAD) and/*
*/ LoadUnitHandle(mht,dat,dat.meteors) != null then
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
call FlushChildHashtable( mht, dat)
call FlushChildHashtable( mht,-dat)
call FlushChildHashtable(mvht, dat)
call FlushChildHashtable(mvht,-dat)
if index == 0 then
call PauseTimer(tim)
endif
call dat.deallocate()
endif
else // Move the meteor
set face = LoadReal(mvht,dat,j)
//set face = dat.face
set slope = LoadReal(mvht,-dat,j)
set const = LoadReal(mvht,-dat,-j)
set x = x + STEP * Cos(face*DTR)
set y = y + STEP * Sin(face*DTR)
set dx = x - ix
set dy = y - iy
set dv = SquareRoot( dx*dx + dy*dy )
set z = slope * dv + const
call SetUnitX(u,x)
call SetUnitY(u,y)
call SetUnitFlyHeight(u,z,0.0)
endif
endif
set j = j + 1
endloop
//===========================================================================
set i = i + 1
endloop
endmethod
static method Start takes unit caster, real tx, real ty returns nothing
local thistype dat = thistype.allocate()
local integer al = GetUnitAbilityLevel(caster,ABIL_ID)
local unit u
local real cx
local real cy
local real ix
local real iy
local real iz
local real dx
local real dy
local real face
local real scale
local real ratio
set temp_dat[index] = dat
set dat.caster = caster
set dat.TX = tx
set dat.TY = ty
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set dx = tx - cx
set dy = ty - cy
set face = Atan2(dy,dx)
set ix = tx - M_DIST * Cos(face)
set iy = ty - M_DIST * Sin(face)
set iz = GetRandomReal(H_MIN,H_MAX)
set u = CreateUnit(GetOwningPlayer(caster),DUMMYID,ix,iy,face*RTD)
call SetUnitPathing(u,false)
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
set ratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(u,scale,scale,scale)
call SetUnitFlyHeight(u,iz,0.0)
set dat.cfm = 1
set dat.ibem = GetRandomInt(INT_MIN,INT_MAX)
call SaveUnitHandle(mht,dat,1,u)
call SaveBoolean(mht,dat,1,false)
call SaveReal(mht , dat, 1,tx)
call SaveReal(mht , dat,-1,ty)
call SaveReal(mht ,-dat, 1,ix)
call SaveReal(mht ,-dat,-1,iy)
call SaveReal(mvht, dat, 1,face*RTD)
call SaveReal(mvht, dat,-1,ratio)
call SaveReal(mvht,-dat, 1,-iz/M_DIST)
call SaveReal(mvht,-dat,-1,iz)
set dat.meteors = MeteorsNumber(al)
set dat.grandarea = GrandArea(al)
set dat.maxarea = MaxAreaPerMeteor(al)
set dat.minarea = MinAreaPerMeteor(al)
set dat.maxdamage = MaxDamage(al)
set dat.mindamage = MinDamage(al)
if index == 0 then
call TimerStart(tim,SPEED,true, function thistype.fall )
endif
set index = index + 1
endmethod
endstruct
function MeteorSwarm_Actions takes nothing returns nothing
call Meteor.Start(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
endfunction
function MeteorSwarm_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == ABIL_ID ) ) then
return false
endif
return true
endfunction
//===========================================================================
function Init_MeteorSwarm takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, function MeteorSwarm_Conditions )
call TriggerAddAction( t, function MeteorSwarm_Actions )
call RemoveUnit(CreateUnit(Player(12),DUMMYID,0.,0.,0.))
call Spell_Data_Table()
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Meteor Swarm Version 1.10
scope MS initializer Init_MeteorSwarm
globals
private constant integer M_DUMMY_ID = 'e000'
private constant integer ABILITY_ID = 'A000'
private constant timer tim = CreateTimer()
private integer array temp_dat
private integer index = 0
private constant integer INT_MIN = 10 // Minimum Interval Between Meteors Threshold
private constant integer INT_MAX = 100 // Maximum Interval Between Meteors Threshold
private constant real SCALE_MIN = 0.80 // Minimum Meteor Scale Threshold
private constant real SCALE_MAX = 3.50 // Maximum Meteor Scale Threshold
private constant real H_MIN = 750.00 // Minimum Meteor Height Threshold
private constant real H_MAX = 1000.00 // Maximum Meteor Height Threshold
private constant real INIT_DIST = 1000.00
private constant real SPEED = 0.01
private constant real STEP = 16.00
private constant real CHECK_DIST = 32.00
private constant integer HT_OFFSET = 4096
private hashtable mht = InitHashtable() // Meteor Hashtable
//private constant group damage_group = CreateGroup()
//====================================================================================================
// User-edit data //
//====================================================================================================
// Make sure to change the dummy spell text to be appropriate with the following variables.
// General Formula: ConstantFactor + AbilityLevel * AbilityLevelFactor + PrevLevelFactor * PrevLevelValue
// The following variable can be changed according to the desired results
// Number of Meteors Variables:
private constant boolean NUM_TABLE = false // true/false to use Table/Formula
// Table:
private constant integer array nMeteors // set the values in the function named "Spell_Data_Table"
// Formula:
private constant integer NUM_CF = 25 // changes 'ConstantFactor'
private constant integer NUM_LF = 5 // changes 'AbilityLevelFactor'
private constant real NUM_PLF = 0 // changes 'PrevLevelFactor'
// Minimum Damage Variables:
private constant boolean D_MIN_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MinDmg // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MIN_CF = 75.00 // changes 'ConstantFactor'
private constant real D_MIN_LF = 25.00 // changes 'AbilityLevelFactor'
private constant real D_MIN_PLF = 0 // changes 'PrevLevelFactor'
// Maximum Damage Variables:
private constant boolean D_MAX_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MaxDmg // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MAX_CF = 250.00 // changes 'ConstantFactor'
private constant real D_MAX_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real D_MAX_PLF = 0 // changes 'PrevLevelFactor'
// Area of Effect (AoE) Variables:
// Grand Area of Effect:
private constant boolean GR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array AreaGrand // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real GR_CF = 750.00 // changes 'ConstantFactor'
private constant real GR_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real GR_PLF = 0 // changes 'PrevLevelFactor'
// Maximum AOE per Meteor:
private constant boolean MAXR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MaxArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MAXR_CF = 150.00 // changes 'ConstantFactor'
private constant real MAXR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MAXR_PLF = 0 // changes 'PrevLevelFactor'
// Minimum AOE per Meteor:
private constant boolean MINR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MinArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MINR_CF = 100.00 // changes 'ConstantFactor'
private constant real MINR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MINR_PLF = 0 // changes 'PrevLevelFactor'
// Targets:
private constant boolean FRIENDLY = true // true/false to harm enemy/all units
private constant boolean GROUND = true // true/false to include/exclude ground units
private constant boolean FLYING = false // true/false to include/exclude flying units
private constant boolean STRUCTURE = true // true/false to include/exclude structure units
// Flags:
private constant boolean INIT_MAXD = true // true/false to enable/disable maximum damage on first meteor
endglobals
// Just IGNORE this function (Table) IF you are using a formula (Just fold it)
private function Spell_Data_Table takes nothing returns nothing
// Number of Meteors Table
set nMeteors[1] = 30
set nMeteors[2] = 35
set nMeteors[3] = 40
set nMeteors[4] = 45
set nMeteors[5] = 50
// Minimum Damage Table
set MinDmg[1] = 100.00
set MinDmg[2] = 125.00
set MinDmg[3] = 150.00
set MinDmg[4] = 175.00
set MinDmg[5] = 200.00
// Maximum Damage Table
set MaxDmg[1] = 300.00
set MaxDmg[2] = 350.00
set MaxDmg[3] = 400.00
set MaxDmg[4] = 450.00
set MaxDmg[5] = 500.00
// Maximum Area of Effect per Meteor
set MaxArea[1] = 160.00
set MaxArea[2] = 170.00
set MaxArea[3] = 180.00
set MaxArea[4] = 190.00
set MaxArea[5] = 200.00
// Minimum Area of Effect per Meteor
set MinArea[1] = 110.00
set MinArea[2] = 120.00
set MinArea[3] = 130.00
set MinArea[4] = 140.00
set MinArea[5] = 150.00
// Grand Area of Effect
set AreaGrand[1] = 800.00
set AreaGrand[2] = 850.00
set AreaGrand[3] = 900.00
set AreaGrand[4] = 950.00
set AreaGrand[5] = 1000.00
//====================================================================================================
// End of User-edit data //
//====================================================================================================
endfunction
private function MeteorsNumber takes integer al returns integer
local integer prevNMet
static if NUM_TABLE then
return nMeteors[al]
else
if al == 1 then
set prevNMet = 0
else
set prevNMet = MeteorsNumber(al - 1)
endif
return NUM_CF + NUM_LF * al + R2I(NUM_PLF * I2R(prevNMet))
endif
endfunction
private function MinDamage takes integer al returns real
local real prevMinDmg
static if D_MIN_TABLE then
return MinDmg[al]
else
if al == 1 then
set prevMinDmg = 0
else
set prevMinDmg = MinDamage(al - 1)
endif
return D_MIN_CF + D_MIN_LF * al + D_MIN_PLF * prevMinDmg
endif
endfunction
private function MaxDamage takes integer al returns real
local real prevMaxDmg
static if D_MAX_TABLE then
return MaxDmg[al]
else
if al == 1 then
set prevMaxDmg = 0
else
set prevMaxDmg = MaxDamage(al - 1)
endif
return D_MAX_CF + D_MAX_LF * al + D_MAX_PLF * prevMaxDmg
endif
endfunction
private function GrandArea takes integer al returns real
local real prevGrArea
static if GR_TABLE then
return AreaGrand[al]
else
if al == 1 then
set prevGrArea = 0
else
set prevGrArea = GrandArea(al - 1)
endif
return GR_CF + GR_LF * al + GR_PLF * prevGrArea
endif
endfunction
private function MinAreaPerMeteor takes integer al returns real
local real prevMinA
static if MINR_TABLE then
return MinArea[al]
else
if al == 1 then
set prevMinA = 0
else
set prevMinA = MinAreaPerMeteor(al - 1)
endif
return MINR_CF + MINR_LF * al + MINR_PLF * prevMinA
endif
endfunction
private function MaxAreaPerMeteor takes integer al returns real
local real prevMaxA
static if MAXR_TABLE then
return MaxArea[al]
else
if al == 1 then
set prevMaxA = 0
else
set prevMaxA = MaxAreaPerMeteor(al - 1)
endif
return MAXR_CF + MAXR_LF * al + MAXR_PLF * prevMaxA
endif
endfunction
private function MatchingUnit takes unit u,player owner returns boolean
return not( IsUnitType(u,UNIT_TYPE_DEAD) or/*
*/ IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or/*
*/( FRIENDLY and IsUnitAlly(u,owner)) or/*
*/(not FLYING and IsUnitType(u,UNIT_TYPE_FLYING)) or/*
*/(not GROUND and IsUnitType(u,UNIT_TYPE_GROUND)) or/*
*/(not STRUCTURE and IsUnitType(u,UNIT_TYPE_STRUCTURE)))
endfunction
private function meteorDamage takes player owner,unit caster,real X, real Y,real rad,real damage returns nothing
local unit target
call GroupEnumUnitsInRange(bj_lastCreatedGroup,X,Y,rad,null)
loop
set target = FirstOfGroup(bj_lastCreatedGroup)
exitwhen target == null
if MatchingUnit(target,owner) then
call UnitDamageTarget(caster,target,damage,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
endif
call GroupRemoveUnit(bj_lastCreatedGroup,target)
endloop
endfunction
private struct Meteor extends array
player owner
unit caster
unit meteor
real TX
real TY
real slope
real const
real face
real grandarea
real minarea
real maxarea
real mindamage
real maxdamage
integer meteors
integer ibem // Interval between each meteor
integer cfm // Current Falling Meteors
implement Alloc
static method meteorFall takes nothing returns nothing
local thistype this
local integer i = 0
local integer j = 0
local unit u
local unit nu
local real x
local real y
local real z
local real ix
local real iy
local real tx
local real ty
local real dx
local real dy
local real dv
local real face
local real slope
local real const
local real ratio
local real ntx
local real nty
local real nface
local real nix
local real niy
local real niz
local real rnd_rad
local real rnd_ang
local real bound
local real scale
local real nratio
local real act_rad
local real act_dmg
loop
exitwhen i >= index
set this = temp_dat[i]
set this.ibem = this.ibem - 1
if this.ibem <= 0 then
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
set this.cfm = this.cfm + 1
if this.cfm <= this.meteors then
// launch another meteor
set bound = this.grandarea - this.maxarea
set rnd_rad = GetRandomReal(0.0,bound)
set rnd_ang = GetRandomReal(0.0,2.*bj_PI)
set ntx = this.TX + rnd_rad*Cos(rnd_ang)
set nty = this.TY + rnd_rad*Sin(rnd_ang)
set nface = GetRandomReal(0.0,2.*bj_PI)
set nix = ntx - INIT_DIST * Cos(nface)
set niy = nty - INIT_DIST * Sin(nface)
set niz = GetRandomReal(H_MIN,H_MAX)
set nu = CreateUnit(this.owner,M_DUMMY_ID,nix,niy,nface*bj_RADTODEG)
call SetUnitPathing(nu,false)
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
set nratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(nu,scale,scale,scale)
call SetUnitFlyHeight(nu,niz,0.0)
call SaveUnitHandle(mht,this,this.cfm,nu)
call SaveBoolean(mht,this,this.cfm,false)
call SaveReal(mht, this, this.cfm,ntx)
call SaveReal(mht, this,-this.cfm,nty)
call SaveReal(mht,-this, this.cfm,nix)
call SaveReal(mht,-this,-this.cfm,niy)
call SaveReal(mht, this + HT_OFFSET, this.cfm,nface)
call SaveReal(mht, this + HT_OFFSET,-this.cfm,nratio)
call SaveReal(mht,-this - HT_OFFSET, this.cfm,-niz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-this.cfm,niz)
endif
endif
set j = 1
loop
exitwhen j > this.cfm
set u = LoadUnitHandle(mht,this,j)
if not(LoadBoolean(mht,this,j)) then
set tx = LoadReal(mht, this, j)
set ty = LoadReal(mht, this,-j)
set ix = LoadReal(mht,-this, j)
set iy = LoadReal(mht,-this,-j)
set x = GetUnitX(u)
set y = GetUnitY(u)
set dx = tx - x
set dy = ty - y
set ratio = LoadReal(mht,this + HT_OFFSET,-j)
set act_rad = ratio * (this.maxarea - this.minarea ) + this.minarea
set act_dmg = ratio * (this.maxdamage - this.mindamage) + this.mindamage
if SquareRoot( dx*dx + dy*dy ) < CHECK_DIST or GetUnitFlyHeight(u) < CHECK_DIST then
call KillUnit(u)
call SaveBoolean(mht,this,j,true)
// Shake camera (to be made)
// Do Damage stuff here
call meteorDamage(this.owner,this.caster,tx,ty,act_rad,act_dmg)
if IsUnitType(LoadUnitHandle(mht,this,this.meteors),UNIT_TYPE_DEAD) and/*
*/ LoadUnitHandle(mht,this,this.meteors) != null then
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
call FlushChildHashtable(mht, this)
call FlushChildHashtable(mht,-this)
call FlushChildHashtable(mht, this + HT_OFFSET)
call FlushChildHashtable(mht,-this - HT_OFFSET)
if index == 0 then
call PauseTimer(tim)
endif
call this.deallocate()
endif
else // Move the meteor
set face = LoadReal(mht,this + HT_OFFSET,j)
set slope = LoadReal(mht,-this - HT_OFFSET,j)
set const = LoadReal(mht,-this - HT_OFFSET,-j)
set x = x + STEP * Cos(face)
set y = y + STEP * Sin(face)
set dx = x - ix
set dy = y - iy
set dv = SquareRoot( dx*dx + dy*dy )
set z = slope * dv + const
call SetUnitX(u,x)
call SetUnitY(u,y)
call SetUnitFlyHeight(u,z,0.0)
endif
endif
set j = j + 1
endloop
set i = i + 1
endloop
set u = null
set nu = null
endmethod
static method meteorCall takes unit caster, real tx, real ty returns nothing
local thistype this = thistype.allocate()
local integer al = GetUnitAbilityLevel(caster,ABILITY_ID)
local unit u
local real cx
local real cy
local real ix
local real iy
local real iz
local real dx
local real dy
local real face
local real scale
local real ratio
set temp_dat[index] = this
set this.caster = caster
set this.owner = GetOwningPlayer(caster)
set this.TX = tx
set this.TY = ty
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set dx = tx - cx
set dy = ty - cy
set face = Atan2(dy,dx)
set ix = tx - INIT_DIST * Cos(face)
set iy = ty - INIT_DIST * Sin(face)
set iz = GetRandomReal(H_MIN,H_MAX)
set u = CreateUnit(this.owner,M_DUMMY_ID,ix,iy,face*bj_RADTODEG)
call SetUnitPathing(u,false)
static if INIT_MAXD then
set scale = SCALE_MAX
else
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
endif
set ratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(u,scale,scale,scale)
call SetUnitFlyHeight(u,iz,0.0)
set this.cfm = 1
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
call SaveUnitHandle(mht,this,1,u)
call SaveBoolean(mht,this,1,false)
call SaveReal(mht, this, 1,tx)
call SaveReal(mht, this,-1,ty)
call SaveReal(mht,-this, 1,ix)
call SaveReal(mht,-this,-1,iy)
call SaveReal(mht, this + HT_OFFSET, 1,face)
call SaveReal(mht, this + HT_OFFSET,-1,ratio)
call SaveReal(mht,-this - HT_OFFSET, 1,-iz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-1,iz)
set this.meteors = MeteorsNumber(al)
set this.grandarea = GrandArea(al)
set this.maxarea = MaxAreaPerMeteor(al)
set this.minarea = MinAreaPerMeteor(al)
set this.maxdamage = MaxDamage(al)
set this.mindamage = MinDamage(al)
set u = null
if index == 0 then
call TimerStart(tim,SPEED,true, function thistype.meteorFall )
endif
set index = index + 1
endmethod
endstruct
function MeteorSwarm_Conditions takes nothing returns boolean
if GetSpellAbilityId() == ABILITY_ID then
call Meteor.meteorCall(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
endif
return true
endfunction
function Init_MeteorSwarm takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, function MeteorSwarm_Conditions )
call RemoveUnit(CreateUnit(Player(12),M_DUMMY_ID,0.,0.,0.))
call Spell_Data_Table()
set t = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Meteor Swarm Version 1.11
library Utils initializer SetBoundary
globals
public real MIN_X
public real MIN_Y
public real MAX_X
public real MAX_Y
private constant real SAFETY_OFFSET = 32.00
endglobals
private function SetBoundary takes nothing returns nothing
set MIN_X = GetRectMinX(bj_mapInitialPlayableArea)
set MIN_Y = GetRectMinY(bj_mapInitialPlayableArea)
set MAX_X = GetRectMaxX(bj_mapInitialPlayableArea)
set MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea)
endfunction
function GetSpellTargetXBounded takes nothing returns real
local real x = GetSpellTargetX()
if x < MIN_X then
return MIN_X + SAFETY_OFFSET
elseif x > MAX_X then
return MAX_X - SAFETY_OFFSET
else
return x
endif
endfunction
function GetSpellTargetYBounded takes nothing returns real
local real y = GetSpellTargetY()
if y < MIN_Y then
return MIN_Y + SAFETY_OFFSET
elseif y > MAX_Y then
return MAX_Y - SAFETY_OFFSET
else
return y
endif
endfunction
endlibrary
scope MS initializer Init_MeteorSwarm
globals
private constant integer M_DUMMY_ID = 'e000'
private constant integer B_DUMMY_ID = 'b000'
private constant integer ABILITY_ID = 'A000'
private constant timer tim = CreateTimer()
private integer array temp_dat
private integer index = 0
private constant integer INT_MIN = 10 // Minimum Interval Between Meteors Threshold
private constant integer INT_MAX = 100 // Maximum Interval Between Meteors Threshold
private constant real SCALE_MIN = 0.80 // Minimum Meteor Scale Threshold
private constant real SCALE_MAX = 3.50 // Maximum Meteor Scale Threshold
private constant real H_MIN = 750.00 // Minimum Meteor Height Threshold
private constant real H_MAX = 1000.00 // Maximum Meteor Height Threshold
private constant real INIT_DIST = 1000.00
private constant real SPEED = 0.01
private constant real STEP = 16.00
private constant real CHECK_DIST = 32.00
private constant integer HT_OFFSET = 4096
private hashtable mht = InitHashtable() // Meteor Hashtable
//private constant group damage_group = CreateGroup()
private unit CASTER
private real DAMAGE
//====================================================================================================
// User-edit data //
//====================================================================================================
// Make sure to change the dummy spell text to be appropriate with the following variables.
// General Formula: ConstantFactor + AbilityLevel * AbilityLevelFactor + PrevLevelFactor * PrevLevelValue
// The following variable can be changed according to the desired results
// Number of Meteors Variables:
private constant boolean NUM_TABLE = false // true/false to use Table/Formula
// Table:
private constant integer array nMeteors // set the values in the function named "Spell_Data_Table"
// Formula:
private constant integer NUM_CF = 25 // changes 'ConstantFactor'
private constant integer NUM_LF = 5 // changes 'AbilityLevelFactor'
private constant real NUM_PLF = 0 // changes 'PrevLevelFactor'
// Minimum Damage Variables:
private constant boolean D_MIN_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MinDamage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MIN_CF = 75.00 // changes 'ConstantFactor'
private constant real D_MIN_LF = 25.00 // changes 'AbilityLevelFactor'
private constant real D_MIN_PLF = 0 // changes 'PrevLevelFactor'
// Maximum Damage Variables:
private constant boolean D_MAX_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MaxDamage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MAX_CF = 250.00 // changes 'ConstantFactor'
private constant real D_MAX_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real D_MAX_PLF = 0 // changes 'PrevLevelFactor'
// Area of Effect (AoE) Variables:
// Grand Area of Effect:
private constant boolean GR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array GrandArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real GR_CF = 750.00 // changes 'ConstantFactor'
private constant real GR_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real GR_PLF = 0 // changes 'PrevLevelFactor'
// Maximum AOE per Meteor:
private constant boolean MAXR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array LargeArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MAXR_CF = 150.00 // changes 'ConstantFactor'
private constant real MAXR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MAXR_PLF = 0 // changes 'PrevLevelFactor'
// Minimum AOE per Meteor:
private constant boolean MINR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array SmallArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MINR_CF = 100.00 // changes 'ConstantFactor'
private constant real MINR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MINR_PLF = 0 // changes 'PrevLevelFactor'
// Targets:
private constant boolean FRIENDLY = true // true/false to harm enemy/all units
private constant boolean GROUND = true // true/false to include/exclude ground units
private constant boolean FLYING = false // true/false to include/exclude flying units
private constant boolean STRUCTURE = true // true/false to include/exclude structure units
private constant boolean DESTRUCTABLES = true
private constant real DESTRUCTABLE_FACTOR = 10.00
// Flags:
private constant boolean INIT_MAX_D = true // true/false to enable/disable maximum damage on first meteor
private constant boolean SHOW_INDICATOR = true
endglobals
// Just IGNORE this function (Table) IF you are using a formula (Just fold it)
private function Spell_Data_Table takes nothing returns nothing
// Number of Meteors Table
set nMeteors[1] = 30
set nMeteors[2] = 35
set nMeteors[3] = 40
set nMeteors[4] = 45
set nMeteors[5] = 50
// Minimum Damage Table
set MinDamage[1] = 100.00
set MinDamage[2] = 125.00
set MinDamage[3] = 150.00
set MinDamage[4] = 175.00
set MinDamage[5] = 200.00
// Maximum Damage Table
set MaxDamage[1] = 300.00
set MaxDamage[2] = 350.00
set MaxDamage[3] = 400.00
set MaxDamage[4] = 450.00
set MaxDamage[5] = 500.00
// Grand Area of Effect
set GrandArea[1] = 800.00
set GrandArea[2] = 850.00
set GrandArea[3] = 900.00
set GrandArea[4] = 950.00
set GrandArea[5] = 1000.00
// Maximum Area of Effect per Meteor
set LargeArea[1] = 160.00
set LargeArea[2] = 170.00
set LargeArea[3] = 180.00
set LargeArea[4] = 190.00
set LargeArea[5] = 200.00
// Minimum Area of Effect per Meteor
set SmallArea[1] = 110.00
set SmallArea[2] = 120.00
set SmallArea[3] = 130.00
set SmallArea[4] = 140.00
set SmallArea[5] = 150.00
//====================================================================================================
// End of User-edit data //
//====================================================================================================
endfunction
private function MeteorsNumber takes integer al returns integer
local integer prevNMet
static if NUM_TABLE then
return nMeteors[al]
else
if al == 1 then
set prevNMet = 0
else
set prevNMet = MeteorsNumber(al - 1)
endif
return NUM_CF + NUM_LF * al + R2I(NUM_PLF * I2R(prevNMet))
endif
endfunction
private function DamageMin takes integer al returns real
local real prevMinDmg
static if D_MIN_TABLE then
return MinDamage[al]
else
if al == 1 then
set prevMinDmg = 0
else
set prevMinDmg = DamageMin(al - 1)
endif
return D_MIN_CF + D_MIN_LF * al + D_MIN_PLF * prevMinDmg
endif
endfunction
private function DamageMax takes integer al returns real
local real prevMaxDmg
static if D_MAX_TABLE then
return MaxDamage[al]
else
if al == 1 then
set prevMaxDmg = 0
else
set prevMaxDmg = DamageMax(al - 1)
endif
return D_MAX_CF + D_MAX_LF * al + D_MAX_PLF * prevMaxDmg
endif
endfunction
private function AreaGrand takes integer al returns real
local real prevGrArea
static if GR_TABLE then
return GrandArea[al]
else
if al == 1 then
set prevGrArea = 0
else
set prevGrArea = AreaGrand(al - 1)
endif
return GR_CF + GR_LF * al + GR_PLF * prevGrArea
endif
endfunction
private function MinAreaPerMeteor takes integer al returns real
local real prevMinA
static if MINR_TABLE then
return SmallArea[al]
else
if al == 1 then
set prevMinA = 0
else
set prevMinA = MinAreaPerMeteor(al - 1)
endif
return MINR_CF + MINR_LF * al + MINR_PLF * prevMinA
endif
endfunction
private function MaxAreaPerMeteor takes integer al returns real
local real prevMaxA
static if MAXR_TABLE then
return LargeArea[al]
else
if al == 1 then
set prevMaxA = 0
else
set prevMaxA = MaxAreaPerMeteor(al - 1)
endif
return MAXR_CF + MAXR_LF * al + MAXR_PLF * prevMaxA
endif
endfunction
private function MatchingUnit takes unit u,player owner returns boolean
return not( IsUnitType(u,UNIT_TYPE_DEAD) or/*
*/ IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or/*
*/( FRIENDLY and IsUnitAlly(u,owner)) or/*
*/(not FLYING and IsUnitType(u,UNIT_TYPE_FLYING)) or/*
*/(not GROUND and IsUnitType(u,UNIT_TYPE_GROUND)) or/*
*/(not STRUCTURE and IsUnitType(u,UNIT_TYPE_STRUCTURE)))
endfunction
private function meteorDamageUnits takes player owner,unit caster,real X, real Y,real rad,real damage returns nothing
local unit target
call GroupEnumUnitsInRange(bj_lastCreatedGroup,X,Y,rad,null)
loop
set target = FirstOfGroup(bj_lastCreatedGroup)
exitwhen target == null
if MatchingUnit(target,owner) then
call UnitDamageTarget(caster,target,damage,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
endif
call GroupRemoveUnit(bj_lastCreatedGroup,target)
endloop
endfunction
/* this function/filter is not needed for now and is turned off
private function matchingDestructable takes nothing returns boolean
return GetDestructableTypeId(GetFilterDestructable()) == WHO_KNOWS_WHAT_TYPE
endfunction*/
private function meteorDamageDestructables takes nothing returns nothing
call UnitDamageTarget(CASTER,GetEnumDestructable(),DAMAGE,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
endfunction
private struct Meteor extends array
player owner
unit caster
unit targetindicator
unit meteor
real TX
real TY
real slope
real const
real face
real grandarea
real minarea
real maxarea
real mindamage
real maxdamage
integer meteors
integer ibem // Interval Between Each Meteor
integer cfm // Current Falling Meteors
implement Alloc
static method meteorFall takes nothing returns nothing
local thistype this
local integer i = 0 // Instance Counter index
local integer j = 0 // Meteor Counter index
local unit u // This Dummy Unit
local unit nu // Next Dummy Unit
local real x // This Meteor X
local real y // This Meteor Y
local real z // This Meteor Z
local real ix // This Meteor Initial X
local real iy // This Meteor Initial Y
local real tx // This Meteor Target X
local real ty // This Meteor Target Y
local real dx // Delta X
local real dy // Delta Y
local real dv // XY Plane variable
local real face // This Meteor Facing
local real slope // This Meteor Fall Line Slope
local real const // This Meteor Fall Line Constant
local real ratio // This Meteor Scaling Ratio
local real ntx // Next Target X
local real nty // Next Target Y
local real nface // Next Facing
local real nix // Next Initial X
local real niy // Next Initial Y
local real niz // Next Initial Z
local real rnd_rad // Random Radius
local real rnd_ang // Random Angle
local real bound // Bound Size of Falling Meteor w.r.t Grand Area
local real scale // Next Meteor Scaling
local real nratio // Next Meteor Scaling Ratio
local real act_rad // Accurate Radius
local real act_dmg // Accurate Damage
local rect effectrect
loop
exitwhen i >= index
set this = temp_dat[i]
set this.ibem = this.ibem - 1
if this.ibem <= 0 then
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
set this.cfm = this.cfm + 1
if this.cfm <= this.meteors then
// launch another meteor
set bound = this.grandarea - this.maxarea
loop
set rnd_rad = GetRandomReal(0.0,bound)
//set rnd_rad = bound
set rnd_ang = GetRandomReal(0.0,2.*bj_PI)
set ntx = this.TX + rnd_rad*Cos(rnd_ang)
set nty = this.TY + rnd_rad*Sin(rnd_ang)
exitwhen ntx > Utils_MIN_X and ntx < Utils_MAX_X and nty > Utils_MIN_Y and nty < Utils_MAX_Y
endloop
loop
set nface = GetRandomReal(0.0,2.*bj_PI)
set nix = ntx - INIT_DIST * Cos(nface)
set niy = nty - INIT_DIST * Sin(nface)
exitwhen nix > Utils_MIN_X and nix < Utils_MAX_X and niy > Utils_MIN_Y and niy < Utils_MAX_Y
endloop
set niz = GetRandomReal(H_MIN,H_MAX)
set nu = CreateUnit(this.owner,M_DUMMY_ID,nix,niy,nface*bj_RADTODEG)
call SetUnitPathing(nu,false)
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
//set scale = SCALE_MAX
set nratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(nu,scale,scale,scale)
call SetUnitFlyHeight(nu,niz,0.0)
call SaveUnitHandle(mht,this,this.cfm,nu)
call SaveBoolean(mht,this,this.cfm,false)
call SaveReal(mht, this, this.cfm,ntx)
call SaveReal(mht, this,-this.cfm,nty)
call SaveReal(mht,-this, this.cfm,nix)
call SaveReal(mht,-this,-this.cfm,niy)
call SaveReal(mht, this + HT_OFFSET, this.cfm,nface)
call SaveReal(mht, this + HT_OFFSET,-this.cfm,nratio)
call SaveReal(mht,-this - HT_OFFSET, this.cfm,-niz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-this.cfm,niz)
endif
endif
set j = 1
loop
exitwhen j > this.cfm
set u = LoadUnitHandle(mht,this,j)
if not(LoadBoolean(mht,this,j)) then
set tx = LoadReal(mht, this, j)
set ty = LoadReal(mht, this,-j)
set ix = LoadReal(mht,-this, j)
set iy = LoadReal(mht,-this,-j)
set x = GetUnitX(u)
set y = GetUnitY(u)
set dx = tx - x
set dy = ty - y
set ratio = LoadReal(mht,this + HT_OFFSET,-j)
set act_rad = ratio * (this.maxarea - this.minarea ) + this.minarea
set act_dmg = ratio * (this.maxdamage - this.mindamage) + this.mindamage
if SquareRoot( dx*dx + dy*dy ) < CHECK_DIST or GetUnitFlyHeight(u) < CHECK_DIST then
call KillUnit(u)
call SaveBoolean(mht,this,j,true)
// Shake camera (to be made)
// Do Damage stuff here
call meteorDamageUnits(this.owner,this.caster,tx,ty,act_rad,act_dmg)
static if DESTRUCTABLES then
set CASTER = this.caster
set DAMAGE = act_dmg/DESTRUCTABLE_FACTOR
set effectrect = Rect(tx - act_rad, ty - act_rad, tx + act_rad, ty + act_rad)
call EnumDestructablesInRect(effectrect,null,function meteorDamageDestructables)
endif
if IsUnitType(LoadUnitHandle(mht,this,this.meteors),UNIT_TYPE_DEAD) and/*
*/ LoadUnitHandle(mht,this,this.meteors) != null then
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
static if SHOW_INDICATOR then
//call KillUnit(this.targetindicator)
call RemoveUnit(this.targetindicator)
set this.targetindicator = null
endif
call FlushChildHashtable(mht, this)
call FlushChildHashtable(mht,-this)
call FlushChildHashtable(mht, this + HT_OFFSET)
call FlushChildHashtable(mht,-this - HT_OFFSET)
if index == 0 then
call PauseTimer(tim)
endif
call this.deallocate()
endif
else // Move the meteor
set face = LoadReal(mht,this + HT_OFFSET,j)
set slope = LoadReal(mht,-this - HT_OFFSET,j)
set const = LoadReal(mht,-this - HT_OFFSET,-j)
set x = x + STEP * Cos(face)
set y = y + STEP * Sin(face)
set dx = x - ix
set dy = y - iy
set dv = SquareRoot( dx*dx + dy*dy )
set z = slope * dv + const
call SetUnitX(u,x)
call SetUnitY(u,y)
call SetUnitFlyHeight(u,z,0.0)
endif
endif
set j = j + 1
endloop
set i = i + 1
endloop
set u = null
set nu = null
endmethod
static method meteorCall takes unit caster, real tx, real ty returns nothing
local thistype this = thistype.allocate()
local integer al = GetUnitAbilityLevel(caster,ABILITY_ID)
local unit u // Dummy Unit
local real cx // Cast Point X
local real cy // Cast Point Y
local real ix // First Meteor Initial X
local real iy // First Meteor Initial Y
local real iz // First Meteor Initial Z
local real dx
local real dy
local real face
local real scale
local real ratio
local real temp = 9. + I2R(al)
set temp_dat[index] = this
set this.caster = caster
set this.owner = GetOwningPlayer(caster)
set this.TX = tx
set this.TY = ty
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set dx = tx - cx
set dy = ty - cy
set face = Atan2(dy,dx)
set ix = tx - INIT_DIST * Cos(face)
set iy = ty - INIT_DIST * Sin(face)
set iz = GetRandomReal(H_MIN,H_MAX)
static if SHOW_INDICATOR then
set this.targetindicator = CreateUnit(this.owner,B_DUMMY_ID,tx,ty,0.0)
call SetUnitVertexColor(this.targetindicator, 255,255,255,100)
call SetUnitTimeScale(this.targetindicator, 0.0)
call SetUnitScale(this.targetindicator,temp,temp,temp)
call SetUnitPathing(this.targetindicator,false)
call SetUnitX(this.targetindicator,tx)
call SetUnitY(this.targetindicator,ty)
endif
set u = CreateUnit(this.owner,M_DUMMY_ID,ix,iy,face*bj_RADTODEG)
call SetUnitPathing(u,false)
static if INIT_MAX_D then
set scale = SCALE_MAX
else
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
endif
set ratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(u,scale,scale,scale)
call SetUnitFlyHeight(u,iz,0.0)
set this.cfm = 1
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
call SaveUnitHandle(mht,this,1,u)
call SaveBoolean(mht,this,1,false)
call SaveReal(mht, this, 1,tx)
call SaveReal(mht, this,-1,ty)
call SaveReal(mht,-this, 1,ix)
call SaveReal(mht,-this,-1,iy)
call SaveReal(mht, this + HT_OFFSET, 1,face)
call SaveReal(mht, this + HT_OFFSET,-1,ratio)
call SaveReal(mht,-this - HT_OFFSET, 1,-iz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-1,iz)
set this.meteors = MeteorsNumber(al)
set this.grandarea = AreaGrand(al)
set this.maxarea = MaxAreaPerMeteor(al)
set this.minarea = MinAreaPerMeteor(al)
set this.maxdamage = DamageMax(al)
set this.mindamage = DamageMin(al)
set u = null
if index == 0 then
call TimerStart(tim,SPEED,true, function thistype.meteorFall )
endif
set index = index + 1
endmethod
endstruct
private function MeteorSwarm_Conditions takes nothing returns boolean
if GetSpellAbilityId() == ABILITY_ID then
call Meteor.meteorCall(GetTriggerUnit(),GetSpellTargetXBounded(),GetSpellTargetYBounded())
endif
return false
endfunction
private function Init_MeteorSwarm takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, function MeteorSwarm_Conditions )
call RemoveUnit(CreateUnit(Player(12),M_DUMMY_ID,0.,0.,0.))
call Spell_Data_Table()
set t = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Meteor Swarm Version 1.12
library Utils initializer SetBoundary
globals
public real MIN_X
public real MIN_Y
public real MAX_X
public real MAX_Y
private constant real SAFETY_OFFSET = 32.00
endglobals
private function SetBoundary takes nothing returns nothing
set MIN_X = GetRectMinX(bj_mapInitialPlayableArea)
set MIN_Y = GetRectMinY(bj_mapInitialPlayableArea)
set MAX_X = GetRectMaxX(bj_mapInitialPlayableArea)
set MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea)
endfunction
function GetSpellTargetXBounded takes nothing returns real
local real x = GetSpellTargetX()
if x < MIN_X then
return MIN_X + SAFETY_OFFSET
elseif x > MAX_X then
return MAX_X - SAFETY_OFFSET
else
return x
endif
endfunction
function GetSpellTargetYBounded takes nothing returns real
local real y = GetSpellTargetY()
if y < MIN_Y then
return MIN_Y + SAFETY_OFFSET
elseif y > MAX_Y then
return MAX_Y - SAFETY_OFFSET
else
return y
endif
endfunction
endlibrary
scope MS initializer Init_MeteorSwarm
globals
private constant integer M_DUMMY_ID = 'e000'
private constant integer B_DUMMY_ID = 'b000'
private constant integer ABILITY_ID = 'A000'
private constant timer tim = CreateTimer()
private constant timer shake_timer = CreateTimer()
private integer array temp_dat
private integer index = 0
private constant integer INT_MIN = 10 // Minimum Interval Between Meteors Threshold
private constant integer INT_MAX = 100 // Maximum Interval Between Meteors Threshold
private constant real SCALE_MIN = 0.80 // Minimum Meteor Scale Threshold
private constant real SCALE_MAX = 3.50 // Maximum Meteor Scale Threshold
private constant real H_MIN = 750.00 // Minimum Meteor Height Threshold
private constant real H_MAX = 1000.00 // Maximum Meteor Height Threshold
private constant real INIT_DIST = 1000.00
private constant real SPEED = 0.01
private constant real STEP = 16.00
private constant real CHECK_DIST = 32.00
private boolean START_SHAKING = false
private integer TEMP_THIS
private constant integer HT_OFFSET = 4096
private hashtable mht = InitHashtable() // Meteor Hashtable
//private constant group damage_group = CreateGroup()
private unit CASTER
private real DAMAGE
//====================================================================================================
// User-edit data //
//====================================================================================================
// Make sure to change the dummy spell text to be appropriate with the following variables.
// General Formula: ConstantFactor + AbilityLevel * AbilityLevelFactor + PrevLevelFactor * PrevLevelValue
// The following variable can be changed according to the desired results
// Number of Meteors Variables:
private constant boolean NUM_TABLE = false // true/false to use Table/Formula
// Table:
private constant integer array nMeteors // set the values in the function named "Spell_Data_Table"
// Formula:
private constant integer NUM_CF = 25 // changes 'ConstantFactor'
private constant integer NUM_LF = 5 // changes 'AbilityLevelFactor'
private constant real NUM_PLF = 0 // changes 'PrevLevelFactor'
// Minimum Damage Variables:
private constant boolean D_MIN_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MinDamage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MIN_CF = 75.00 // changes 'ConstantFactor'
private constant real D_MIN_LF = 25.00 // changes 'AbilityLevelFactor'
private constant real D_MIN_PLF = 0 // changes 'PrevLevelFactor'
// Maximum Damage Variables:
private constant boolean D_MAX_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MaxDamage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MAX_CF = 250.00 // changes 'ConstantFactor'
private constant real D_MAX_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real D_MAX_PLF = 0 // changes 'PrevLevelFactor'
// Area of Effect (AoE) Variables:
// Grand Area of Effect:
private constant boolean GR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array GrandArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real GR_CF = 750.00 // changes 'ConstantFactor'
private constant real GR_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real GR_PLF = 0 // changes 'PrevLevelFactor'
// Maximum AOE per Meteor:
private constant boolean MAXR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array LargeArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MAXR_CF = 150.00 // changes 'ConstantFactor'
private constant real MAXR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MAXR_PLF = 0 // changes 'PrevLevelFactor'
// Minimum AOE per Meteor:
private constant boolean MINR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array SmallArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MINR_CF = 100.00 // changes 'ConstantFactor'
private constant real MINR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MINR_PLF = 0 // changes 'PrevLevelFactor'
// Targets:
private constant boolean FRIENDLY = true // true/false to harm enemy/all units
private constant boolean GROUND = true // true/false to include/exclude ground units
private constant boolean FLYING = false // true/false to include/exclude flying units
private constant boolean STRUCTURE = true // true/false to include/exclude structure units
private constant boolean DESTRUCTABLES = true // true/false to enable/disable destructables
private constant real DESTRUCTABLE_FACTOR = 10.00 // this is the damage dealt to destructables divisor
// Flags:
private constant boolean INIT_MAX_D = true // true/false to enable/disable maximum damage on first meteor
private constant boolean KILLS_EXPLODE = true // true/false to enable/disable unit explosion if killed by a meteor
private constant boolean SHOW_INDICATOR = true // true/false to enable/disable cast area of effect indicator
private constant boolean SHAKE_CAMERA = true // true/false to enable/disable camera shaking
endglobals
// Just IGNORE this function (Table) IF you are using a formula (Just fold it)
private function Spell_Data_Table takes nothing returns nothing
// Number of Meteors Table
set nMeteors[1] = 30
set nMeteors[2] = 35
set nMeteors[3] = 40
set nMeteors[4] = 45
set nMeteors[5] = 50
// Minimum Damage Table
set MinDamage[1] = 100.00
set MinDamage[2] = 125.00
set MinDamage[3] = 150.00
set MinDamage[4] = 175.00
set MinDamage[5] = 200.00
// Maximum Damage Table
set MaxDamage[1] = 300.00
set MaxDamage[2] = 350.00
set MaxDamage[3] = 400.00
set MaxDamage[4] = 450.00
set MaxDamage[5] = 500.00
// Grand Area of Effect
set GrandArea[1] = 800.00
set GrandArea[2] = 850.00
set GrandArea[3] = 900.00
set GrandArea[4] = 950.00
set GrandArea[5] = 1000.00
// Maximum Area of Effect per Meteor
set LargeArea[1] = 160.00
set LargeArea[2] = 170.00
set LargeArea[3] = 180.00
set LargeArea[4] = 190.00
set LargeArea[5] = 200.00
// Minimum Area of Effect per Meteor
set SmallArea[1] = 110.00
set SmallArea[2] = 120.00
set SmallArea[3] = 130.00
set SmallArea[4] = 140.00
set SmallArea[5] = 150.00
//====================================================================================================
// End of User-edit data //
//====================================================================================================
endfunction
private function MeteorsNumber takes integer al returns integer
local integer prevNMet
static if NUM_TABLE then
return nMeteors[al]
else
if al == 1 then
set prevNMet = 0
else
set prevNMet = MeteorsNumber(al - 1)
endif
return NUM_CF + NUM_LF * al + R2I(NUM_PLF * I2R(prevNMet))
endif
endfunction
private function DamageMin takes integer al returns real
local real prevMinDmg
static if D_MIN_TABLE then
return MinDamage[al]
else
if al == 1 then
set prevMinDmg = 0
else
set prevMinDmg = DamageMin(al - 1)
endif
return D_MIN_CF + D_MIN_LF * al + D_MIN_PLF * prevMinDmg
endif
endfunction
private function DamageMax takes integer al returns real
local real prevMaxDmg
static if D_MAX_TABLE then
return MaxDamage[al]
else
if al == 1 then
set prevMaxDmg = 0
else
set prevMaxDmg = DamageMax(al - 1)
endif
return D_MAX_CF + D_MAX_LF * al + D_MAX_PLF * prevMaxDmg
endif
endfunction
private function AreaGrand takes integer al returns real
local real prevGrArea
static if GR_TABLE then
return GrandArea[al]
else
if al == 1 then
set prevGrArea = 0
else
set prevGrArea = AreaGrand(al - 1)
endif
return GR_CF + GR_LF * al + GR_PLF * prevGrArea
endif
endfunction
private function MinAreaPerMeteor takes integer al returns real
local real prevMinA
static if MINR_TABLE then
return SmallArea[al]
else
if al == 1 then
set prevMinA = 0
else
set prevMinA = MinAreaPerMeteor(al - 1)
endif
return MINR_CF + MINR_LF * al + MINR_PLF * prevMinA
endif
endfunction
private function MaxAreaPerMeteor takes integer al returns real
local real prevMaxA
static if MAXR_TABLE then
return LargeArea[al]
else
if al == 1 then
set prevMaxA = 0
else
set prevMaxA = MaxAreaPerMeteor(al - 1)
endif
return MAXR_CF + MAXR_LF * al + MAXR_PLF * prevMaxA
endif
endfunction
private function MatchingUnit takes unit u,player owner returns boolean
return not( IsUnitType(u,UNIT_TYPE_DEAD) or/*
*/ IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or/*
*/( FRIENDLY and IsUnitAlly(u,owner)) or/*
*/(not FLYING and IsUnitType(u,UNIT_TYPE_FLYING)) or/*
*/(not GROUND and IsUnitType(u,UNIT_TYPE_GROUND)) or/*
*/(not STRUCTURE and IsUnitType(u,UNIT_TYPE_STRUCTURE)))
endfunction
private function meteorDamageUnits takes player owner,unit caster,real X, real Y,real rad,real damage returns nothing
local unit target
call GroupEnumUnitsInRange(bj_lastCreatedGroup,X,Y,rad,null)
loop
set target = FirstOfGroup(bj_lastCreatedGroup)
exitwhen target == null
if MatchingUnit(target,owner) then
static if KILLS_EXPLODE then
call SetUnitExploded(target,true)
endif
call UnitDamageTarget(caster,target,damage,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
static if KILLS_EXPLODE then
call SetUnitExploded(target,false)
endif
endif
call GroupRemoveUnit(bj_lastCreatedGroup,target)
endloop
endfunction
/* this function/filter is not needed for now and is turned off
private function matchingDestructable takes nothing returns boolean
return GetDestructableTypeId(GetFilterDestructable()) == WHO_KNOWS_WHAT_TYPE
endfunction*/
private function meteorDamageDestructables takes nothing returns nothing
call UnitDamageTarget(CASTER,GetEnumDestructable(),DAMAGE,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
endfunction
private struct Meteor extends array
player owner
unit caster
unit targetindicator
unit meteor
real TX
real TY
real slope
real const
real face
real grandarea
real minarea
real maxarea
real mindamage
real maxdamage
integer meteors
integer ibem // Interval Between Each Meteor
integer cfm // Current Falling Meteors
implement Alloc
static method cameraShake takes nothing returns nothing
local thistype this = TEMP_THIS
local real dx = this.TX - GetCameraTargetPositionX()
local real dy = this.TY - GetCameraTargetPositionY()
// A function for Getting nearest active spell for each player must be added
local real x = 0.001 * SquareRoot( dx*dx + dy*dy )
local real magnitude = 20.0 * Pow(bj_E,- x) // magnitude = 20*e^(-x)
if START_SHAKING then
call CameraSetSourceNoiseEx(magnitude,Pow(10,3.),false)
call CameraSetTargetNoiseEx(magnitude,Pow(10,3.),false)
if index == 0 then
set START_SHAKING = false
call PauseTimer(shake_timer)
call CameraSetSourceNoise(0, 0)
call CameraSetTargetNoise(0, 0)
endif
endif
endmethod
static method meteorFall takes nothing returns nothing
local thistype this
local integer i = 0 // Instance Counter index
local integer j = 0 // Meteor Counter index
local unit u // This Dummy Unit
local unit nu // Next Dummy Unit
local real x // This Meteor X
local real y // This Meteor Y
local real z // This Meteor Z
local real ix // This Meteor Initial X
local real iy // This Meteor Initial Y
local real tx // This Meteor Target X
local real ty // This Meteor Target Y
local real dx // Delta X
local real dy // Delta Y
local real dv // XY Plane variable
local real face // This Meteor Facing
local real slope // This Meteor Fall Line Slope
local real const // This Meteor Fall Line Constant
local real ratio // This Meteor Scaling Ratio
local real ntx // Next Target X
local real nty // Next Target Y
local real nface // Next Facing
local real nix // Next Initial X
local real niy // Next Initial Y
local real niz // Next Initial Z
local real rnd_rad // Random Radius
local real rnd_ang // Random Angle
local real bound // Bound Size of Falling Meteor w.r.t Grand Area
local real scale // Next Meteor Scaling
local real nratio // Next Meteor Scaling Ratio
local real act_rad // Accurate Radius
local real act_dmg // Accurate Damage
local rect effectrect // effective Rect for destructables
loop
exitwhen i >= index
set this = temp_dat[i]
set this.ibem = this.ibem - 1
if this.ibem <= 0 then
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
set this.cfm = this.cfm + 1
if this.cfm <= this.meteors then
// launch another meteor
set bound = this.grandarea - this.maxarea
loop
set rnd_rad = GetRandomReal(0.0,bound)
//set rnd_rad = bound
set rnd_ang = GetRandomReal(0.0,2.*bj_PI)
set ntx = this.TX + rnd_rad*Cos(rnd_ang)
set nty = this.TY + rnd_rad*Sin(rnd_ang)
exitwhen ntx > Utils_MIN_X and ntx < Utils_MAX_X and nty > Utils_MIN_Y and nty < Utils_MAX_Y
endloop
loop
set nface = GetRandomReal(0.0,2.*bj_PI)
set nix = ntx - INIT_DIST * Cos(nface)
set niy = nty - INIT_DIST * Sin(nface)
exitwhen nix > Utils_MIN_X and nix < Utils_MAX_X and niy > Utils_MIN_Y and niy < Utils_MAX_Y
endloop
set niz = GetRandomReal(H_MIN,H_MAX)
set nu = CreateUnit(this.owner,M_DUMMY_ID,nix,niy,nface*bj_RADTODEG)
call SetUnitPathing(nu,false)
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
//set scale = SCALE_MAX
set nratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(nu,scale,scale,scale)
call SetUnitFlyHeight(nu,niz,0.0)
call SaveUnitHandle(mht,this,this.cfm,nu)
call SaveBoolean(mht,this,this.cfm,false)
call SaveReal(mht, this, this.cfm,ntx)
call SaveReal(mht, this,-this.cfm,nty)
call SaveReal(mht,-this, this.cfm,nix)
call SaveReal(mht,-this,-this.cfm,niy)
call SaveReal(mht, this + HT_OFFSET, this.cfm,nface)
call SaveReal(mht, this + HT_OFFSET,-this.cfm,nratio)
call SaveReal(mht,-this - HT_OFFSET, this.cfm,-niz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-this.cfm,niz)
endif
endif
set j = 1
loop
exitwhen j > this.cfm
set u = LoadUnitHandle(mht,this,j)
if not(LoadBoolean(mht,this,j)) then
set tx = LoadReal(mht, this, j)
set ty = LoadReal(mht, this,-j)
set ix = LoadReal(mht,-this, j)
set iy = LoadReal(mht,-this,-j)
set x = GetUnitX(u)
set y = GetUnitY(u)
set dx = tx - x
set dy = ty - y
set ratio = LoadReal(mht,this + HT_OFFSET,-j)
set act_rad = ratio * (this.maxarea - this.minarea ) + this.minarea
set act_dmg = ratio * (this.maxdamage - this.mindamage) + this.mindamage
if SquareRoot( dx*dx + dy*dy ) < CHECK_DIST or GetUnitFlyHeight(u) < CHECK_DIST then
call KillUnit(u)
call SaveBoolean(mht,this,j,true)
// Shake camera
static if SHAKE_CAMERA then
set TEMP_THIS = this
set START_SHAKING = true
endif
// Do Damage stuff
call meteorDamageUnits(this.owner,this.caster,tx,ty,act_rad,act_dmg)
static if DESTRUCTABLES then
set CASTER = this.caster
set DAMAGE = act_dmg/DESTRUCTABLE_FACTOR
set effectrect = Rect(tx - act_rad, ty - act_rad, tx + act_rad, ty + act_rad)
call EnumDestructablesInRect(effectrect,null,function meteorDamageDestructables)
endif
if IsUnitType(LoadUnitHandle(mht,this,this.meteors),UNIT_TYPE_DEAD) and/*
*/ LoadUnitHandle(mht,this,this.meteors) != null then
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
static if SHOW_INDICATOR then
//call KillUnit(this.targetindicator)
call RemoveUnit(this.targetindicator)
set this.targetindicator = null
endif
call FlushChildHashtable(mht, this)
call FlushChildHashtable(mht,-this)
call FlushChildHashtable(mht, this + HT_OFFSET)
call FlushChildHashtable(mht,-this - HT_OFFSET)
if index == 0 then
call PauseTimer(tim)
endif
call this.deallocate()
endif
else // Move the meteor
set face = LoadReal(mht,this + HT_OFFSET,j)
set slope = LoadReal(mht,-this - HT_OFFSET,j)
set const = LoadReal(mht,-this - HT_OFFSET,-j)
set x = x + STEP * Cos(face)
set y = y + STEP * Sin(face)
set dx = x - ix
set dy = y - iy
set dv = SquareRoot( dx*dx + dy*dy )
set z = slope * dv + const
call SetUnitX(u,x)
call SetUnitY(u,y)
call SetUnitFlyHeight(u,z,0.0)
endif
endif
set j = j + 1
endloop
set i = i + 1
endloop
set u = null
set nu = null
endmethod
static method meteorCall takes unit caster, real tx, real ty returns nothing
local thistype this = thistype.allocate()
local integer al = GetUnitAbilityLevel(caster,ABILITY_ID)
local unit u // Dummy Unit
local real cx // Cast Point X
local real cy // Cast Point Y
local real ix // First Meteor Initial X
local real iy // First Meteor Initial Y
local real iz // First Meteor Initial Z
local real dx
local real dy
local real face
local real scale
local real ratio
local real temp = 9. + I2R(al)
set temp_dat[index] = this
set this.caster = caster
set this.owner = GetOwningPlayer(caster)
set this.TX = tx
set this.TY = ty
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set dx = tx - cx
set dy = ty - cy
set face = Atan2(dy,dx)
set ix = tx - INIT_DIST * Cos(face)
set iy = ty - INIT_DIST * Sin(face)
set iz = GetRandomReal(H_MIN,H_MAX)
static if SHOW_INDICATOR then
set this.targetindicator = CreateUnit(this.owner,B_DUMMY_ID,tx,ty,0.0)
call SetUnitVertexColor(this.targetindicator, 255,255,255,100)
call SetUnitTimeScale(this.targetindicator,0.0)
call SetUnitScale(this.targetindicator,temp,temp,temp)
call SetUnitPathing(this.targetindicator,false)
call SetUnitX(this.targetindicator,tx)
call SetUnitY(this.targetindicator,ty)
endif
set u = CreateUnit(this.owner,M_DUMMY_ID,ix,iy,face*bj_RADTODEG)
call SetUnitPathing(u,false)
static if INIT_MAX_D then
set scale = SCALE_MAX
else
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
endif
set ratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(u,scale,scale,scale)
call SetUnitFlyHeight(u,iz,0.0)
set this.cfm = 1
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
call SaveUnitHandle(mht,this,1,u)
call SaveBoolean(mht,this,1,false)
call SaveReal(mht, this, 1,tx)
call SaveReal(mht, this,-1,ty)
call SaveReal(mht,-this, 1,ix)
call SaveReal(mht,-this,-1,iy)
call SaveReal(mht, this + HT_OFFSET, 1,face)
call SaveReal(mht, this + HT_OFFSET,-1,ratio)
call SaveReal(mht,-this - HT_OFFSET, 1,-iz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-1,iz)
set this.meteors = MeteorsNumber(al)
set this.grandarea = AreaGrand(al)
set this.maxarea = MaxAreaPerMeteor(al)
set this.minarea = MinAreaPerMeteor(al)
set this.maxdamage = DamageMax(al)
set this.mindamage = DamageMin(al)
set u = null
if index == 0 then
call TimerStart(tim,SPEED,true, function thistype.meteorFall )
static if SHAKE_CAMERA then
call TimerStart(shake_timer,0.01,true,function thistype.cameraShake)
endif
endif
set index = index + 1
endmethod
endstruct
private function MeteorSwarm_Conditions takes nothing returns boolean
if GetSpellAbilityId() == ABILITY_ID then
call Meteor.meteorCall(GetTriggerUnit(),GetSpellTargetXBounded(),GetSpellTargetYBounded())
endif
return false
endfunction
private function Init_MeteorSwarm takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, function MeteorSwarm_Conditions )
call RemoveUnit(CreateUnit(Player(12),M_DUMMY_ID,0.,0.,0.))
call Spell_Data_Table()
set t = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Meteor Swarm Version 1.13
// this was never published but there are changes after 1.12 so I included it
scope MS initializer Init_MeteorSwarm
globals
private real MIN_X
private real MIN_Y
private real MAX_X
private real MAX_Y
private constant real SAFETY_OFFSET = 16.00
private constant integer M_DUMMY_ID = 'e000'
private constant integer I_DUMMY_ID = 'b000'
private constant integer ABILITY_ID = 'A000'
private constant timer tim = CreateTimer()
private constant timer shake_timer = CreateTimer()
private integer array temp_dat
private integer index = 0
private constant integer INT_MIN = 10 // Minimum Interval Between Meteors Threshold
private constant integer INT_MAX = 100 // Maximum Interval Between Meteors Threshold
private constant real SCALE_MIN = 0.80 // Minimum Meteor Scale Threshold
private constant real SCALE_MAX = 3.50 // Maximum Meteor Scale Threshold
private constant real H_MIN = 750.00 // Minimum Meteor Height Threshold
private constant real H_MAX = 1000.00 // Maximum Meteor Height Threshold
private constant real INIT_DIST = 1000.00
private constant real SPEED = 0.01
private constant real STEP = 16.00
private constant real CHECK_DIST = 32.00
private boolean START_SHAKING = false
private constant integer HT_OFFSET = 4096
private hashtable mht = InitHashtable() // Meteor Hashtable
//private constant group damage_group = CreateGroup()
private unit CASTER
private real DAMAGE
//====================================================================================================
// User-edit data //
//====================================================================================================
// Make sure to change the dummy spell text to be appropriate with the following variables.
// General Formula: ConstantFactor + AbilityLevel * AbilityLevelFactor + PrevLevelFactor * PrevLevelValue
// The following variable can be changed according to the desired results
// Number of Meteors Variables:
private constant boolean NUM_TABLE = false // true/false to use Table/Formula
// Table:
private constant integer array nMeteors // set the values in the function named "Spell_Data_Table"
// Formula:
private constant integer NUM_CF = 25 // changes 'ConstantFactor'
private constant integer NUM_LF = 5 // changes 'AbilityLevelFactor'
private constant real NUM_PLF = 0 // changes 'PrevLevelFactor'
// Minimum Damage Variables:
private constant boolean D_MIN_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MinDamage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MIN_CF = 75.00 // changes 'ConstantFactor'
private constant real D_MIN_LF = 25.00 // changes 'AbilityLevelFactor'
private constant real D_MIN_PLF = 0 // changes 'PrevLevelFactor'
// Maximum Damage Variables:
private constant boolean D_MAX_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MaxDamage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MAX_CF = 250.00 // changes 'ConstantFactor'
private constant real D_MAX_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real D_MAX_PLF = 0 // changes 'PrevLevelFactor'
// Area of Effect (AoE) Variables:
// Grand Area of Effect:
private constant boolean GR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array GrandArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real GR_CF = 750.00 // changes 'ConstantFactor'
private constant real GR_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real GR_PLF = 0 // changes 'PrevLevelFactor'
// Maximum AOE per Meteor:
private constant boolean MAXR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array LargeArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MAXR_CF = 150.00 // changes 'ConstantFactor'
private constant real MAXR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MAXR_PLF = 0 // changes 'PrevLevelFactor'
// Minimum AOE per Meteor:
private constant boolean MINR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array SmallArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MINR_CF = 100.00 // changes 'ConstantFactor'
private constant real MINR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MINR_PLF = 0 // changes 'PrevLevelFactor'
// Targets:
private constant boolean FRIENDLY = true // true/false to harm enemy/all units
private constant boolean GROUND = true // true/false to include/exclude ground units
private constant boolean FLYING = false // true/false to include/exclude flying units
private constant boolean STRUCTURE = true // true/false to include/exclude structure units
private constant boolean DESTRUCTABLES = true // true/false to enable/disable destructables
private constant real DESTRUCTABLE_FACTOR = 10.00 // this is the damage dealt to destructables divisor
// Flags:
private constant boolean INIT_MAX_D = true // true/false to enable/disable maximum damage on first meteor
private constant boolean KILLS_EXPLODE = true // true/false to enable/disable unit explosion if killed by a meteor
private constant boolean SHOW_INDICATOR = true // true/false to enable/disable cast area of effect indicator
private constant boolean ALLOW_CAMERA_SHAKE = true // true/false to enable/disable camera shaking
endglobals
// Just IGNORE this function (Table) IF you are using a formula (Just fold it)
private function Spell_Data_Table takes nothing returns nothing
// Number of Meteors Table
set nMeteors[1] = 30
set nMeteors[2] = 35
set nMeteors[3] = 40
set nMeteors[4] = 45
set nMeteors[5] = 50
// Minimum Damage Table
set MinDamage[1] = 100.00
set MinDamage[2] = 125.00
set MinDamage[3] = 150.00
set MinDamage[4] = 175.00
set MinDamage[5] = 200.00
// Maximum Damage Table
set MaxDamage[1] = 300.00
set MaxDamage[2] = 350.00
set MaxDamage[3] = 400.00
set MaxDamage[4] = 450.00
set MaxDamage[5] = 500.00
// Grand Area of Effect
set GrandArea[1] = 800.00
set GrandArea[2] = 850.00
set GrandArea[3] = 900.00
set GrandArea[4] = 950.00
set GrandArea[5] = 1000.00
// Maximum Area of Effect per Meteor
set LargeArea[1] = 160.00
set LargeArea[2] = 170.00
set LargeArea[3] = 180.00
set LargeArea[4] = 190.00
set LargeArea[5] = 200.00
// Minimum Area of Effect per Meteor
set SmallArea[1] = 110.00
set SmallArea[2] = 120.00
set SmallArea[3] = 130.00
set SmallArea[4] = 140.00
set SmallArea[5] = 150.00
//====================================================================================================
// End of User-edit data //
//====================================================================================================
endfunction
private function MeteorsNumber takes integer al returns integer
local integer prevNMet
static if NUM_TABLE then
return nMeteors[al]
else
if al == 1 then
set prevNMet = 0
else
set prevNMet = MeteorsNumber(al - 1)
endif
return NUM_CF + NUM_LF * al + R2I(NUM_PLF * I2R(prevNMet))
endif
endfunction
private function DamageMin takes integer al returns real
local real prevMinDmg
static if D_MIN_TABLE then
return MinDamage[al]
else
if al == 1 then
set prevMinDmg = 0.
else
set prevMinDmg = DamageMin(al - 1)
endif
return D_MIN_CF + D_MIN_LF * al + D_MIN_PLF * prevMinDmg
endif
endfunction
private function DamageMax takes integer al returns real
local real prevMaxDmg
static if D_MAX_TABLE then
return MaxDamage[al]
else
if al == 1 then
set prevMaxDmg = 0.
else
set prevMaxDmg = DamageMax(al - 1)
endif
return D_MAX_CF + D_MAX_LF * al + D_MAX_PLF * prevMaxDmg
endif
endfunction
private function AreaGrand takes integer al returns real
local real prevGrArea
static if GR_TABLE then
return GrandArea[al]
else
if al == 1 then
set prevGrArea = 0.
else
set prevGrArea = AreaGrand(al - 1)
endif
return GR_CF + GR_LF * al + GR_PLF * prevGrArea
endif
endfunction
private function MinAreaPerMeteor takes integer al returns real
local real prevMinA
static if MINR_TABLE then
return SmallArea[al]
else
if al == 1 then
set prevMinA = 0.
else
set prevMinA = MinAreaPerMeteor(al - 1)
endif
return MINR_CF + MINR_LF * al + MINR_PLF * prevMinA
endif
endfunction
private function MaxAreaPerMeteor takes integer al returns real
local real prevMaxA
static if MAXR_TABLE then
return LargeArea[al]
else
if al == 1 then
set prevMaxA = 0.
else
set prevMaxA = MaxAreaPerMeteor(al - 1)
endif
return MAXR_CF + MAXR_LF * al + MAXR_PLF * prevMaxA
endif
endfunction
private function GetSpellTargetXBounded takes nothing returns real
local real x = GetSpellTargetX()
if x < MIN_X then
return MIN_X + SAFETY_OFFSET
elseif x > MAX_X then
return MAX_X - SAFETY_OFFSET
else
return x
endif
endfunction
private function GetSpellTargetYBounded takes nothing returns real
local real y = GetSpellTargetY()
if y < MIN_Y then
return MIN_Y + SAFETY_OFFSET
elseif y > MAX_Y then
return MAX_Y - SAFETY_OFFSET
else
return y
endif
endfunction
private function MatchingUnit takes unit u,player owner returns boolean
return not( IsUnitType(u,UNIT_TYPE_DEAD) or/*
*/ IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or/*
*/( FRIENDLY and IsUnitAlly(u,owner)) or/*
*/(not FLYING and IsUnitType(u,UNIT_TYPE_FLYING)) or/*
*/(not GROUND and IsUnitType(u,UNIT_TYPE_GROUND)) or/*
*/(not STRUCTURE and IsUnitType(u,UNIT_TYPE_STRUCTURE)))
endfunction
private function meteorDamageUnits takes player owner,unit caster,real X, real Y,real rad,real damage returns nothing
local unit target
call GroupEnumUnitsInRange(bj_lastCreatedGroup,X,Y,rad,null)
loop
set target = FirstOfGroup(bj_lastCreatedGroup)
exitwhen target == null
if MatchingUnit(target,owner) then
static if KILLS_EXPLODE then
call SetUnitExploded(target,true)
endif
call UnitDamageTarget(caster,target,damage,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
static if KILLS_EXPLODE then
call SetUnitExploded(target,false)
endif
endif
call GroupRemoveUnit(bj_lastCreatedGroup,target)
endloop
endfunction
/* this function/filter is not needed for now and is turned off
private function matchingDestructable takes nothing returns boolean
return GetDestructableTypeId(GetFilterDestructable()) == WHO_KNOWS_WHAT_TYPE
endfunction*/
private function meteorDamageDestructables takes nothing returns nothing
call UnitDamageTarget(CASTER,GetEnumDestructable(),DAMAGE,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
endfunction
private struct Meteor extends array
player owner
unit caster
unit targetindicator
unit meteor
real TX
real TY
real slope
real const
real face
real grandarea
real minarea
real maxarea
real mindamage
real maxdamage
integer meteors
integer ibem // Interval Between Each Meteor
integer cfm // Current Falling Meteors
implement Alloc
static method cameraShake takes nothing returns nothing
local integer i = 0
local thistype this = temp_dat[i]
local real camX = GetCameraTargetPositionX()
local real camY = GetCameraTargetPositionY()
local real dx = this.TX - camX
local real dy = this.TY - camY
local real x1 = SquareRoot( dx*dx + dy*dy )
local real x2
local real magnitude
if START_SHAKING then
loop
exitwhen i >= index
set this = temp_dat[i]
set dx = this.TX - camX
set dy = this.TY - camY
set x2 = SquareRoot( dx*dx + dy*dy )
if x1 > x2 then
set x1 = x2
endif
set i = i + 1
endloop
set magnitude = 20.0 * Pow(bj_E,- 0.001 * x1) // magnitude = 20*e^(-x)
call CameraSetSourceNoiseEx(magnitude,1000.,false)
call CameraSetTargetNoiseEx(magnitude,1000.,false)
if index == 0 then
set START_SHAKING = false
call PauseTimer(shake_timer)
call CameraSetSourceNoise(0, 0)
call CameraSetTargetNoise(0, 0)
endif
endif
endmethod
static method meteorFall takes nothing returns nothing
local thistype this
local integer i = 0 // Instance Counter index
local integer j = 0 // Meteor Counter index
local unit u // This Dummy Unit
local unit nu // Next Dummy Unit
local real x // This Meteor X
local real y // This Meteor Y
local real z // This Meteor Z
local real ix // This Meteor Initial X
local real iy // This Meteor Initial Y
local real tx // This Meteor Target X
local real ty // This Meteor Target Y
local real dx // Delta X
local real dy // Delta Y
local real dv // XY Plane variable
local real face // This Meteor Facing
local real slope // This Meteor Fall Line Slope
local real const // This Meteor Fall Line Constant
local real ratio // This Meteor Scaling Ratio
local real ntx // Next Target X
local real nty // Next Target Y
local real nface // Next Facing
local real nix // Next Initial X
local real niy // Next Initial Y
local real niz // Next Initial Z
local real rnd_rad // Random Radius
local real rnd_ang // Random Angle
local real bound // Bound Size of Falling Meteor w.r.t Grand Area
local real scale // Next Meteor Scaling
local real nratio // Next Meteor Scaling Ratio
local real accurate_radius // Accurate Radius
local real accurate_damage // Accurate Damage
local rect effectrect // effective Rect for destructables
loop
exitwhen i >= index
set this = temp_dat[i]
set this.ibem = this.ibem - 1
if this.ibem <= 0 then
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
set this.cfm = this.cfm + 1
if this.cfm <= this.meteors then
// launch another meteor
set bound = this.grandarea - this.maxarea
loop
set rnd_rad = GetRandomReal(0.0,bound)
//set rnd_rad = bound
set rnd_ang = GetRandomReal(0.0,2.*bj_PI)
set ntx = this.TX + rnd_rad*Cos(rnd_ang)
set nty = this.TY + rnd_rad*Sin(rnd_ang)
exitwhen ntx > MIN_X and ntx < MAX_X and nty > MIN_Y and nty < MAX_Y
endloop
loop
set nface = GetRandomReal(0.0,2.*bj_PI)
set nix = ntx - INIT_DIST * Cos(nface)
set niy = nty - INIT_DIST * Sin(nface)
exitwhen nix > MIN_X and nix < MAX_X and niy > MIN_Y and niy < MAX_Y
endloop
set niz = GetRandomReal(H_MIN,H_MAX)
set nu = CreateUnit(this.owner,M_DUMMY_ID,nix,niy,nface*bj_RADTODEG)
call SetUnitPathing(nu,false)
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
//set scale = SCALE_MAX
set nratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(nu,scale,scale,scale)
call SetUnitFlyHeight(nu,niz,0.0)
call SaveUnitHandle(mht,this,this.cfm,nu)
call SaveBoolean(mht,this,this.cfm,false)
call SaveReal(mht, this, this.cfm,ntx)
call SaveReal(mht, this,-this.cfm,nty)
call SaveReal(mht,-this, this.cfm,nix)
call SaveReal(mht,-this,-this.cfm,niy)
call SaveReal(mht, this + HT_OFFSET, this.cfm,nface)
call SaveReal(mht, this + HT_OFFSET,-this.cfm,nratio)
call SaveReal(mht,-this - HT_OFFSET, this.cfm,-niz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-this.cfm,niz)
endif
endif
set j = 1
loop
exitwhen j > this.cfm
set u = LoadUnitHandle(mht,this,j)
if not(LoadBoolean(mht,this,j)) then
set tx = LoadReal(mht, this, j)
set ty = LoadReal(mht, this,-j)
set ix = LoadReal(mht,-this, j)
set iy = LoadReal(mht,-this,-j)
set x = GetUnitX(u)
set y = GetUnitY(u)
set dx = tx - x
set dy = ty - y
set ratio = LoadReal(mht,this + HT_OFFSET,-j)
set accurate_radius = ratio * (this.maxarea - this.minarea ) + this.minarea
set accurate_damage = ratio * (this.maxdamage - this.mindamage) + this.mindamage
if SquareRoot( dx*dx + dy*dy ) < CHECK_DIST or GetUnitFlyHeight(u) < CHECK_DIST then
call KillUnit(u)
call SaveBoolean(mht,this,j,true)
// Shake camera
static if ALLOW_CAMERA_SHAKE then
set START_SHAKING = true
endif
// Do Damage stuff
call meteorDamageUnits(this.owner,this.caster,tx,ty,accurate_radius,accurate_damage)
static if DESTRUCTABLES then
set CASTER = this.caster
set DAMAGE = accurate_damage/DESTRUCTABLE_FACTOR
set effectrect = Rect(tx - accurate_radius, ty - accurate_radius, tx + accurate_radius, ty + accurate_radius)
call EnumDestructablesInRect(effectrect,null,function meteorDamageDestructables)
endif
if IsUnitType(LoadUnitHandle(mht,this,this.meteors),UNIT_TYPE_DEAD) and/*
*/ LoadUnitHandle(mht,this,this.meteors) != null then
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
static if SHOW_INDICATOR then
call RemoveUnit(this.targetindicator)
set this.targetindicator = null
endif
call FlushChildHashtable(mht, this)
call FlushChildHashtable(mht,-this)
call FlushChildHashtable(mht, this + HT_OFFSET)
call FlushChildHashtable(mht,-this - HT_OFFSET)
if index == 0 then
call PauseTimer(tim)
endif
call this.deallocate()
endif
else // Move the meteor
set face = LoadReal(mht,this + HT_OFFSET,j)
set slope = LoadReal(mht,-this - HT_OFFSET,j)
set const = LoadReal(mht,-this - HT_OFFSET,-j)
set x = x + STEP * Cos(face)
set y = y + STEP * Sin(face)
set dx = x - ix
set dy = y - iy
set dv = SquareRoot( dx*dx + dy*dy )
set z = slope * dv + const
call SetUnitX(u,x)
call SetUnitY(u,y)
call SetUnitFlyHeight(u,z,0.0)
endif
endif
set j = j + 1
endloop
set i = i + 1
endloop
set u = null
set nu = null
endmethod
static method meteorCall takes unit caster, real tx, real ty returns nothing
local thistype this = thistype.allocate()
local integer al = GetUnitAbilityLevel(caster,ABILITY_ID)
local unit u // Dummy Unit
local real cx // Cast Point X
local real cy // Cast Point Y
local real ix // First Meteor Initial X
local real iy // First Meteor Initial Y
local real iz // First Meteor Initial Z
local real dx
local real dy
local real face
local real scale
local real ratio
local real temp = 9. + I2R(al)
set temp_dat[index] = this
set this.caster = caster
set this.owner = GetOwningPlayer(caster)
set this.TX = tx
set this.TY = ty
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set dx = tx - cx
set dy = ty - cy
set face = Atan2(dy,dx)
set ix = tx - INIT_DIST * Cos(face)
set iy = ty - INIT_DIST * Sin(face)
set iz = GetRandomReal(H_MIN,H_MAX)
static if SHOW_INDICATOR then
set this.targetindicator = CreateUnit(this.owner,I_DUMMY_ID,tx,ty,0.0)
call SetUnitVertexColor(this.targetindicator, 255,255,255,255)
call SetUnitTimeScale(this.targetindicator,0.7)
call SetUnitScale(this.targetindicator,temp,temp,temp)
call SetUnitPathing(this.targetindicator,false)
call SetUnitX(this.targetindicator,tx)
call SetUnitY(this.targetindicator,ty)
endif
set u = CreateUnit(this.owner,M_DUMMY_ID,ix,iy,face*bj_RADTODEG)
call SetUnitPathing(u,false)
static if INIT_MAX_D then
set scale = SCALE_MAX
else
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
endif
set ratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(u,scale,scale,scale)
call SetUnitFlyHeight(u,iz,0.0)
set this.cfm = 1
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
call SaveUnitHandle(mht,this,1,u)
call SaveBoolean(mht,this,1,false)
call SaveReal(mht, this, 1,tx)
call SaveReal(mht, this,-1,ty)
call SaveReal(mht,-this, 1,ix)
call SaveReal(mht,-this,-1,iy)
call SaveReal(mht, this + HT_OFFSET, 1,face)
call SaveReal(mht, this + HT_OFFSET,-1,ratio)
call SaveReal(mht,-this - HT_OFFSET, 1,-iz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-1,iz)
set this.meteors = MeteorsNumber(al)
set this.grandarea = AreaGrand(al)
set this.maxarea = MaxAreaPerMeteor(al)
set this.minarea = MinAreaPerMeteor(al)
set this.maxdamage = DamageMax(al)
set this.mindamage = DamageMin(al)
set u = null
if index == 0 then
call TimerStart(tim,SPEED,true, function thistype.meteorFall )
static if ALLOW_CAMERA_SHAKE then
call TimerStart(shake_timer,0.01,true,function thistype.cameraShake)
endif
endif
set index = index + 1
endmethod
endstruct
private function MeteorSwarm_Conditions takes nothing returns boolean
if GetSpellAbilityId() == ABILITY_ID then
call Meteor.meteorCall(GetTriggerUnit(),GetSpellTargetXBounded(),GetSpellTargetYBounded())
endif
return false
endfunction
private function Init_MeteorSwarm takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, function MeteorSwarm_Conditions )
call RemoveUnit(CreateUnit(Player(12),M_DUMMY_ID,0.,0.,0.))
call RemoveUnit(CreateUnit(Player(12),I_DUMMY_ID,0.,0.,0.))
call Spell_Data_Table()
set MIN_X = GetCameraBoundMinX() - 512.000
set MIN_Y = GetCameraBoundMinY() - 256.000
set MAX_X = GetCameraBoundMaxX() + 512.000
set MAX_Y = GetCameraBoundMaxY() + 256.000
set t = null
endfunction
endscope
//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
"
Its important to copy all the following contents to your map in order for this spell to function properly.
trigger " CTL " NOT OPTIONAL
trigger " BoundSentinel " OPTIONAL
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CTL /* v1.2.0.1
*************************************************************************************
*
* CTL or Constant Timer Loop provides a loop for constant merged timers of timeout .03125
*
* Similar to T32 but pauses timer when no structs have instances and removes structs
* from timer trigger when those structs have no instances.
*
* This can also create new timers after destroying a previous timer and generates less
* code in the module. It also generates no triggers so long as the module is implemented
* at the top of the struct.
*
************************************************************************************
*
* module CTL
*
* Allows creation/destruction of timers in a struct. Provides instancing of those timers.
*
* - static method create takes nothing returns thistype
* - method destroy takes nothing returns nothing
*
* CTL (optional)
* local variables, code before running any timers
* CTLExpire (not optional)
* timer code
* CTLNull (optional)
* null any locals, runs after all timers
* CTLEnd (not optional)
*
* module CT32
*
* Converts struct into a timer group. Allows the timer group to be started and stopped.
* Instancing and looping through active timers is up to the user.
*
* - static method start takes nothing returns nothing
* - static method stop takes nothing returns nothing
*
* CT32 (not optional)
* timer code
* CT32End (not optional)
*
* struct TimerGroup32 extends array
*
* Allows for the creation of timer groups. Timer instancing and looping is entirely up
* to the user.
*
* - static method create takes code func returns thistype
* - method destroy takes nothing returns nothing
* - method start takes nothing returns nothing
* - method stop takes nothing returns nothing
*
************************************************************************************/
globals
private integer tgc = 0 //timer group count
private integer array tgr //timer group recycler
private integer ic=0 //instance count
private integer tc=0 //timer count
private integer array rf //root first
private integer array n //next
private integer array p //previous
private integer array th //timer head
private integer array ns //next stack
private trigger t=CreateTrigger()
private timer m=CreateTimer()
private triggercondition array ct
private conditionfunc array rc
private boolean array e32 //enabled
private integer array i32r //ct32 recycler
private integer i32cr = 0 //ct32 count recycler
private boolean array ir32 //is recycling
private boolean array id32 //is destroying
endglobals
private function E takes nothing returns nothing
local integer i=ns[0]
set ns[0]=0
loop
exitwhen 0==i
if (0==p[i]) then
if (0==n[i]) then
call TriggerRemoveCondition(t,ct[th[i]])
set ct[th[i]]=null
set tc=tc-1
set rf[th[i]]=0
else
set rf[th[i]]=n[i]
set p[n[i]]=0
endif
else
set p[n[i]]=p[i]
set n[p[i]]=n[i]
endif
set n[i]=n[0]
set n[0]=i
set i=ns[i]
endloop
loop
exitwhen 0 == i32cr
set i32cr = i32cr - 1
set i = i32r[i32cr]
if (not e32[i]) then
call TriggerRemoveCondition(t,ct[i])
set ct[i] = null
if (id32[i]) then
set tgr[i] = tgr[0]
set tgr[0] = i
set id32[i] = false
set e32[i] = false
set ir32[i] = false
endif
endif
endloop
if (0==tc) then
call PauseTimer(m)
else
call TriggerEvaluate(t)
endif
endfunction
private function CT takes integer r returns integer
local integer i
local integer f
if (0==n[0]) then
set i=ic+1
set ic=i
else
set i=n[0]
set n[0]=n[i]
endif
set th[i]=r
set ns[i]=-1
set f=rf[r]
if (0==f) then
set n[i]=0
set p[i]=0
set rf[r]=i
set ct[r]=TriggerAddCondition(t,rc[r])
//set ct[r] = null
if (0==tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc=tc+1
else
set n[i]=f
set p[i]=0
set p[f]=i
set rf[r]=i
endif
return i
endfunction
private function DT takes integer t returns nothing
debug if (0>ns[t]) then
set ns[t]=ns[0]
set ns[0]=t
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"TIMER LOOP ERROR: ATTEMPT TO DESTROY NULL TIMER")
debug endif
endfunction
private function A takes code c returns integer
local integer i = tgr[0]
if (0 == i) then
set i = tgc + 1
set tgc = i
else
set tgr[0] = tgr[i]
endif
set rc[i]=Condition(c)
return i
endfunction
private function A32 takes integer i returns nothing
if (not e32[i]) then
if (not ir32[i] and not id32[i]) then
set ct[i] = TriggerAddCondition(t, rc[i])
endif
if (0 == tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc = tc + 1
set e32[i] = true
endif
endfunction
private function SR32 takes integer i returns nothing
if (e32[i]) then
if (not ir32[i] and not id32[i]) then
set i32r[i32cr] = i
set i32cr = i32cr + 1
set ir32[i] = true
endif
set e32[i] = false
set tc = tc - 1
endif
endfunction
private function DT32 takes integer i returns nothing
if (not id32[i]) then
if (not ir32[i]) then
set ir32[i] = true
set tc = tc - 1
set i32r[i32cr] = i
set i32cr = i32cr + 1
set e32[i] = false
endif
set id32[i] = true
endif
endfunction
private keyword r
private keyword e
module CTL
static integer rctl32
static method create takes nothing returns thistype
return CT(rctl32)
endmethod
method destroy takes nothing returns nothing
call DT(this)
endmethod
static method ectl32 takes nothing returns boolean
local thistype this=rf[rctl32]
endmodule
module CTLExpire
implement CTL
loop
exitwhen 0==this
endmodule
module CTLNull
set this=n[this]
endloop
endmodule
module CTLEnd
implement CTLNull
return false
endmethod
private static method onInit takes nothing returns nothing
set rctl32 = A(function thistype.ectl32)
endmethod
endmodule
module CT32
static integer rctl32
static method ectl32 takes nothing returns boolean
endmodule
module CT32End
return false
endmethod
static method start takes nothing returns nothing
call A32(rctl32)
endmethod
static method stop takes nothing returns nothing
call SR32(rctl32)
endmethod
private static method onInit takes nothing returns nothing
set rctl32 = A(function thistype.ectl32)
endmethod
endmodule
struct TimerGroup32 extends array
static method create takes code c returns thistype
return A(c)
endmethod
method destroy takes nothing returns nothing
call DT32(this)
endmethod
method start takes nothing returns nothing
call A32(this)
endmethod
method stop takes nothing returns nothing
call SR32(this)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// IMPORTANT NOTE: This is a "modified" version of 'BoundSentinel' library by me (Starquizer).
// I am NOT its original scripter.
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
private real maxx
private real maxy
private real minx
private real miny
endglobals
private function dis takes nothing returns boolean
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
return false
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - 512.000 // Modification
set miny=GetCameraBoundMinY() - 256.000 // Modification
set maxx=GetCameraBoundMaxX() + 512.000 // Modification
set maxy=GetCameraBoundMaxY() + 256.000 // Modification
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddCondition(t, function dis) // Modification
//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
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 "Meteor Swarm 120" below to an empty trigger in your map.
Change the values to your needs and follow the instruction (green comments) that are in the frame:
//====================================================================================================
// User-edit data //
//====================================================================================================
//====================================================================================================
// End of User-edit data //
//====================================================================================================
Copy this data from the Object Editor:
UNITS:
Custom Units
|
---Night Elf
|
---Melee
|
---Units
|-- Target Area
|-- Effect
ABILITIES:
Custom Abilities
|
---Neutral Hostile
|
---Heroes
|
--- Meteor Swarm
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Meteor Swarm Version 1.20
scope MeteorSwarm initializer Init_MeteorSwarm
globals
private real MIN_X
private real MIN_Y
private real MAX_X
private real MAX_Y
private constant real SAFETY_OFFSET = 16.00
private constant integer M_DUMMY_ID = 'e000'
private constant integer I_DUMMY_ID = 'b000'
private constant integer ABILITY_ID = 'A000'
private integer index = 0
private integer array temp_this
private constant integer INT_MIN = 3 // Minimum Interval Between Meteors Threshold
private constant integer INT_MAX = 32 // Maximum Interval Between Meteors Threshold
private constant real SCALE_MIN = 0.80 // Minimum Meteor Scale Threshold
private constant real SCALE_MAX = 3.50 // Maximum Meteor Scale Threshold
private constant real H_MIN = 750.00 // Minimum Meteor Height Threshold
private constant real H_MAX = 1000.00 // Maximum Meteor Height Threshold
private constant real INIT_DIST = 1000.00
private constant real STEP = 50.00
private constant real CHECK_DIST = 32.00
private constant integer HT_OFFSET = 4096
private hashtable mht = InitHashtable() // Meteor Hashtable
private constant timer shake_timer = CreateTimer()
private unit CASTER
private real DAMAGE
//====================================================================================================
// User-edit data //
//====================================================================================================
// Make sure to change the dummy spell text to be appropriate with the following variables.
// General Formula: ConstantFactor + AbilityLevel * AbilityLevelFactor + PrevLevelFactor * PrevLevelValue
// The following variable can be changed according to the desired results
// Number of Meteors Variables:
private constant boolean NUM_TABLE = false // true/false to use Table/Formula
// Table:
private constant integer array nMeteors // set the values in the function named "Spell_Data_Table"
// Formula:
private constant integer NUM_CF = 25 // changes 'ConstantFactor'
private constant integer NUM_LF = 5 // changes 'AbilityLevelFactor'
private constant real NUM_PLF = 0 // changes 'PrevLevelFactor'
// Minimum Damage Variables:
private constant boolean D_MIN_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MinDamage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MIN_CF = 75.00 // changes 'ConstantFactor'
private constant real D_MIN_LF = 25.00 // changes 'AbilityLevelFactor'
private constant real D_MIN_PLF = 0 // changes 'PrevLevelFactor'
// Maximum Damage Variables:
private constant boolean D_MAX_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array MaxDamage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_MAX_CF = 250.00 // changes 'ConstantFactor'
private constant real D_MAX_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real D_MAX_PLF = 0 // changes 'PrevLevelFactor'
// Area of Effect (AoE) Variables:
// Grand Area of Effect:
private constant boolean GR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array GrandArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real GR_CF = 750.00 // changes 'ConstantFactor'
private constant real GR_LF = 50.00 // changes 'AbilityLevelFactor'
private constant real GR_PLF = 0 // changes 'PrevLevelFactor'
// Maximum AOE per Meteor:
private constant boolean MAXR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array LargeArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MAXR_CF = 150.00 // changes 'ConstantFactor'
private constant real MAXR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MAXR_PLF = 0 // changes 'PrevLevelFactor'
// Minimum AOE per Meteor:
private constant boolean MINR_TABLE = false // true/false to use Table/Formula
// Table:
private constant real array SmallArea // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real MINR_CF = 100.00 // changes 'ConstantFactor'
private constant real MINR_LF = 10.00 // changes 'AbilityLevelFactor'
private constant real MINR_PLF = 0 // changes 'PrevLevelFactor'
// Targets:
private constant boolean FRIENDLY = true // true/false to harm enemy/all units
private constant boolean GROUND = true // true/false to include/exclude ground units
private constant boolean FLYING = false // true/false to include/exclude flying units
private constant boolean STRUCTURE = true // true/false to include/exclude structure units
private constant boolean DESTRUCTABLES = true // true/false to enable/disable destructables
private constant real DESTRUCTABLE_FACTOR = 10.00 // this is the damage dealt to destructables divisor
// Flags:
private constant boolean INIT_MAX_D = true // true/false to enable/disable maximum damage on first meteor
private constant boolean KILLS_EXPLODE = true // true/false to enable/disable unit explosion if killed by a meteor
private constant boolean SHOW_INDICATOR = true // true/false to enable/disable cast area of effect indicator
private constant boolean ALLOW_CAMERA_SHAKE = true // true/false to enable/disable camera shaking
endglobals
// Just IGNORE this function (Table) IF you are using a formula (Just fold it)
private function Spell_Data_Table takes nothing returns nothing
// Number of Meteors Table
set nMeteors[1] = 30
set nMeteors[2] = 35
set nMeteors[3] = 40
set nMeteors[4] = 45
set nMeteors[5] = 50
// Minimum Damage Table
set MinDamage[1] = 100.00
set MinDamage[2] = 125.00
set MinDamage[3] = 150.00
set MinDamage[4] = 175.00
set MinDamage[5] = 200.00
// Maximum Damage Table
set MaxDamage[1] = 300.00
set MaxDamage[2] = 350.00
set MaxDamage[3] = 400.00
set MaxDamage[4] = 450.00
set MaxDamage[5] = 500.00
// Grand Area of Effect
set GrandArea[1] = 800.00
set GrandArea[2] = 850.00
set GrandArea[3] = 900.00
set GrandArea[4] = 950.00
set GrandArea[5] = 1000.00
// Maximum Area of Effect per Meteor
set LargeArea[1] = 160.00
set LargeArea[2] = 170.00
set LargeArea[3] = 180.00
set LargeArea[4] = 190.00
set LargeArea[5] = 200.00
// Minimum Area of Effect per Meteor
set SmallArea[1] = 110.00
set SmallArea[2] = 120.00
set SmallArea[3] = 130.00
set SmallArea[4] = 140.00
set SmallArea[5] = 150.00
//====================================================================================================
// End of User-edit data //
//====================================================================================================
endfunction
private function MeteorsNumber takes integer al returns integer
local integer prevNMet
static if NUM_TABLE then
return nMeteors[al]
else
if al == 1 then
set prevNMet = 0
else
set prevNMet = MeteorsNumber(al - 1)
endif
return NUM_CF + NUM_LF * al + R2I(NUM_PLF) * prevNMet
endif
endfunction
private function DamageMin takes integer al returns real
local real prevMinDmg
static if D_MIN_TABLE then
return MinDamage[al]
else
if al == 1 then
set prevMinDmg = 0.
else
set prevMinDmg = DamageMin(al - 1)
endif
return D_MIN_CF + D_MIN_LF * al + D_MIN_PLF * prevMinDmg
endif
endfunction
private function DamageMax takes integer al returns real
local real prevMaxDmg
static if D_MAX_TABLE then
return MaxDamage[al]
else
if al == 1 then
set prevMaxDmg = 0.
else
set prevMaxDmg = DamageMax(al - 1)
endif
return D_MAX_CF + D_MAX_LF * al + D_MAX_PLF * prevMaxDmg
endif
endfunction
private function AreaGrand takes integer al returns real
local real prevGrArea
static if GR_TABLE then
return GrandArea[al]
else
if al == 1 then
set prevGrArea = 0.
else
set prevGrArea = AreaGrand(al - 1)
endif
return GR_CF + GR_LF * al + GR_PLF * prevGrArea
endif
endfunction
private function MinAreaPerMeteor takes integer al returns real
local real prevMinA
static if MINR_TABLE then
return SmallArea[al]
else
if al == 1 then
set prevMinA = 0.
else
set prevMinA = MinAreaPerMeteor(al - 1)
endif
return MINR_CF + MINR_LF * al + MINR_PLF * prevMinA
endif
endfunction
private function MaxAreaPerMeteor takes integer al returns real
local real prevMaxA
static if MAXR_TABLE then
return LargeArea[al]
else
if al == 1 then
set prevMaxA = 0.
else
set prevMaxA = MaxAreaPerMeteor(al - 1)
endif
return MAXR_CF + MAXR_LF * al + MAXR_PLF * prevMaxA
endif
endfunction
private function GetSpellTargetXBounded takes nothing returns real
local real x = GetSpellTargetX()
if x < MIN_X then
return MIN_X + SAFETY_OFFSET
elseif x > MAX_X then
return MAX_X - SAFETY_OFFSET
else
return x
endif
endfunction
private function GetSpellTargetYBounded takes nothing returns real
local real y = GetSpellTargetY()
if y < MIN_Y then
return MIN_Y + SAFETY_OFFSET
elseif y > MAX_Y then
return MAX_Y - SAFETY_OFFSET
else
return y
endif
endfunction
private function MatchingUnit takes unit u,player owner returns boolean
return not( IsUnitType(u,UNIT_TYPE_DEAD) or/*
*/ IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or/*
*/( FRIENDLY and IsUnitAlly(u,owner)) or/*
*/(not FLYING and IsUnitType(u,UNIT_TYPE_FLYING)) or/*
*/(not GROUND and IsUnitType(u,UNIT_TYPE_GROUND)) or/*
*/(not STRUCTURE and IsUnitType(u,UNIT_TYPE_STRUCTURE)))
endfunction
private function meteorDamageUnits takes player owner,unit caster,real X, real Y,real rad,real damage returns nothing
local unit target
call GroupEnumUnitsInRange(bj_lastCreatedGroup,X,Y,rad,null)
loop
set target = FirstOfGroup(bj_lastCreatedGroup)
exitwhen target == null
if MatchingUnit(target,owner) then
static if KILLS_EXPLODE then
call SetUnitExploded(target,true)
endif
call UnitDamageTarget(caster,target,damage,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
static if KILLS_EXPLODE then
call SetUnitExploded(target,false)
endif
endif
call GroupRemoveUnit(bj_lastCreatedGroup,target)
endloop
endfunction
/* this function/filter is not needed for now and is turned off
private function matchingDestructable takes nothing returns boolean
return GetDestructableTypeId(GetFilterDestructable()) == WHO_KNOWS_WHAT_TYPE
endfunction*/
private function meteorDamageDestructables takes nothing returns nothing
call UnitDamageTarget(CASTER,GetEnumDestructable(),DAMAGE,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
endfunction
private struct Meteor extends array
player owner
unit caster
unit target_indicator
unit meteor
real TX
real TY
real slope
real const
real face
real grandarea
real minarea
real maxarea
real mindamage
real maxdamage
integer meteors
integer ibem // Interval Between Each Meteor
integer cfm // Current Falling Meteors
static method cameraShake takes thistype this returns nothing
local integer i = 0
local real camX = GetCameraTargetPositionX()
local real camY = GetCameraTargetPositionY()
local real dx = this.TX - camX
local real dy = this.TY - camY
local real x1 = SquareRoot( dx*dx + dy*dy )
local real x2
local real magnitude
loop
exitwhen i >= index
set this = temp_this[i]
set dx = this.TX - camX
set dy = this.TY - camY
set x2 = SquareRoot( dx*dx + dy*dy )
if x1 > x2 then
set x1 = x2
endif
set i = i + 1
endloop
set magnitude = 20.0 * Pow(bj_E,- 0.001 * x1) // magnitude = 20*e^(-x)
call CameraSetSourceNoiseEx(magnitude,1000.,false)
call CameraSetTargetNoiseEx(magnitude,1000.,false)
if index == 0 then
call CameraSetSourceNoise(0, 0)
call CameraSetTargetNoise(0, 0)
endif
endmethod
implement CTL
local integer i = 0 // Instance Counter index
local integer j = 0 // Meteor Counter index
local unit u // This Dummy Unit
local unit nu // Next Dummy Unit
local real x // This Meteor X
local real y // This Meteor Y
local real z // This Meteor Z
local real ix // This Meteor Initial X
local real iy // This Meteor Initial Y
local real tx // This Meteor Target X
local real ty // This Meteor Target Y
local real dx // Delta X
local real dy // Delta Y
local real dv // XY Plane variable
local real face // This Meteor Facing
local real slope // This Meteor Fall Line Slope
local real const // This Meteor Fall Line Constant
local real ratio // This Meteor Scaling Ratio
local real ntx // Next Target X
local real nty // Next Target Y
local real nface // Next Facing
local real nix // Next Initial X
local real niy // Next Initial Y
local real niz // Next Initial Z
local real rnd_rad // Random Radius
local real rnd_ang // Random Angle
local real bound // Bound Size of Falling Meteor w.r.t Grand Area
local real scale // Next Meteor Scaling
local real nratio // Next Meteor Scaling Ratio
local real accurate_radius // Accurate Radius
local real accurate_damage // Accurate Damage
local rect effectrect // effective Rect for destructables
implement CTLExpire
set this.ibem = this.ibem - 1
if this.ibem <= 0 then
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
set this.cfm = this.cfm + 1
if this.cfm <= this.meteors then
// launch another meteor
set bound = this.grandarea - this.maxarea
loop
set rnd_rad = GetRandomReal(0.0,bound)
set rnd_ang = GetRandomReal(0.0,2.*bj_PI)
set ntx = this.TX + rnd_rad*Cos(rnd_ang)
set nty = this.TY + rnd_rad*Sin(rnd_ang)
exitwhen ntx > MIN_X and ntx < MAX_X and nty > MIN_Y and nty < MAX_Y
endloop
loop
set nface = GetRandomReal(0.0,2.*bj_PI)
set nix = ntx - INIT_DIST * Cos(nface)
set niy = nty - INIT_DIST * Sin(nface)
exitwhen nix > MIN_X and nix < MAX_X and niy > MIN_Y and niy < MAX_Y
endloop
set niz = GetRandomReal(H_MIN,H_MAX)
set nu = CreateUnit(this.owner,M_DUMMY_ID,nix,niy,nface*bj_RADTODEG)
call SetUnitPathing(nu,false)
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
set nratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(nu,scale,scale,scale)
call SetUnitFlyHeight(nu,niz,0.0)
call SaveUnitHandle(mht,this,this.cfm,nu)
call SaveBoolean(mht,this,this.cfm,false)
call SaveReal(mht, this, this.cfm,ntx)
call SaveReal(mht, this,-this.cfm,nty)
call SaveReal(mht,-this, this.cfm,nix)
call SaveReal(mht,-this,-this.cfm,niy)
call SaveReal(mht, this + HT_OFFSET, this.cfm,nface)
call SaveReal(mht, this + HT_OFFSET,-this.cfm,nratio)
call SaveReal(mht,-this - HT_OFFSET, this.cfm,-niz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-this.cfm,niz)
endif
endif
set j = 1
loop
exitwhen j > this.cfm
set u = LoadUnitHandle(mht,this,j)
if not(LoadBoolean(mht,this,j)) then
set tx = LoadReal(mht, this, j)
set ty = LoadReal(mht, this,-j)
set ix = LoadReal(mht,-this, j)
set iy = LoadReal(mht,-this,-j)
set x = GetUnitX(u)
set y = GetUnitY(u)
set dx = tx - x
set dy = ty - y
set ratio = LoadReal(mht,this + HT_OFFSET,-j)
set accurate_radius = ratio * (this.maxarea - this.minarea ) + this.minarea
set accurate_damage = ratio * (this.maxdamage - this.mindamage) + this.mindamage
if SquareRoot( dx*dx + dy*dy ) < CHECK_DIST or GetUnitFlyHeight(u) < CHECK_DIST then
call KillUnit(u)
call SaveBoolean(mht,this,j,true)
// Shake camera
static if ALLOW_CAMERA_SHAKE then
call cameraShake(this)
endif
// Do Damage stuff
call meteorDamageUnits(this.owner,this.caster,tx,ty,accurate_radius,accurate_damage)
static if DESTRUCTABLES then
set CASTER = this.caster
set DAMAGE = accurate_damage/DESTRUCTABLE_FACTOR
set effectrect = Rect(tx - accurate_radius, ty - accurate_radius, tx + accurate_radius, ty + accurate_radius)
call EnumDestructablesInRect(effectrect,null,function meteorDamageDestructables)
call RemoveRect(effectrect)
endif
if IsUnitType(LoadUnitHandle(mht,this,this.meteors),UNIT_TYPE_DEAD) and/*
*/ LoadUnitHandle(mht,this,this.meteors) != null then
set index = index - 1
set temp_this[this] = temp_this[index]
call cameraShake(this)
static if SHOW_INDICATOR then
call RemoveUnit(this.target_indicator)
set this.target_indicator = null
endif
call this.destroy()
call FlushChildHashtable(mht, this)
call FlushChildHashtable(mht,-this)
call FlushChildHashtable(mht, this + HT_OFFSET)
call FlushChildHashtable(mht,-this - HT_OFFSET)
endif
else // Move the meteor
set face = LoadReal(mht,this + HT_OFFSET,j)
set slope = LoadReal(mht,-this - HT_OFFSET,j)
set const = LoadReal(mht,-this - HT_OFFSET,-j)
set x = x + STEP * Cos(face)
set y = y + STEP * Sin(face)
set dx = x - ix
set dy = y - iy
set dv = SquareRoot( dx*dx + dy*dy )
set z = slope * dv + const
call SetUnitX(u,x)
call SetUnitY(u,y)
call SetUnitFlyHeight(u,z,0.0)
endif
endif
set j = j + 1
endloop
implement CTLNull
set u = null
set nu = null
set effectrect = null
implement CTLEnd
static method meteorCall takes unit caster, real tx, real ty returns nothing
local thistype this = create()
local integer al = GetUnitAbilityLevel(caster,ABILITY_ID)
local unit u // Dummy Unit
local real cx // Cast Point X
local real cy // Cast Point Y
local real ix // First Meteor Initial X
local real iy // First Meteor Initial Y
local real iz // First Meteor Initial Z
local real dx
local real dy
local real face
local real scale
local real ratio
local real indicator_scale = 9. + I2R(al)
set temp_this[index] = this
set this.caster = caster
set this.owner = GetOwningPlayer(caster)
set this.TX = tx
set this.TY = ty
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set dx = tx - cx
set dy = ty - cy
set face = Atan2(dy,dx)
set ix = tx - INIT_DIST * Cos(face)
set iy = ty - INIT_DIST * Sin(face)
set iz = GetRandomReal(H_MIN,H_MAX)
static if SHOW_INDICATOR then
set this.target_indicator = CreateUnit(this.owner,I_DUMMY_ID,tx,ty,0.0)
call SetUnitVertexColor(this.target_indicator, 255,255,255,255)
call SetUnitTimeScale(this.target_indicator,0.7)
call SetUnitScale(this.target_indicator,indicator_scale,indicator_scale,indicator_scale)
call SetUnitPathing(this.target_indicator,false)
call SetUnitX(this.target_indicator,tx)
call SetUnitY(this.target_indicator,ty)
endif
set u = CreateUnit(this.owner,M_DUMMY_ID,ix,iy,face*bj_RADTODEG)
call SetUnitPathing(u,false)
static if INIT_MAX_D then
set scale = SCALE_MAX
else
set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
endif
set ratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
call SetUnitScale(u,scale,scale,scale)
call SetUnitFlyHeight(u,iz,0.0)
set this.cfm = 1
set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
call SaveUnitHandle(mht,this,1,u)
call SaveBoolean(mht,this,1,false)
call SaveReal(mht, this, 1,tx)
call SaveReal(mht, this,-1,ty)
call SaveReal(mht,-this, 1,ix)
call SaveReal(mht,-this,-1,iy)
call SaveReal(mht, this + HT_OFFSET, 1,face)
call SaveReal(mht, this + HT_OFFSET,-1,ratio)
call SaveReal(mht,-this - HT_OFFSET, 1,-iz/INIT_DIST)
call SaveReal(mht,-this - HT_OFFSET,-1,iz)
set this.meteors = MeteorsNumber(al)
set this.grandarea = AreaGrand(al)
set this.maxarea = MaxAreaPerMeteor(al)
set this.minarea = MinAreaPerMeteor(al)
set this.maxdamage = DamageMax(al)
set this.mindamage = DamageMin(al)
set u = null
set index = index + 1
endmethod
endstruct
private function MeteorSwarm_Conditions takes nothing returns boolean
if GetSpellAbilityId() == ABILITY_ID then
call Meteor.meteorCall(GetTriggerUnit(),GetSpellTargetXBounded(),GetSpellTargetYBounded())
endif
return false
endfunction
private function Init_MeteorSwarm takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, function MeteorSwarm_Conditions )
call RemoveUnit(CreateUnit(Player(12),M_DUMMY_ID,0.,0.,0.))
call RemoveUnit(CreateUnit(Player(12),I_DUMMY_ID,0.,0.,0.))
call Spell_Data_Table()
set MIN_X = GetCameraBoundMinX() - 512.000
set MIN_Y = GetCameraBoundMinY() - 256.000
set MAX_X = GetCameraBoundMaxX() + 512.000
set MAX_Y = GetCameraBoundMaxY() + 256.000
set t = null
endfunction
endscope