Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ThisMapOnly initializer Init
globals
private player CREEP = Player(13)
private group TESTERS = CreateGroup()
private rect array SECTOR
endglobals
private function PortBack takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer data = GetUnitUserData(u)
local real x = GetRectCenterX(SECTOR[data])
local real y = GetRectCenterY(SECTOR[data])
local string s = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl"
call DestroyEffect(AddSpecialEffect(s,GetWidgetX(u),GetWidgetY(u)))
call SetUnitX(u,x)
call SetUnitY(u,y)
call DestroyEffect(AddSpecialEffect(s,x,y))
set u = null
endfunction
private function IsCorrectOwner takes nothing returns boolean
return GetOwningPlayer(GetTriggerUnit()) == CREEP
endfunction
private function ChangeMS_GRP_CB takes nothing returns nothing
local unit u = GetEnumUnit()
call SetUnitMoveSpeed(u,GetRandomReal(150.,522))
call SetUnitVertexColor(u,GetRandomInt(0,0x100-1),GetRandomInt(0,0x100-1),GetRandomInt(0,0x100-1),125)
set u = null
endfunction
private function ChangeMS_P takes nothing returns nothing
local timer t = GetExpiredTimer()
call PauseTimer(t)
call ForGroup(TESTERS, function ChangeMS_GRP_CB)
call TimerStart(t,GetRandomReal(.0,5.),false, function ChangeMS_P)
set t = null
endfunction
private function InitSpawn takes nothing returns nothing
local unit array u
local integer i = 0
local trigger trg = CreateTrigger()
local timer t = GetExpiredTimer()
call PauseTimer(t)
call TimerStart(t,GetRandomReal(.0,5.),false, function ChangeMS_P)
set u[0] = CreateUnit(CREEP,'ntrg',GetRectCenterX(SECTOR[0]),GetRectCenterY(SECTOR[0]),.0)
set u[1] = CreateUnit(CREEP,'nsc2',GetRectCenterX(SECTOR[1]),GetRectCenterY(SECTOR[1]),.0)
set u[2] = CreateUnit(CREEP,'nmsc',GetRectCenterX(SECTOR[2]),GetRectCenterY(SECTOR[2]),.0)
set u[3] = CreateUnit(CREEP,'nrel',GetRectCenterX(SECTOR[3]),GetRectCenterY(SECTOR[3]),.0)
loop
exitwhen i == 4
call TriggerRegisterEnterRectSimple(trg,SECTOR[7-i])
call SetUnitUserData(u[i],i)
call IssuePointOrder(u[i],"smart",GetRectCenterX(SECTOR[7-i]),GetRectCenterY(SECTOR[7-i]))
call SetUnitVertexColor(u[i],255,255,255,135)
call GroupAddUnit(TESTERS,u[i])
call SetUnitPathing(u[i],false)
set u[i] = null
set i = i +1
endloop
call TriggerAddAction(trg, function PortBack )
call TriggerAddCondition(trg, Filter( function IsCorrectOwner))
set trg = null
set t = null
endfunction
private function Respawn takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer data = GetUnitUserData(u)
local integer id = GetUnitTypeId(u)
local integer i = 0
call GroupRemoveUnit(TESTERS,u)
loop
exitwhen i == 2 or CountUnitsInGroup(TESTERS) >= 45
set u = CreateUnit(CREEP,id,GetRectCenterX(SECTOR[data]),GetRectCenterY(SECTOR[data]),0.)
call SetUnitUserData(u,data)
call IssuePointOrder(u,"smart",GetRectCenterX(SECTOR[7-data]),GetRectCenterY(SECTOR[7-data]))
call SetUnitVertexColor(u,255,255,255,135)
call GroupAddUnit(TESTERS,u)
call SetUnitPathing(u,false)
set i = i +1
endloop
set u = null
endfunction
private function Init takes nothing returns nothing
local timer t = CreateTimer()
local trigger trg = CreateTrigger()
set SECTOR[0x0] = gg_rct_Region_000
set SECTOR[0x1] = gg_rct_Region_001
set SECTOR[0x2] = gg_rct_Region_002
set SECTOR[0x3] = gg_rct_Region_003
set SECTOR[0x4] = gg_rct_Region_004
set SECTOR[0x5] = gg_rct_Region_005
set SECTOR[0x6] = gg_rct_Region_006
set SECTOR[0x7] = gg_rct_Region_007
call CreateFogModifierRectBJ( true, Player(0), FOG_OF_WAR_VISIBLE, GetPlayableMapRect() )
call TriggerRegisterAnyUnitEventBJ(trg,EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction(trg, function Respawn)
call TriggerAddCondition(trg, Filter( function IsCorrectOwner))
call TimerStart(t,3,false, function InitSpawn)
set t = null
endfunction
endscope
//TESH.scrollpos=123
//TESH.alwaysfold=0
library SplitterWave initializer Init uses IsTerrainWalkable,TNTK
globals
private constant integer SID = 'spws'
//The spell rawcode
private constant integer DID = 'spld'
//The dummy unit rawcode
private constant real MOVE_STEP = 25
//The moving speed of a wave
private constant real COLLISION_RADIUS = 80.
//The collisions radius of a wave
private constant real MIN_DAMAGE = 10.0
//The minimal damage dealt on impact. Because of the damage
//reduction feature this is necessary.
private constant real TIME_OUT = 0.04
//Time buffer which prevents perma multi-splits
private constant string SPLIT_SFX = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
//Special effect being displayed when a wave *dies*
private constant string IMPACT_SFX = ""
//Special effect being displayed on a unit which got hit by a wave
//Damage options... should be selfexplaining
private constant attacktype ATTACK = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WEAPON = WEAPON_TYPE_WHOKNOWS
endglobals
//_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-
//The spell privates. PLEASE DO NOT TOUCH !
//_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-
private keyword Control
globals
private Control TEMP
private group TEMP_GROUP
private filterfunc CollisionFilter //Filter callback for the enum group
endglobals
//_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-
//_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-_-=-
//The maximal travel distance for one spell instance....Well if you don´t understand here´s an example:
//The maximal travel distance is 5000. The first wave has already travelled 3000. If it splits the new
//waves can reach a maximal distance of 2000 (5000-3000). This is valid for every split
private constant function GetMaxDistance takes integer level returns integer
return level * 1250 + 550
endfunction
//The maximal amount of splits for one spell instance
private constant function GetSplits takes integer level returns integer
return level * 4 - 1
endfunction
//The amount of new waves which spawn when a wave is splitting
private constant function GetNewWaveAmount takes integer level returns integer
return level * 0 + 3
endfunction
//The damage dealt to the nearby enemies
private constant function GetImpactDamage takes integer level returns integer
return level * 30 + 10
endfunction
//A small part of the damage which is decreasing on every split.
private constant function GetDamageReduction takes real level returns real
return 2 / level + 1.5
endfunction
//A single wave instance
private struct Wave extends TNTKnock
//Destructor
method onDestroy takes nothing returns nothing
local Control m = .data
set m.waves = m.waves -1
set m.damage = m.damage - m.reduct
if m.damage < MIN_DAMAGE then
set m.damage = MIN_DAMAGE
endif
call DestroyEffect(AddSpecialEffect(SPLIT_SFX,.x,.y))
call RemoveUnit(.knocker)
endmethod
//Selfexplaining
method OnSplit takes nothing returns nothing
local Control m = .data
local real start = Acos(.velx/MOVE_STEP)
local real end = start + bj_PI
local real step = bj_PI / m.new
local unit u
//Checks the timer buffer, the remaining distance and splits
if m.splits > 0 and .distance > 0 and m.cansplit then
set m.cansplit = false
set m.splits = m.splits - 1 //If this becomes 0 the spell is over
//Creating new waves
loop
exitwhen start > end
set u = CreateUnit(Player(15),DID,.0,.0,start*bj_RADTODEG)
call Wave.create(m,u,.x + 2 * .speed * Cos(start),.y + 2 * .speed * Cos(start),start,.distance - 2* .speed)
set m.waves = m.waves + 1
set start = start + step
endloop
endif
call .destroy()
set u = null
endmethod
//Redefining the onBreak method
method onBreak takes nothing returns boolean
local Control m = .data
//Is the instance about to be destroyed normally
if super.onBreak() then
set m.waves = m.waves -1
return true
endif
set TEMP = m
call GroupClear(TEMP_GROUP)
call GroupEnumUnitsInRange(TEMP_GROUP,.x,.y,COLLISION_RADIUS,CollisionFilter)
if not IsTerrainWalkable(.x,.y) or FirstOfGroup(TEMP_GROUP) != null then
call .OnSplit()
return true
endif
return false
endmethod
//Constructor
static method create takes Control m, unit dummy, real x, real y, real rad, real dist returns thistype
local thistype this = thistype.allocate()
call .StartLinear(null,x,y,rad,dist,MOVE_STEP)
call SetUnitPosition(dummy,.x + .velx,.y + .vely)
call SetUnitFacing(dummy,rad*bj_RADTODEG)
set .knocker = dummy
set .data = m
return this
endmethod
endstruct
//This struct controls the splits and holds some coherent data
private struct Control extends Indexable
real runtime = TIME_OUT
unit caster
//real timeout //The time buffer
real damage
real reduct
integer splits
integer waves
integer new
boolean cansplit
//Checks if there are on splits/waves left
method onBreak takes nothing returns boolean
return .splits <= 0 or .waves <= 0
endmethod
//Updates the the time buffer
method onLoop takes nothing returns nothing
set .cansplit = true
endmethod
//Creator
static method create takes unit ca, unit dummy, location loc returns thistype
local thistype this = thistype.allocate()
local real rad = Atan2(GetLocationY(loc) - GetWidgetY(ca),GetLocationX(loc) - GetWidgetX(ca))
local integer lv = GetUnitAbilityLevel(ca,SID)
call Wave.create(this,dummy,GetWidgetX(ca),GetWidgetY(ca),rad,GetMaxDistance(GetUnitAbilityLevel(ca,SID)))
set .caster = ca
set .new = GetNewWaveAmount(lv)
set .splits = GetSplits(lv)
set .damage = GetImpactDamage(lv)
set .reduct = GetDamageReduction(lv)
set .waves = 1
set .cansplit = true
return this
endmethod
endstruct
//A filter which checks if there are valid-enemy units near a wave
private function CollisionFilterFunc takes nothing returns boolean
local Control m = TEMP
local unit u = GetFilterUnit()
//Checks if a wave has to split
if IsUnitEnemy(u,GetOwningPlayer(m.caster)) and not ( /*
*/ GetUnitTypeId(u) == DID or /*
*/ IsUnitType(u, UNIT_TYPE_DEAD) or /*
*/ IsUnitType(u, UNIT_TYPE_STRUCTURE) or /*
*/ IsUnitType(u, UNIT_TYPE_MECHANICAL) or /*
*/ IsUnitType(u, UNIT_TYPE_FLYING) or /*
*/ IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) or /*
*/ IsUnitType(u, UNIT_TYPE_ANCIENT) ) /*
*/then
call UnitDamageTarget(m.caster,u,m.damage,false,false,ATTACK,DAMAGE,WEAPON)
call DestroyEffect(AddSpecialEffect(IMPACT_SFX,GetWidgetX(u),GetWidgetY(u)))
set u = null
return true
endif
set u = null
return false
endfunction
//Starts the spell
private function onCast takes nothing returns nothing
call Control.create(GetTriggerUnit(),CreateUnit(Player(15),DID,.0,.0,.0),GetSpellTargetLoc())
//I know its a bad idea to create the dummy unit at this point of the script
//but this will prevent a weird bug
endfunction
//Checks the rawcode
private function onCheck takes nothing returns boolean
return GetSpellAbilityId() == SID
endfunction
//Initializer
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
set TEMP_GROUP = CreateGroup()
set CollisionFilter = Filter( function CollisionFilterFunc )
call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddAction(trig, function onCast)
call TriggerAddCondition(trig, Filter( function onCheck))
call Preload(SPLIT_SFX)
call Preload(IMPACT_SFX)
call PreloadStart()
set trig = null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library IsTerrainWalkable initializer Init
//*****************************************************************
//* IsTerrainWalkable
//*
//* rewritten in vJass by: Anitarf
//* original implementation: Vexorian
//*
//* A function for checking if a point is pathable for ground
//* units (it does so by attempting to move an item there and
//* checking where it ended up), typically used to stop sliding
//* units before they end up stuck in trees. If the point is not
//* pathable, the function will also determine the nearest point
//* that is (the point where the item ends up).
//*****************************************************************
globals
// this value is how far from a point the item may end up for the point to be considered pathable
private constant real MAX_RANGE = 10.0
// the following two variables are set to the position of the item after each pathing check
// that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
public real X = 0.0
public real Y = 0.0
// END OF CALIBRATION SECTION
// ================================================================
private rect r
private item check
private item array hidden
private integer hiddenMax = 0
endglobals
private function Init takes nothing returns nothing
set check = CreateItem('ciri',0,0)
call SetItemVisible(check,false)
set r = Rect(0.0,0.0,128.0,128.0)
endfunction
private function HideBothersomeItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set hidden[hiddenMax]=GetEnumItem()
call SetItemVisible(hidden[hiddenMax],false)
set hiddenMax=hiddenMax+1
endif
endfunction
// ================================================================
function IsTerrainWalkable takes real x, real y returns boolean
// first, hide any items in the area so they don't get in the way of our item
call MoveRectTo(r, x,y)
call EnumItemsInRect(r,null,function HideBothersomeItem)
// try to move the check item and get it's coordinates
call SetItemPosition(check,x,y)//this unhides the item...
set X = GetItemX(check)
set Y = GetItemY(check)
call SetItemVisible(check,false)//...so we must hide it again
// before returning, unhide any items that got hidden at the start
loop
exitwhen hiddenMax<=0
set hiddenMax=hiddenMax-1
call SetItemVisible(hidden[hiddenMax],true)
set hidden[hiddenMax]=null
endloop
// return pathability status
return (x-X)*(x-X)+(y-Y)*(y-Y) < MAX_RANGE*MAX_RANGE
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=1
library TNTI
private interface Ext
static integer Total = 0
static integer Current = 0
static constant real Intervall = .01
static timer TIM = null
real runtime = .01
method onBreak takes nothing returns boolean defaults true
method onLoop takes nothing returns nothing defaults nothing
endinterface
struct Indexable extends Ext
private static Ext array Index
private real timing = .0
/*stub method onBreak takes nothing returns boolean
call BJDebugMsg(SCOPE_PREFIX+": |c00FF0000onBreak() from struct with id: "+I2S(.getType())+" is not defined !")
return true
endmethod
stub method onLoop takes nothing returns nothing
call BJDebugMsg(SCOPE_PREFIX+": |c00FF0000onLoop() from struct with id: "+I2S(.getType())+" is not defined !")
endmethod*/
private method onDestroy takes nothing returns nothing
set Ext.Total = Ext.Total -1
set thistype.Index[Ext.Current] = thistype.Index[Ext.Total]
set Ext.Current = Ext.Current-1
if Ext.Total == 0 then
call PauseTimer(Ext.TIM)
endif
endmethod
private static method Loop takes nothing returns nothing
local Ext this
set Ext.Current = 0
loop
exitwhen Ext.Current == Ext.Total
set this = thistype.Index[Ext.Current]
set .timing = .timing + thistype.Intervall
if .timing >= .runtime then
if .onBreak() then
call .destroy()
else
call .onLoop()
set .timing = 0
endif
endif
set Ext.Current = Ext.Current+1
endloop
endmethod
static method create takes nothing returns thistype
local Ext this = thistype.allocate()
set thistype.Index[Ext.Total] = this
set Ext.Total = Ext.Total +1
if Ext.Total == 1 then
call TimerStart(Ext.TIM,Ext.Intervall,true, function thistype.Loop)
endif
return this
endmethod
private static method onInit takes nothing returns nothing
set Ext.TIM = CreateTimer()
endmethod
endstruct
endlibrary
//TESH.scrollpos=48
//TESH.alwaysfold=1
library TNTK requires TNTI
globals
private constant real OFFSET = 64.
private real MAX
private real MAY
private real MIX
private real MIY
endglobals
struct TNTKnock extends Indexable
unit knocker = null
unit target = null
integer data = 0x0
readonly real velx = .0
readonly real vely = .0
readonly boolean inuse = false
readonly real runtime = 0.02
readonly real x = .0
readonly real y = .0
readonly real distance = 10.
readonly real radiants = .0
readonly real time = .0
readonly real speed = .0
method IsInMap takes nothing returns boolean
return .x <= MAX and .x >= MIX and .y <= MAY and .y >= MIY
endmethod
stub method onKnock takes nothing returns nothing
call SetUnitPosition(.knocker,.x,.y)
//Sorry but for some unknown reason SetUnitX/Y bugs
//call SetUnitX(.knocker,.x)
//call SetUnitY(.knocker,.y)
endmethod
method onBreak takes nothing returns boolean
return not .IsInMap() or .distance <= .speed+1. /*
*/ or ( .target != null and IsUnitType(.target ,UNIT_TYPE_DEAD)) /*
*/ or ( .knocker != null and IsUnitType(.knocker,UNIT_TYPE_DEAD))
endmethod
method onLoop takes nothing returns nothing
if .inuse then
if .target == null then
set .x = .x + velx
set .y = .y + vely
set .time = .time - .runtime
set .distance = .distance - .speed
else
set .velx = GetWidgetX(.target) - GetWidgetX(.knocker)
set .vely = GetWidgetY(.target) - GetWidgetY(.knocker)
set .radiants = Atan2(.vely,.velx)
set .distance = SquareRoot(.velx*.velx+.vely*.vely) - .speed
set .x = .x + Cos(.radiants) * .speed
set .y = .y + Sin(.radiants) * .speed
set .time = .time + .runtime
endif
call .onKnock()
endif
endmethod
method onDestroy takes nothing returns nothing
set .knocker = null
set .target = null
set .inuse = false
endmethod
method ChangeSpeed takes real newspeed returns nothing
set velx = Cos(.radiants) * newspeed
set vely = Sin(.radiants) * newspeed
set .speed = newspeed
set .time = (.distance*.runtime)/newspeed
endmethod
method ChangeDirection takes real newrad returns nothing
set velx = Cos(newrad) * .speed
set vely = Sin(newrad) * .speed
set .radiants = newrad
endmethod
method Stop takes nothing returns nothing
set .inuse = true
set .distance = -1.
endmethod
method StartLinear takes unit u, real sx, real sy, real rad, real dis, real speed returns nothing
if speed > 1 and not .inuse then
set .knocker = u
set .target = null
set .x = sx
set .y = sy
set .distance = dis
set .radiants = rad
set .time = (dis*.runtime) / speed
set .speed = speed
set .velx = Cos(rad)*speed
set .vely = Sin(rad)*speed
set .inuse = true
endif
endmethod
method StartLinearTimed takes unit u, real sx, real sy, real rad, real dis, real time returns nothing
if time > .runtime and not .inuse then
set .knocker = u
set .target = null
set .x = sx
set .y = sy
set .distance = dis
set .radiants = rad
set .time = time
set .speed = (dis*.runtime) / time
set velx = Cos(rad) * .speed
set vely = Sin(rad) * .speed
set .inuse = true
endif
endmethod
method StartLinearTimedLoc takes unit u, location from, location to, real time, boolean noleak returns nothing
local real x = GetLocationX(to) - GetLocationX(from)
local real y = GetLocationY(to) - GetLocationY(from)
local real rad = Atan2(y,x)
local real dis = SquareRoot(x*x+y*y)
call .StartLinearTimed(u,GetLocationX(from),GetLocationY(from),rad,dis,time)
if noleak then
call RemoveLocation(from)
call RemoveLocation(to)
endif
endmethod
method StartHomingTimed takes unit u, unit targ, real sx, real sy, real speed returns nothing
if IsUnitType(targ,UNIT_TYPE_DEAD) != true and targ != null and speed > 0. then
set .knocker = u
set .target = targ
set .x = sx
set .y = sy
set .speed = speed
set .time = 0.
set .distance = speed+3.
set .inuse = true
endif
endmethod
private static method onInit takes nothing returns nothing
set MAX = GetRectMaxX(bj_mapInitialPlayableArea) - OFFSET
set MAY = GetRectMaxY(bj_mapInitialPlayableArea) - OFFSET
set MIX = GetRectMinX(bj_mapInitialPlayableArea) + OFFSET
set MIY = GetRectMinY(bj_mapInitialPlayableArea) + OFFSET
endmethod
endstruct
endlibrary