Name | Type | is_array | initial_value |
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//<o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o>
//: Loc
//: by Anachron
//:
//: Simple library to give me more control about locations.
//: Feel free to use it without credits.
//<o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o>
library Loc
globals
private constant location zLoc = Location(0, 0)
constant real TWO_PI = 2 * bj_PI
endglobals
//! textmacro CMPos_addOperator takes name, type, object
method operator $name$= takes $type$ val returns nothing
set .$object$ = val
endmethod
method operator $name$ takes nothing returns $type$
return .$object$
endmethod
//! endtextmacro
function GetLocZ takes real x, real y returns real
call MoveLocation(zLoc, x, y)
return GetLocationZ(zLoc)
endfunction
function makeRealAngle takes real a returns real
loop
exitwhen a < TWO_PI and a >= 0.
if a < 0 then
set a = a + TWO_PI
elseif a > TWO_PI then
set a = a - TWO_PI
endif
endloop
return a
endfunction
struct Loc
private real F = 0.
private real X = 0.
private real Y = 0.
private real Z = 0.
//! runtextmacro CMPos_addOperator("f", "real", "F")
//! runtextmacro CMPos_addOperator("x", "real", "X")
//! runtextmacro CMPos_addOperator("y", "real", "Y")
//! runtextmacro CMPos_addOperator("z", "real", "Z")
public static method create takes real x, real y, real z, real f returns thistype
local thistype this = thistype.allocate()
set .F = f
set .X = x
set .Y = y
set .Z = z
return this
endmethod
public static method fromUnit takes unit theUnit returns thistype
local real x = GetUnitX(theUnit)
local real y = GetUnitY(theUnit)
local real z = GetLocZ(x, y) + GetUnitFlyHeight(theUnit)
local real f = GetUnitFacing(theUnit) * bj_DEGTORAD
return Loc.create(x, y, z, f)
endmethod
public static method fromDest takes destructable theDest returns thistype
local real x = GetDestructableX(theDest)
local real y = GetDestructableY(theDest)
local real z = GetLocZ(x, y) + GetDestructableOccluderHeight(theDest)
local real f = 0.
return Loc.create(x, y, z, f)
endmethod
public static method fromItem takes item theItem returns thistype
local real x = GetItemX(theItem)
local real y = GetItemY(theItem)
local real z = GetLocZ(x, y)
local real f = 0.
return Loc.create(x, y, z, f)
endmethod
public static method fromXY takes real x, real y returns thistype
local real z = GetLocZ(x, y)
local real f = 0.
return Loc.create(x, y, z, f)
endmethod
public method angleTo takes thistype that returns real
return makeRealAngle(Atan2(that.y - .y, that.x - .x))
endmethod
public method distanceTo takes thistype that returns real
//: Thanks to The_Reborn_Devil
return SquareRoot((that.x - .x)*(that.x - .x) + (that.y - .y)*(that.y - .y))
endmethod
public method distanceToZ takes thistype that returns real
//: Thanks to The_Reborn_Devil
return SquareRoot((that.x - .x)*(that.x - .x) + (that.y - .y)*(that.y - .y) + (that.z - .z)*(that.z - .z))
endmethod
public method move takes real d returns nothing
set .x = .x + d * Cos(.f)
set .y = .y + d * Sin(.f)
endmethod
public method moveFaced takes real d, real f returns nothing
set .x = .x + d * Cos(f)
set .y = .y + d * Sin(f)
endmethod
public method applyNew takes real x, real y, real z, real f returns nothing
set .f = f
set .x = x
set .y = y
set .z = z
endmethod
public method applyLoc takes thistype that returns nothing
set .f = that.f
set .x = that.x
set .y = that.y
set .z = that.z
endmethod
public method clone takes nothing returns thistype
local thistype that = thistype.allocate()
set that.f = .f
set that.x = .x
set that.y = .y
set that.z = .z
return that
endmethod
endstruct
function AngleBetweenUnits takes unit first, unit sec returns real
local Loc lFirst = Loc.create(GetUnitX(first), GetUnitY(first), 0., 0.)
local Loc lSec = Loc.create(GetUnitX(sec), GetUnitY(sec), 0., 0.)
local real angle = 0.
if first == null or sec == null then
set angle = -1.
else
set angle = lFirst.angleTo(lSec)
endif
call lFirst.destroy()
call lSec.destroy()
return angle
endfunction
function AngleBetweenLocs takes real x1, real y1, real x2, real y2 returns real
return makeRealAngle(Atan2(y2 - y1, x2 - x1))
endfunction
function DistBetweenPoints takes real startX, real startY, real endX, real endY returns real
return SquareRoot((endX - startX)*(endX - startX) + (endY - startY)*(endY - startY))
endfunction
function DistBetweenUnits takes unit first, unit sec returns real
local Loc lFirst = Loc.create(GetUnitX(first), GetUnitY(first), 0., GetUnitFacing(first))
local Loc lSec = Loc.create(GetUnitX(sec), GetUnitY(sec), 0., GetUnitFacing(sec))
local real dist = 0.
if first == null or sec == null then
set dist = -1.
else
set dist = lFirst.distanceTo(lSec)
endif
call lFirst.destroy()
call lSec.destroy()
return dist
endfunction
function DistBetweenUnitsZ takes unit first, unit sec returns real
local real zFirst = GetLocZ(GetUnitX(first), GetUnitY(first)) + GetUnitFlyHeight(first)
local real zSec = GetLocZ(GetUnitX(sec), GetUnitY(sec)) + GetUnitFlyHeight(sec)
local Loc lFirst = Loc.create(GetUnitX(first), GetUnitY(first), zFirst, GetUnitFacing(first))
local Loc lSec = Loc.create(GetUnitX(sec), GetUnitY(sec), zSec, GetUnitFacing(sec))
local real dist = 0.
if first == null or sec == null then
set dist = -1.
else
set dist = lFirst.distanceToZ(lSec)
endif
call lFirst.destroy()
call lSec.destroy()
return dist
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//<o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o>
//: CustomEffect
//: by Anachron
//:
//: This system is thought to replace xefx.
//:
//: What it is:
//: This system uses a dummy unit to create an effect that is scale- and
//: colorizeble and gives the posability to modify it in any way you want.
//:
//: Credits:
//: Vexorian for xe(fx) [which I don't use]
//: Vexorian for ARGB (optional) [http://www.wc3c.net/showthread.php?t=101858]
//:
//: Thanks to:
//: Slaydon, catch_ya, Flame_Phoenix, Element_Of_Water and Berbanog
//: for their input to the system. Without them this wouldn't be that
//: customizeable and would've less functionality.
//:
//<o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o>
library CustomEffect requires Loc, optional ARGB
globals
constant integer EFFECT_UNIT_TYPE = 'cefx'
private constant integer EFFECT_HEIGHT_ENABLER = 'Arav'
private constant integer EFFECT_DISSELECT = 'Aloc'
private constant integer MAX_INSTANCES = 8190
endglobals
struct CustomEffect[MAX_INSTANCES]
private unit effUnit = null
private effect effHandle = null
private string effSFX = null
private Loc pos = 0
private real zAng = 0.
/* ------- Scale ------- */
private real scal = 1.
/* ------- Color ------- */
private integer colr = 255
private integer colrA = 255
private integer colrR = 255
private integer colrG = 255
private integer colrB = 255
public static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set .pos = Loc.create(0., 0., 0., 0.)
return this
endmethod
public method use takes real x, real y, real z, real f returns nothing
call .pos.applyNew (x, y, z, f)
call RemoveUnit (.effUnit)
set .effUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), EFFECT_UNIT_TYPE, x, y, bj_RADTODEG * f)
call UnitAddAbility (.effUnit, EFFECT_HEIGHT_ENABLER)
call SetUnitFlyHeight (.effUnit, z - GetLocZ(x, y), 0.)
call UnitRemoveAbility (.effUnit, EFFECT_HEIGHT_ENABLER)
call UnitAddAbility (.effUnit, EFFECT_DISSELECT)
// apply the settings to the new unit
set .sfx = .sfx
set .scale = .scale
set .colorA = .colorA
set .colorR = .colorR
set .colorG = .colorG
set .colorB = .colorB
endmethod
public static method copy takes thistype this, thistype that returns nothing
set that.sfx = .sfx
set that.scale = .scale
set that.colorA = .colorA
set that.colorR = .colorR
set that.colorG = .colorG
set that.colorB = .colorB
endmethod
//# ======= -----------------------------------
/* Position */
//# ======= -----------------------------------
method operator f takes nothing returns real
return .pos.f
endmethod
method operator f= takes real f returns nothing
call SetUnitFacing(.effUnit, f * bj_RADTODEG)
set .pos.f = f
endmethod
method operator x takes nothing returns real
return .pos.x
endmethod
method operator x= takes real x returns nothing
call SetUnitX(.effUnit, x)
set .pos.x = x
endmethod
method operator y takes nothing returns real
return .pos.y
endmethod
method operator y= takes real y returns nothing
call SetUnitY(.effUnit, y)
set .pos.y = y
endmethod
method operator z takes nothing returns real
return .pos.z
endmethod
method operator z= takes real z returns nothing
call SetUnitFlyHeight(.effUnit, z - GetLocZ(.pos.x, .pos.y), 0.)
set .pos.z = z
endmethod
method operator zAngle takes nothing returns real
return .zAng
endmethod
//: Thanks to The_Reborn_Devil
method operator zAngle= takes real value returns nothing
local integer i = R2I(value * bj_RADTODEG + 90.5)
if i >= 180 then
set i = 179
elseif i < 0 then
set i = 0
endif
call SetUnitAnimationByIndex(.effUnit, i)
set .zAng = value
endmethod
method operator loc takes nothing returns Loc
return .pos
endmethod
method operator loc= takes Loc l returns nothing
call .pos.applyLoc(l)
call SetUnitFacing(.effUnit, .pos.f * bj_RADTODEG)
call SetUnitX(.effUnit, .pos.x)
call SetUnitY(.effUnit, .pos.y)
call SetUnitFlyHeight(.effUnit, .pos.z - GetLocZ(pos.x, .pos.y), 0.)
endmethod
public method applyFOnce takes real f returns nothing
call SetUnitFacing(.effUnit, f * bj_RADTODEG)
endmethod
public method applyXOnce takes real x returns nothing
call SetUnitX(.effUnit, x)
endmethod
public method applyYOnce takes real y returns nothing
call SetUnitY(.effUnit, y)
endmethod
public method applyZOnce takes real z returns nothing
call SetUnitFlyHeight(.effUnit, z - GetLocZ(pos.x, .pos.y), 0.)
endmethod
public method applyLocOnce takes Loc l returns nothing
call SetUnitFacing(.effUnit, l.f * bj_RADTODEG)
call SetUnitX(.effUnit, l.x)
call SetUnitY(.effUnit, l.y)
call SetUnitFlyHeight(.effUnit, l.z - GetLocZ(l.x, l.y), 0.)
endmethod
//# ======= -----------------------------------
/* Scale */
//# ======= -----------------------------------
method operator scale takes nothing returns real
return .scal
endmethod
method operator scale= takes real s returns nothing
set .scal = s
call SetUnitScale(.effUnit, .scal, .scal, .scal)
endmethod
//# ======= -----------------------------------
/* Color */
//# ======= -----------------------------------
private method applyColor takes nothing returns nothing
call SetUnitVertexColor(.effUnit, .colorR, .colorG, .colorB, .colorA)
endmethod
method operator colorA takes nothing returns integer
return .colrA
endmethod
method operator colorA= takes integer c returns nothing
set .colrA = c
call .applyColor()
endmethod
method operator colorR takes nothing returns integer
return .colrA
endmethod
method operator colorR= takes integer c returns nothing
set .colrR = c
call .applyColor()
endmethod
method operator colorG takes nothing returns integer
return .colrG
endmethod
method operator colorG= takes integer c returns nothing
set .colrG = c
call .applyColor()
endmethod
method operator colorB takes nothing returns integer
return .colrB
endmethod
method operator colorB= takes integer c returns nothing
set .colrB = c
call .applyColor()
endmethod
method operator color takes nothing returns integer
return .colr
endmethod
method operator color= takes integer c returns nothing
set .colrA = c
set .colrR = c
set .colrG = c
set .colrB = c
call .applyColor()
endmethod
public method colorARGB takes integer a, integer r, integer g, integer b returns nothing
set .colrA = a
set .colrR = r
set .colrG = g
set .colrB = b
call .applyColor()
endmethod
static if LIBRARY_ARGB then
public method ARGB takes ARGB color returns nothing
set .colrA = color.alpha
set .colrR = color.red
set .colrG = color.green
set .colrB = color.blue
call .applyColor()
endmethod
endif
//# ======= -----------------------------------
/* Misc */
//# ======= -----------------------------------
method operator owner takes nothing returns player
return GetOwningPlayer(.effUnit)
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(.effUnit, p, false)
endmethod
method operator sfx takes nothing returns string
return .effSFX
endmethod
method operator sfx= takes string sfx returns nothing
call DestroyEffect(.effHandle)
set .effHandle = AddSpecialEffectTarget(sfx, .effUnit, "origin")
set .effSFX = sfx
endmethod
public method toUnit takes nothing returns unit
return .effUnit
endmethod
//# ======= -----------------------------------
/* Effects */
//# ======= -----------------------------------
public method createHere takes string sfx returns thistype
local thistype that = thistype.create()
local real x = GetUnitX(.effUnit)
local real y = GetUnitY(.effUnit)
local real z = GetLocZ(x, y) + GetUnitFlyHeight(.effUnit)
local real f = GetUnitFacing(.effUnit) * bj_DEGTORAD
set that.sfx = sfx
call that.use(x, y, z, f)
return that
endmethod
private method onDestroy takes nothing returns nothing
call DestroyEffect(.effHandle)
if .pos != 0 then
call .pos.destroy()
set .pos = 0
endif
call RemoveUnit(.effUnit)
endmethod
endstruct
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library StructUtils
//! textmacro DynamicCallStructModule takes Action
module Dynamic$Action$Struct
public static trigger $Action$Trigger = null
public static method add$Action$ takes code theFunc returns triggeraction
return TriggerAddAction(thistype.$Action$Trigger, theFunc)
endmethod
public static method remove$Action$ takes triggeraction theFunc returns nothing
call TriggerRemoveAction(thistype.$Action$Trigger, theFunc)
endmethod
public static method invoke$Action$ takes nothing returns nothing
call TriggerExecute(thistype.$Action$Trigger)
endmethod
private static method onInit takes nothing returns nothing
set thistype.$Action$Trigger = CreateTrigger()
endmethod
endmodule
//! endtextmacro
//! runtextmacro DynamicCallStructModule("Register")
//! runtextmacro DynamicCallStructModule("Periodic")
//! runtextmacro DynamicCallStructModule("Cleanup")
module SaveLoadStruct
private static Table INSTANCES = 0
public integer id = -1
public static integer SELF = 0
public method save takes nothing returns nothing
set thistype.INSTANCES[.id] = integer(this)
endmethod
public method clear takes nothing returns nothing
call thistype.INSTANCES.reset()
endmethod
public method remove takes nothing returns nothing
call thistype.INSTANCES.flush(.id)
endmethod
public static method exists takes integer id returns boolean
return thistype.INSTANCES.exists(id)
endmethod
public static method load takes integer id returns thistype
return thistype(thistype.INSTANCES[id])
endmethod
private static method onInit takes nothing returns nothing
set thistype.INSTANCES = Table.create()
endmethod
endmodule
module IndexedStruct
public static integer amount = 0
implement SaveLoadStruct
public method isIndexed takes nothing returns boolean
return thistype.exists(.id)
endmethod
public static method lastKey takes nothing returns integer
return thistype.amount -1
endmethod
public method index takes nothing returns boolean
if .isIndexed() then
return false
endif
set .id = thistype.amount
call .save()
set thistype.amount = thistype.amount +1
return true
endmethod
public method deindex takes nothing returns boolean
local thistype that = 0
if not .isIndexed() then
return false
endif
call .remove()
set thistype.amount = thistype.amount -1
if thistype.amount >= 0 then
set that = thistype.load(thistype.amount)
if that != 0 then
call that.remove()
set that.id = .id
call that.save()
endif
endif
return true
endmethod
endmodule
module PeriodicStruct
public boolean isAlive = false
public boolean isActive = false
public static timer clock = null
implement IndexedStruct
public stub method onPeriod takes nothing returns boolean
return true
endmethod
public method isRegistered takes nothing returns boolean
return .isIndexed()
endmethod
public method register takes nothing returns boolean
if .index() then
if thistype.lastKey() == 0 then
call thistype.startClock()
endif
return true
endif
return false
endmethod
public method unregister takes nothing returns boolean
if .deindex() then
if thistype.lastKey() == -1 then
call thistype.pauseClock()
endif
return true
endif
return false
endmethod
private static method runPeriod takes nothing returns nothing
local integer i = 0
local thistype this = 0
loop
exitwhen i > thistype.lastKey()
set this = thistype.load(i)
set thistype.SELF = this
if .onPeriod() then
set i = i +1
else
call .destroy()
endif
endloop
endmethod
public static method startClock takes nothing returns nothing
call TimerStart(thistype.clock, thistype.period, true, function thistype.runPeriod)
endmethod
public static method pauseClock takes nothing returns nothing
call PauseTimer(thistype.clock)
endmethod
private static method onInit takes nothing returns nothing
set thistype.clock = CreateTimer()
endmethod
endmodule
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library ARGB initializer init
//******************************************************************************
//*
//* ARGB 1.2
//* ====
//* For your color needs.
//*
//* An ARGB object is a by-value struct, this means that assigning copies the
//* contents of the struct and that you don't have to use .destroy(), the
//* downside is that you cannot assign its members (can't do set c.r= 123 )
//*
//* This library should have plenty of uses, for example, if your spell involves
//* some unit recoloring you can allow users to input the color in the config
//* section as 0xAARRGGBB and you can then use this to decode that stuff.
//*
//* You can also easily merge two colors and make fading effects using ARGB.mix
//*
//* There's ARGB.fromPlayer which gets an ARGB object containing the player's
//* color. Then you can use the previous utilities on it.
//*
//* The .str() instance method can recolor a string, and the recolorUnit method
//* will apply the ARGB on a unit
//*
//* For other uses, you can use the .red, .green, .blue and .alpha members to get
//* an ARGB object's color value (from 0 to 255).
//*
//* structs that have a recolor method that takes red,green,blue and alpha as 0.255
//* integers can implement the ARGBrecolor module to gain an ability to quickly
//* recolor using an ARGB object.
//*
//********************************************************************************
//=================================================================================
globals
private string array i2cc
endglobals
//this double naming stuff is beginning to make me insane, if only TriggerEvaluate() wasn't so slow...
struct ARGB extends array
static method create takes integer a, integer r, integer g, integer b returns ARGB
return ARGB(b + g*0x100 + r*0x10000 + a*0x1000000)
endmethod
// not really part of the exported stuff, I may remove it in the future, so please don't call this textmacro
//! textmacro ARGB_PLAYER_COLOR_2_ARGB
if(pc==PLAYER_COLOR_RED) then
return 0xFFFF0303
elseif(pc==PLAYER_COLOR_BLUE) then
return 0xFF0042FF
elseif(pc==PLAYER_COLOR_CYAN) then
return 0xFF1CE6B9
elseif(pc==PLAYER_COLOR_PURPLE) then
return 0xFF540081
elseif(pc==PLAYER_COLOR_YELLOW) then
return 0xFFFFFC01
elseif(pc==PLAYER_COLOR_ORANGE) then
return 0xFFFE8A0E
elseif(pc==PLAYER_COLOR_GREEN) then
return 0xFF20C000
elseif(pc==PLAYER_COLOR_PINK) then
return 0xFFE55BB0
elseif(pc==PLAYER_COLOR_LIGHT_GRAY) then
return 0xFF959697
elseif(pc==PLAYER_COLOR_LIGHT_BLUE) then
return 0xFF7EBFF1
elseif(pc==PLAYER_COLOR_AQUA) then
return 0xFF106246
elseif(pc==PLAYER_COLOR_BROWN) then
return 0xFF4E2A04
endif
return 0xFF111111
//! endtextmacro
static method fromPlayerColor takes playercolor pc returns ARGB
//! runtextmacro ARGB_PLAYER_COLOR_2_ARGB()
endmethod
static method fromPlayer takes player p returns ARGB
local playercolor pc=GetPlayerColor(p)
//! runtextmacro ARGB_PLAYER_COLOR_2_ARGB()
endmethod
method operator alpha takes nothing returns integer
if( integer(this) <0) then
return 0x80+(-(-integer(this)+0x80000000))/0x1000000
else
return (integer(this))/0x1000000
endif
endmethod
method operator alpha= takes integer na returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + g*0x100 + r*0x10000 + na*0x1000000)
endmethod
method operator red takes nothing returns integer
local integer c=integer(this)*0x100
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator red= takes integer nr returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + g*0x100 + nr*0x10000 + a*0x1000000)
endmethod
method operator green takes nothing returns integer
local integer c=integer(this)*0x10000
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator green= takes integer ng returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + ng*0x100 + r*0x10000 + a*0x1000000)
endmethod
//=======================================================
//
//
method operator blue takes nothing returns integer
local integer c=integer(this)*0x1000000
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator blue= takes integer nb returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(nb + g*0x100 + r*0x10000 + a*0x1000000)
endmethod
//====================================================================
// Mixes two colors, s would be a number 0<=s<=1 that determines
// the weight given to color c2.
//
// mix(c1,c2,0) = c1
// mix(c1,c2,1) = c2
// mix(c1,c2,0.5) = Mixing the colors c1 and c2 in equal proportions.
//
static method mix takes ARGB c1, ARGB c2, real s returns ARGB
//widest function ever
return ARGB( R2I(c2.blue*s+c1.blue*(1-s)+0.5) + R2I(c2.green*s+c1.green*(1-s)+0.5)*0x100 + R2I(c2.red*s+c1.red*(1-s)+0.5)*0x10000 + R2I(c2.alpha*s+c1.alpha*(1-s)+0.5)*0x1000000)
endmethod
method str takes string s returns string
return "|c"+i2cc[.alpha]+i2cc[.red]+i2cc[.green]+i2cc[.blue]+s+"|r"
endmethod
method recolorUnit takes unit u returns nothing
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
call SetUnitVertexColor(u,r,g,b,a)
endmethod
endstruct
module ARGBrecolor
method ARGBrecolor takes ARGB color returns nothing
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
call this.recolor(r, g , b, a)
endmethod
endmodule
private function init takes nothing returns nothing
local integer i=0
// Don't run textmacros you don't own!
//! textmacro ARGB_CHAR takes int, chr
set i=0
loop
exitwhen i==16
set i2cc[$int$*16+i]="$chr$"+i2cc[$int$*16+i]
set i2cc[i*16+$int$]=i2cc[i*16+$int$]+"$chr$"
set i=i+1
endloop
//! endtextmacro
//! runtextmacro ARGB_CHAR( "0","0")
//! runtextmacro ARGB_CHAR( "1","1")
//! runtextmacro ARGB_CHAR( "2","2")
//! runtextmacro ARGB_CHAR( "3","3")
//! runtextmacro ARGB_CHAR( "4","4")
//! runtextmacro ARGB_CHAR( "5","5")
//! runtextmacro ARGB_CHAR( "6","6")
//! runtextmacro ARGB_CHAR( "7","7")
//! runtextmacro ARGB_CHAR( "8","8")
//! runtextmacro ARGB_CHAR( "9","9")
//! runtextmacro ARGB_CHAR("10","A")
//! runtextmacro ARGB_CHAR("11","B")
//! runtextmacro ARGB_CHAR("12","C")
//! runtextmacro ARGB_CHAR("13","D")
//! runtextmacro ARGB_CHAR("14","E")
//! runtextmacro ARGB_CHAR("15","F")
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.0
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht = InitHashtable()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this), $key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("String","string","StringHash(key)" )
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Missile requires CustomEffect, Loc, StructUtils
private interface BaseEvents
method onRegister takes nothing returns nothing defaults nothing
method onDeath takes nothing returns nothing defaults nothing
method onLoop takes nothing returns nothing defaults nothing
endinterface
private struct MissileFrame extends BaseEvents
public static real period = 0.03125
implement PeriodicStruct
implement DynamicRegisterStruct
implement DynamicCleanupStruct
implement DynamicPeriodicStruct
endstruct
struct Missile extends MissileFrame
public real height = 0.
private delegate CustomEffect eff = 0
public static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set .eff = CustomEffect.create()
return this
endmethod
public stub method onPeriod takes nothing returns boolean
call .onLoop()
call thistype.invokePeriodic()
return .isAlive
endmethod
public method use takes real x, real y, real z, real f returns nothing
call .eff.use(x, y, z, f)
set .isAlive = true
set .isActive = true
if .register() then
call thistype.invokeRegister()
call .onRegister()
endif
endmethod
private method onDestroy takes nothing returns nothing
call .unregister()
if .eff != 0 then
call .eff.destroy()
set .eff = 0
endif
call .onDeath()
call thistype.invokeCleanup()
endmethod
endstruct
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
module MissileUnitHit
public boolean hitsUnits = true
public real unitHitRange = 0.
private group unitHitGroup = null
private static boolean unitHitSurvive = true
private static group tempHitGroup = null
public static method checkUnitHit takes nothing returns nothing
local thistype this = thistype.SELF
if not .hitsUnits then
return
endif
if .unitHitGroup == null then
set .unitHitGroup = NewGroup()
endif
call GroupEnumUnitsInRange(thistype.tempHitGroup, .x, .y, .unitHitRange, function thistype.delegateUnitHitCheck)
endmethod
private static method delegateUnitHitCheck takes nothing returns boolean
local thistype this = thistype.SELF
local unit theUnit = GetFilterUnit()
local boolean hasHit = .unitHitCond(theUnit)
if hasHit and .isAlive and .isActive then
call GroupAddUnit(.unitHitGroup, theUnit)
if not .onUnitHit(theUnit) then
set .isAlive = false
set .isActive = false
endif
endif
set theUnit = null
return false
endmethod
public method isUnitHit takes unit theUnit returns boolean
return IsUnitInGroup(theUnit, .unitHitGroup)
endmethod
stub method unitHitCond takes unit theUnit returns boolean
local boolean triggerHit = true
set triggerHit = triggerHit and IsPlayerEnemy(.owner, GetOwningPlayer(theUnit))
set triggerHit = triggerHit and GetUnitState(theUnit, UNIT_STATE_LIFE) > 0.405
set triggerHit = triggerHit and not .isUnitHit(theUnit)
return triggerHit
endmethod
stub method onUnitHit takes unit theUnit returns boolean
//: return whether missile should stay alive or not
return thistype.unitHitSurvive
endmethod
private static method clearUnitHit takes nothing returns nothing
local thistype this = thistype.SELF
if .unitHitGroup != null then
call ReleaseGroup(.unitHitGroup)
set .unitHitGroup = null
endif
endmethod
private static method onInit takes nothing returns nothing
call thistype.addPeriodic(function thistype.checkUnitHit)
call thistype.addCleanup(function thistype.clearUnitHit)
set thistype.tempHitGroup = NewGroup()
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
module MissileGroundHit
public boolean hitsGround = true
public boolean hideInGround = true
public boolean inGround = false
private static boolean groundHitSurvive = true
public static method checkGroundHit takes nothing returns nothing
local thistype this = thistype.SELF
local unit effectUnit = null
local boolean checkGround = false
local boolean survive = true
if not .hitsGround or not .isAlive or not .isActive then
return
endif
set effectUnit = .toUnit()
set checkGround = .groundHitCond(.x, .y, .z, .height)
if not checkGround then
if .inGround and .hideInGround then
call ShowUnit(effectUnit, true)
endif
set .inGround = false
else
if not .inGround and .hideInGround then
call ShowUnit(effectUnit, false)
endif
set .inGround = true
set survive = .onGroundHit()
if not survive then
set .isAlive = false
set .isActive = false
endif
endif
endmethod
public method isGroundHit takes real x, real y returns boolean
return .groundHitCond(x, y, .z, .height)
endmethod
stub method groundHitCond takes real x, real y, real z, real h returns boolean
return GetLocZ(x, y) >= z
endmethod
stub method onGroundHit takes nothing returns boolean
//: return whether missile should stay alive or not
return thistype.groundHitSurvive
endmethod
private static method onInit takes nothing returns nothing
call thistype.addPeriodic(function thistype.checkGroundHit)
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
module MissileDestHit
public boolean hitsDests = true
public real destHitRange = 0.
public Table destHitGroup = 0
private rect destCheckRect = null
private static boolean destHitSurvive = true
public static method checkDestHit takes nothing returns nothing
local thistype this = thistype.SELF
local real minX = .x - .destHitRange / 2
local real minY = .y - .destHitRange / 2
local real maxX = .x + .destHitRange / 2
local real maxY = .y + .destHitRange / 2
if not .hitsDests then
return
endif
if .destHitGroup == 0 then
set .destHitGroup = Table.create()
endif
if .destCheckRect == null then
set .destCheckRect = Rect(minX, minY, maxX, maxY)
else
call SetRect(.destCheckRect, minX, minY, maxX, maxY)
endif
call EnumDestructablesInRect(.destCheckRect, function thistype.delegateDestHitCheck, null)
endmethod
private static method delegateDestHitCheck takes nothing returns boolean
local thistype this = thistype.SELF
local destructable theDest = GetFilterDestructable()
local boolean shouldHit = false
local boolean survive = true
if DistBetweenPoints(.x, .y, GetDestructableX(theDest), GetDestructableY(theDest)) <= .destHitRange then
set shouldHit = .destHitCond(theDest)
endif
if shouldHit and .isAlive and .isActive then
set .destHitGroup[GetHandleId(theDest)] = 1
set survive = .onDestHit(theDest)
if not survive then
set .isAlive = false
set .isActive = false
endif
endif
set theDest = null
return false
endmethod
public method isDestHit takes destructable theDest returns boolean
return .destHitGroup[GetHandleId(theDest)] == 1
endmethod
stub method destHitCond takes destructable theDest returns boolean
local boolean triggerHit = true
set triggerHit = triggerHit and not .isDestHit(theDest)
return triggerHit
endmethod
stub method onDestHit takes destructable theDest returns boolean
//: return whether missile should stay alive or not
return thistype.destHitSurvive
endmethod
private static method clearDestHit takes nothing returns nothing
local thistype this = thistype.SELF
if .destHitGroup != 0 then
call .destHitGroup.reset()
call .destHitGroup.destroy()
set .destHitGroup = 0
endif
endmethod
private static method onInit takes nothing returns nothing
call thistype.addPeriodic(function thistype.checkDestHit)
call thistype.addCleanup(function thistype.clearDestHit)
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//: Warning: Not implemented yet!
module MissileCollideHit
public boolean hitsMissiles = true
public real missileHitRange = 0.
public Table missileHitGroup = 0
private static boolean missileHitSurvive = false
public method checkMissileHit takes nothing returns nothing
endmethod
public method isMissileHit takes CustomMissile theMissile returns boolean
return .missileHitGroup[theMissile] == 1
endmethod
stub method missileHitCond takes CustomMissile theMissile returns boolean
local boolean triggerHit = true
set triggerHit = triggerHit and theMissile.isAlive and theMissile.isActive
set triggerHit = triggerHit and not .isMissileHit(theMissile)
return triggerHit
endmethod
stub method onMissileHit takes CustomMissile theMissile returns boolean
//: return whether missile should stay alive or not
return thistype.missileHitSurvive
endmethod
private static method clearMissileHit takes nothing returns nothing
if .missileHitGroup != 0 then
call .missileHitGroup.reset()
call .missileHitGroup.destroy()
set .missileHitGroup = 0
endif
endmethod
private static method onInit takes nothing returns nothing
call thistype.addPeriodic(function thistype.checkMissileHit)
call thistype.addCleanup(function thistype.clearMissileHit)
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
module MissileDecay
public real decay = 0.
public static method checkDecay takes nothing returns nothing
local thistype this = thistype.SELF
if .decay <= 0. then
set .isAlive = false
set .isActive = false
else
set .decay = .decay - thistype.period
endif
endmethod
private static method onInit takes nothing returns nothing
call thistype.addPeriodic(function thistype.checkDecay)
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library MissileMovement requires Loc
function GetHeightChange takes Loc firstLoc, Loc secLoc, real speed, real period returns real
return (speed / firstLoc.distanceTo(secLoc) * (secLoc.z - firstLoc.z)) * period
endfunction
function GetZAngle takes real x1, real y1, real z1, real x2, real y2, real z2 returns real
return Atan2(z2 - z1, SquareRoot((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1)))
endfunction
function GetXYArcDistance takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)
endfunction
//: y0 = starting height
//: y1 = end height
//: h = max height
//: d = max distance
//: x = current distance
function GetZArcHeight takes real y0, real y1, real h, real d, real x returns real
//: Thanks to The_Reborn_Devil, he's a mathbeast
return ((2*(y0+y1)-4*h)/(d*d))*x*x + ((y1-y0-((2*(y0+y1)-4*h)/(d*d))*d*d)/d)*x + y0
endfunction
function GetZArcMaxHeight takes real startZ, real targZ, real maxDist, real arc returns real
return ((startZ + targZ) / 2) + (maxDist * arc)
endfunction
endlibrary
module MissileMovement
public real moveSpeed = 1000.
public real turnSpeed = -1.
public real offsetX = 0.
public real offsetY = 0.
public real offsetZ = 0.
public real offsetF = 0.
public real offsetXYAngle = 0.
public real offsetZAngle = 0.
public real xySpeed = 0.
public real zSpeed = 0.
public real xyArc = 0.
public real zArc = 0.
public boolean moveXY = true
public boolean moveZ = true
public boolean changeXYAngle = true
public boolean changeZAngle = true
public boolean waitForXYAngle = false
public boolean waitForZAngle = false
public Loc startLoc = 0
public static Loc CUR_LOC = 0
public static Loc NEXT_LOC = 0
public static real RAD_PER_PERIOD = 0.
public method execMove takes Loc targLoc returns nothing
local real curXYAngle = makeRealAngle(.f)
local real newXYAngle = curXYAngle
local real turnAround = thistype.RAD_PER_PERIOD / .turnSpeed
local real targXYAngle = 0
local real curDistance = 0
local real maxDistance = 0
local real maxHeight = 0.
local boolean doWaitForXYAngle = false
local boolean doWaitForZ = false
call thistype.CUR_LOC.applyLoc(.loc)
call thistype.NEXT_LOC.applyLoc(.loc)
set targXYAngle = makeRealAngle(thistype.CUR_LOC.angleTo(targLoc))
set curDistance = thistype.CUR_LOC.distanceTo(.startLoc)
set maxDistance = .startLoc.distanceTo(targLoc)
if .changeXYAngle then
if .turnSpeed == -1. then
set newXYAngle = targXYAngle
elseif curXYAngle < targXYAngle then
set newXYAngle = curXYAngle + turnAround
if newXYAngle > targXYAngle then
set newXYAngle = targXYAngle
endif
if .waitForXYAngle then
set doWaitForXYAngle = newXYAngle < targXYAngle
endif
elseif curXYAngle > targXYAngle then
set newXYAngle = curXYAngle - turnAround
if newXYAngle < targXYAngle then
set newXYAngle = targXYAngle
endif
if .waitForXYAngle then
set doWaitForXYAngle = newXYAngle > targXYAngle
endif
endif
endif
set thistype.NEXT_LOC.f = newXYAngle
set .f = newXYAngle
if .moveXY and not doWaitForXYAngle and R2I(curDistance) != R2I(maxDistance) then
call thistype.NEXT_LOC.move(.moveSpeed * thistype.period)
set .x = thistype.NEXT_LOC.x
set .y = thistype.NEXT_LOC.y
if .xyArc != 0. then
call thistype.NEXT_LOC.moveFaced(GetXYArcDistance(maxDistance * .xyArc, maxDistance, curDistance), targXYAngle + 1.571) // 90deg to rad
call .applyXOnce(thistype.NEXT_LOC.x)
call .applyYOnce(thistype.NEXT_LOC.y)
call .applyFOnce(makeRealAngle(thistype.NEXT_LOC.angleTo(targLoc)))
endif
endif
if .moveZ then
if .zArc == 0. then
set thistype.NEXT_LOC.z = thistype.NEXT_LOC.z + GetHeightChange(thistype.NEXT_LOC, targLoc, .moveSpeed, thistype.period)
else
set maxHeight = GetZArcMaxHeight(.startLoc.z, targLoc.z, maxDistance, .zArc)
set thistype.NEXT_LOC.z = GetZArcHeight(.startLoc.z, targLoc.z, maxHeight, maxDistance, curDistance)
endif
set .z = thistype.NEXT_LOC.z
endif
if .changeZAngle and not doWaitForZ then
set .zAngle = GetZAngle(thistype.CUR_LOC.x, thistype.CUR_LOC.y, thistype.CUR_LOC.z, thistype.NEXT_LOC.x, thistype.NEXT_LOC.y, thistype.NEXT_LOC.z)
endif
endmethod
private static method onInit takes nothing returns nothing
set thistype.CUR_LOC = Loc.create(0., 0., 0., 0.)
set thistype.NEXT_LOC = Loc.create(0., 0., 0., 0.)
set thistype.RAD_PER_PERIOD = bj_PI * 2 * thistype.period
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
module MissileUnitTarget
public boolean hasUnitTarget = false
public real unitTargetHitRange = 0.
public boolean checkUnitTargetHeight = false
public unit unitTarget = null
private static Loc LOC_TARGET = 0
implement optional MissileMovement
public method fire takes Loc theStart, unit theTarget returns nothing
set .startLoc = theStart.clone()
set .unitTarget = theTarget
call .use(.startLoc.x, .startLoc.y, .startLoc.z, .startLoc.f)
endmethod
private static method move takes nothing returns nothing
local thistype this = thistype.SELF
local real targX = GetUnitX(.unitTarget) + .offsetX
local real targY = GetUnitY(.unitTarget) + .offsetY
local real targZ = GetLocZ(targX, targY) + GetUnitFlyHeight(.unitTarget) + .offsetZ
local real targF = (GetUnitFacing(.unitTarget) * bj_DEGTORAD) + .offsetF
local boolean hitsTarget = false
call thistype.LOC_TARGET.applyNew(targX, targY, targZ, targF)
call .execMove(thistype.LOC_TARGET)
if .checkUnitTargetHeight then
set hitsTarget = DistBetweenUnitsZ(.toUnit(), .unitTarget) <= .unitTargetHitRange
else
set hitsTarget = DistBetweenUnits(.toUnit(), .unitTarget) <= .unitTargetHitRange
endif
if hitsTarget then
if not .onUnitTargetHit() then
set .isAlive = false
set .isActive = false
endif
endif
endmethod
stub method onUnitTargetHit takes nothing returns boolean
return false
endmethod
private static method cleanupLocTarget takes nothing returns nothing
local thistype this = thistype.SELF
set .unitTarget = null
if .startLoc != 0 then
call .startLoc.destroy()
set .startLoc = 0
endif
endmethod
public static method onInit takes nothing returns nothing
set thistype.LOC_TARGET = Loc.create(0., 0., 0., 0.)
call thistype.addPeriodic(function thistype.move)
call thistype.addCleanup(function thistype.cleanupLocTarget)
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
module MissileLocTarget
public boolean hasLocTarget = false
public real locTargetHitRange = 0.
public boolean checkLocTargetHeight = false
public Loc locTarget = 0
implement optional MissileMovement
public method fire takes Loc theStart, Loc theTarget returns nothing
set .startLoc = theStart.clone()
set .locTarget = theTarget.clone()
call .use(.startLoc.x, .startLoc.y, .startLoc.z, .startLoc.f)
endmethod
private static method move takes nothing returns nothing
local thistype this = thistype.SELF
local boolean hitsTarget = false
call .execMove(.locTarget)
if .checkLocTargetHeight then
set hitsTarget = .locTarget.distanceToZ(.loc) <= .locTargetHitRange
else
set hitsTarget = .locTarget.distanceTo(.loc) <= .locTargetHitRange
endif
if hitsTarget then
if not .onLocTargetHit() then
set .isAlive = false
set .isActive = false
endif
endif
endmethod
stub method onLocTargetHit takes nothing returns boolean
return false
endmethod
private static method cleanupLocTarget takes nothing returns nothing
local thistype this = thistype.SELF
if .startLoc != 0 then
call .startLoc.destroy()
set .startLoc = 0
endif
if .locTarget != 0 then
call .locTarget.destroy()
set .locTarget = 0
endif
endmethod
public static method onInit takes nothing returns nothing
call thistype.addPeriodic(function thistype.move)
call thistype.addCleanup(function thistype.cleanupLocTarget)
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
module MissileBounds
private static real MIN_X = GetRectMinX(bj_mapInitialPlayableArea)
private static real MIN_Y = GetRectMinY(bj_mapInitialPlayableArea)
private static real MAX_X = GetRectMaxX(bj_mapInitialPlayableArea)
private static real MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea)
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
module MissileCastHelper
public static trigger CAST_TRIG = null
private static method castCond takes nothing returns boolean
return GetSpellAbilityId() == thistype.ABILITY_ID
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
set thistype.CAST_TRIG = CreateTrigger()
call TriggerAddCondition(thistype.CAST_TRIG, Condition(function thistype.castCond))
call TriggerAddAction(thistype.CAST_TRIG, function thistype.onCast)
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
call TriggerRegisterPlayerUnitEvent(thistype.CAST_TRIG, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, null)
set i = i +1
endloop
endmethod
endmodule
module MissileCastEndHelper
public static trigger ENDCAST_TRIG = null
private static method castEndCond takes nothing returns boolean
return GetSpellAbilityId() == thistype.ABILITY_ID
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
set thistype.ENDCAST_TRIG = CreateTrigger()
call TriggerAddCondition(thistype.ENDCAST_TRIG, Condition(function thistype.castEndCond))
call TriggerAddAction(thistype.ENDCAST_TRIG, function thistype.onCastEnd)
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
call TriggerRegisterPlayerUnitEvent(thistype.ENDCAST_TRIG, Player(i), EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
set i = i +1
endloop
endmethod
endmodule
module MissileBuffHelper
//public static integer BUFF_ID = 0
//public static string BUFF_ORDER = ""
public unit dummy = null
public method createDummy takes integer theLevel returns nothing
set .dummy = CreateUnit(GetOwningPlayer(.toUnit()), EFFECT_UNIT_TYPE, .x, .y, 0.)
call UnitAddAbility(.dummy, 'Aloc')
call UnitAddAbility(.dummy, thistype.BUFF_ID)
call SetUnitAbilityLevel(.dummy, thistype.BUFF_ID, theLevel)
endmethod
public method castDummy takes unit theUnit returns nothing
call SetUnitX(.dummy, GetUnitX(theUnit))
call SetUnitY(.dummy, GetUnitY(theUnit))
call IssueTargetOrder(.dummy, thistype.BUFF_ORDER, theUnit)
endmethod
private static method clearDummy takes nothing returns nothing
local thistype this = thistype.SELF
call RemoveUnit(.dummy)
endmethod
private static method onInit takes nothing returns nothing
call thistype.addCleanup(function thistype.clearDummy)
endmethod
endmodule
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope ParalyzingWave
private struct dm extends Missile
implement MissileLocTarget
implement MissileUnitHit
implement MissileGroundHit
endstruct
private struct msl extends dm
private static integer BUFF_ID = 'pssl'
private static string BUFF_ORDER = "slow"
public real dmg = 0.
public unit caster = null
implement MissileBuffHelper
method onRegister takes nothing returns nothing
set .owner = GetOwningPlayer(.caster)
set .sfx = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl"
set .scale = 2.
set .moveSpeed = 700.
set .turnSpeed = .15
set .unitHitRange = 125.
set .height = 50.
set .locTargetHitRange = 50.
endmethod
method onUnitHit takes unit theUnit returns boolean
call UnitDamageTarget(.caster, theUnit, .dmg, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS)
call .castDummy(theUnit)
return true
endmethod
endstruct
private struct spell
private static integer ABILITY_ID = 'plsh'
implement MissileCastHelper
private static method onCast takes nothing returns nothing
local msl m = msl.create()
local unit u = GetTriggerUnit()
local Loc target = Loc.fromXY(GetSpellTargetX(), GetSpellTargetY())
local Loc source = Loc.fromUnit(u)
local integer lvl = GetUnitAbilityLevel(u, thistype.ABILITY_ID)
local real face = source.angleTo(target)
set m.caster = u
set m.dmg = 50 + lvl * 50
set source.z = GetLocZ(source.x, source.y) + 50.
set m.moveZ = false // dont set unit z coordinate after initial setting
call target.applyLoc(source)
call target.moveFaced(1200., face)
set target.z = GetLocZ(target.x, target.y) + 50.
set source.f = face
call m.fire(source, target)
call m.createDummy(lvl)
call source.destroy()
call target.destroy()
endmethod
endstruct
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope RainOfFire //requires Missile, Table, StructUtils, Loc, MissileSpellHelper
private struct dm extends Missile
implement MissileUnitHit
endstruct
private struct msl extends dm
public real dmg = 0.
public unit caster = null
public method onUnitHit takes unit theUnit returns boolean
call UnitDamageTarget(.caster, theUnit, .dmg, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS)
return true
endmethod
public method onPeriod takes nothing returns boolean
if GetLocZ(.x, .y) >= .z then
call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\LavaSpawnMissile\\LavaSpawnBirthMissile.mdl", .x, .y))
set .hitsUnits = true
call .checkUnitHit()
return false
endif
set .z = .z - 1200 * thistype.period
return true
endmethod
method onRegister takes nothing returns nothing
set .owner = GetOwningPlayer(.caster)
set .sfx = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
set .hitsUnits = false
set .unitHitRange = 125.
set .height = 50.
endmethod
endstruct
private struct dummySpell
private static real period = 1.
implement PeriodicStruct
endstruct
private struct spell extends dummySpell
private static integer ABILITY_ID = 'roar'
private static Table MAP = 0
private static thistype WAVESIZE = 5
private unit caster = null
private integer level = 0
private Loc target = 0
implement MissileCastHelper
implement MissileCastEndHelper
public method onPeriod takes nothing returns boolean
local msl m = 0
local integer i = 0
local Loc l = Loc.create(0., 0., 0., 0.)
loop
exitwhen i >= WAVESIZE
set m = msl.create()
set m.dmg = 20 + .level * 10
set m.caster = .caster
call l.applyLoc(.target)
call l.moveFaced(125., 360. / WAVESIZE * i * bj_DEGTORAD)
call m.use(l.x, l.y, l.z + 2000., l.f)
set i = i +1
endloop
set m = msl.create()
set m.dmg = 20 + .level * 10
set m.caster = .caster
call m.use(.target.x, .target.y, .target.z + 2000., .target.f)
call l.destroy()
return true
endmethod
private static method onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer id = GetHandleId(u)
local thistype this = thistype.create()
set thistype.MAP[id] = this
call this.register()
set .target = Loc.fromXY(GetSpellTargetX(), GetSpellTargetY())
set .level = GetUnitAbilityLevel(u, thistype.ABILITY_ID)
set .caster = u
set u = null
//call BJDebugMsg("RainOfFire: onCast")
endmethod
private static method onCastEnd takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer id = GetHandleId(u)
local thistype this = 0
//call BJDebugMsg("RainOfFire: onCastEnd")
if thistype.MAP.exists(id) then
//call BJDebugMsg("RainOfFire: onCastEnd --> found instance")
set this = thistype.MAP[id]
call thistype.MAP.flush(id)
call .destroy()
endif
set u = null
endmethod
private method onDestroy takes nothing returns nothing
if .target != 0 then
call .target.destroy()
set .target = 0
endif
call .unregister()
endmethod
private static method onInit takes nothing returns nothing
set thistype.MAP = Table.create()
endmethod
endstruct
endscope