Name | Type | is_array | initial_value |
Hero | unit | No | |
test | integer | No | 0 |
//TESH.scrollpos=269
//TESH.alwaysfold=0
//******************************************************************************
//* *
//* K N O C K B A C K *
//* Actual Code *
//* v1.07 *
//* *
//* By: Rising_Dusk *
//* *
//******************************************************************************
library Knockback initializer Init needs TerrainPathability, GroupUtils, UnitIndexingUtils, LastOrder
globals
//*********************************************************
//* These are the configuration constants for the system
//*
//* EFFECT_ATTACH_POINT: Where on the unit the effect attaches
//* EFFECT_PATH_WATER: What special effect to attach over water
//* EFFECT_PATH_GROUND: What special effect to attach over ground
//* DEST_RADIUS: Radius around which destructs die
//* DEST_RADIUS_SQUARED: Radius squared around which destructs die
//* ADJACENT_RADIUS: Radius for knocking back adjacent units
//* ADJACENT_FACTOR: Factor for collision speed transfers
//* TIMER_INTERVAL: The interval for the timer that gets run
//* ISSUE_LAST_ORDER: A boolean to issue last orders or not
//*
private constant string EFFECT_ATTACH_POINT = "origin"
private constant string EFFECT_PATH_WATER = "MDX\\KnockbackWater.mdx"
private constant string EFFECT_PATH_GROUND = "MDX\\KnockbackDust.mdx"
private constant real DEST_RADIUS = 180.
private constant real DEST_RADIUS_SQUARED = DEST_RADIUS*DEST_RADIUS
private constant real ADJACENT_RADIUS = 180.
private constant real ADJACENT_FACTOR = 0.75
private constant real TIMER_INTERVAL = 0.05
private constant boolean ISSUE_LAST_ORDER = true
//*********************************************************
//* These are static constants used by the system and shouldn't be changed
//*
//* Timer: The timer that runs all of the effects for the spell
//* Counter: The counter for how many KB instances exist
//* HitIndex: Indexes for a given unit's knockback
//* Knockers: The array of all struct instances that exist
//* Entries: Counters for specific unit instances in system
//* ToClear: How many instances to remove on next run
//* DesBoolexpr: The check used for finding destructables
//* AdjBoolexpr: The check for picking adjacent units to knockback
//* DestRect: The rect used to check for destructables
//*
private timer Timer = CreateTimer()
private integer Counter = 0
private integer array HitIndex
private integer array Knockers
private integer array Entries
private integer array ToClear
private boolexpr DesBoolexpr = null
private boolexpr AdjBoolexpr = null
private rect DestRect = Rect(0,0,1,1)
//* Temporary variables used by the system
private real TempX = 0.
private real TempY = 0.
private unit TempUnit1 = null
private unit TempUnit2 = null
endglobals
//* Boolean for whether or not to display effects on a unit
private function ShowEffects takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
//* Functions for the destructable destruction
private function KillDests_Check takes nothing returns boolean
local real x = GetDestructableX(GetFilterDestructable())
local real y = GetDestructableY(GetFilterDestructable())
return (TempX-x)*(TempX-x) + (TempY-y)*(TempY-y) <= DEST_RADIUS_SQUARED
endfunction
private function KillDests takes nothing returns nothing
call KillDestructable(GetEnumDestructable())
endfunction
//* Functions for knocking back adjacent units
private function KnockAdj_Check takes nothing returns boolean
return TempUnit2 != GetFilterUnit() and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempUnit1)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(), 'Avul') <= 0
endfunction
//******************************************************************************
//* Some additional functions that can be used
function KnockbackStop takes unit targ returns boolean
local integer id = GetUnitId(targ)
set ToClear[id] = Entries[id]
return ToClear[id] > 0
endfunction
function IsKnockedBack takes unit targ returns boolean
return Entries[GetUnitId(targ)] > 0
endfunction
//* Struct for the system, I recommend leaving it alone
private struct knocker
unit Source = null
unit Target = null
group HitGroup = null
effect KBEffect = null
integer FXMode = 0
boolean KillDest = false
boolean KnockAdj = false
boolean ChainAdj = false
boolean ShowEff = false
real Decrement = 0.
real Displace = 0.
real CosA = 0.
real SinA = 0.
public method checkterrain takes knocker n returns integer
local integer i = 0
local real x = GetUnitX(n.Target)
local real y = GetUnitY(n.Target)
if IsTerrainPathingType(x, y, TERRAIN_PATHING_LAND) then
set i = 1
elseif IsTerrainPathingType(x, y, TERRAIN_PATHING_SHALLOW) then
set i = 2
endif
return i
endmethod
static method create takes unit source, unit targ, real angle, real disp, real dec, boolean killDestructables, boolean knockAdjacent, boolean chainAdjacent returns knocker
local knocker n = knocker.allocate()
set n.Target = targ
set n.Source = source
set n.FXMode = n.checkterrain(n)
set n.HitGroup = NewGroup()
set n.KillDest = killDestructables
set n.KnockAdj = knockAdjacent
set n.ChainAdj = chainAdjacent
set n.ShowEff = ShowEffects(targ)
set n.Decrement = dec
set n.Displace = disp
set n.CosA = Cos(angle)
set n.SinA = Sin(angle)
if n.ShowEff then
if n.FXMode == 1 then
set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_GROUND, n.Target, EFFECT_ATTACH_POINT)
elseif n.FXMode == 2 then
set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_WATER, n.Target, EFFECT_ATTACH_POINT)
debug else
debug call BJDebugMsg(SCOPE_PREFIX+" Error (On Create): Unknown Terrain Type")
endif
endif
return n
endmethod
private method onDestroy takes nothing returns nothing
local integer id = GetUnitId(this.Target)
set Entries[id] = Entries[id] - 1
if GetWidgetLife(this.Target) > 0.405 and Entries[id] <= 0 and ISSUE_LAST_ORDER then
//* Issue last order if activated
call IssueLastOrder(this.Target)
endif
if this.ShowEff then
//* Destroy effect if it exists
call DestroyEffect(this.KBEffect)
endif
call ReleaseGroup(this.HitGroup)
endmethod
endstruct
private function Update takes nothing returns nothing
local unit u = null
local unit s = null
local rect r = null
local knocker n = 0
local knocker m = 0
local integer i = Counter - 1
local integer j = 0
local integer mode = 0
local integer id = 0
local real xi = 0.
local real yi = 0.
local real xf = 0.
local real yf = 0.
loop
exitwhen i < 0
set n = Knockers[i]
set u = n.Target
set mode = n.FXMode
set id = GetUnitId(u)
set xi = GetUnitX(u)
set yi = GetUnitY(u)
if n.Displace <= 0 or ToClear[id] > 0 then
//* Clean up the knockback when it is over
if ToClear[id] > 0 then
set ToClear[id] = ToClear[id] - 1
endif
call n.destroy()
set Counter = Counter - 1
if Counter < 0 then
call PauseTimer(Timer)
set Counter = 0
else
set Knockers[i] = Knockers[Counter]
endif
else
//* Propagate the knockback in space and time
set xf = xi + n.Displace*n.CosA
set yf = yi + n.Displace*n.SinA
call SetUnitPosition(u, xf, yf)
set n.FXMode = n.checkterrain(n)
//* Modify the special effect if necessary
if n.ShowEff then
if n.FXMode == 1 and mode == 2 then
call DestroyEffect(n.KBEffect)
set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_GROUND, n.Target, EFFECT_ATTACH_POINT)
elseif n.FXMode == 2 and mode == 1 then
call DestroyEffect(n.KBEffect)
set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_WATER, n.Target, EFFECT_ATTACH_POINT)
debug elseif n.FXMode == 0 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error (In Update): Unknown Terrain Type")
endif
endif
//* Decrement displacement left to go
set n.Displace = n.Displace - n.Decrement
//* Destroy destructables if desired
if n.KillDest then
set TempX = GetUnitX(u)
set TempY = GetUnitY(u)
call MoveRectTo(DestRect, TempX, TempY)
call EnumDestructablesInRect(DestRect, DesBoolexpr, function KillDests)
endif
//* Knockback nearby units if desired
if n.KnockAdj then
set xi = GetUnitX(u)
set yi = GetUnitY(u)
set TempUnit1 = n.Source
set TempUnit2 = u
call GroupEnumUnitsInRange(ENUM_GROUP, xi, yi, ADJACENT_RADIUS, AdjBoolexpr)
loop
set s = FirstOfGroup(ENUM_GROUP)
exitwhen s == null
if not IsUnitInGroup(s, n.HitGroup) then
set xf = GetUnitX(s)
set yf = GetUnitY(s)
call GroupAddUnit(n.HitGroup, s)
set m = knocker.create(n.Source, s, Atan2(yf-yi, xf-xi), n.Displace*ADJACENT_FACTOR, n.Decrement, n.KillDest, n.ChainAdj, n.ChainAdj)
call GroupAddUnit(m.HitGroup, u)
set Knockers[Counter] = m
set Counter = Counter + 1
endif
call GroupRemoveUnit(ENUM_GROUP, s)
endloop
endif
endif
set i = i - 1
endloop
set u = null
set s = null
endfunction
//******************************************************************************
//* How to knockback a unit
function KnockbackTarget takes unit source, unit targ, real angle, real startspeed, real decrement, boolean killDestructables, boolean knockAdjacent, boolean chainAdjacent returns boolean
local knocker n = 0
local integer id = GetUnitId(targ)
local boolean b = true
//* Protect users from themselves
if decrement <= 0. or startspeed <= 0. or targ == null then
debug call BJDebugMsg(SCOPE_PREFIX+" Error (On Call): Invalid Starting Conditions")
set b = false
else
//* Can't chain if you don't knockback adjacent units
if not knockAdjacent and chainAdjacent then
set chainAdjacent = false
endif
set n = knocker.create(source, targ, angle*bj_DEGTORAD, startspeed*TIMER_INTERVAL, decrement*TIMER_INTERVAL*TIMER_INTERVAL, killDestructables, knockAdjacent, chainAdjacent)
if Counter == 0 then
call TimerStart(Timer, TIMER_INTERVAL, true, function Update)
endif
set Entries[id] = Entries[id] + 1
set HitIndex[id] = Counter + 1
set Knockers[Counter] = n
set Counter = Counter + 1
endif
return b
endfunction
private function Init takes nothing returns nothing
call SetRect(DestRect, -DEST_RADIUS, -DEST_RADIUS, DEST_RADIUS, DEST_RADIUS)
set DesBoolexpr = Condition(function KillDests_Check)
set AdjBoolexpr = Condition(function KnockAdj_Check)
endfunction
endlibrary
//TESH.scrollpos=46
//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
// 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 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
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
endlibrary
//TESH.scrollpos=198
//TESH.alwaysfold=0
library LastOrder initializer Init needs UnitIndexingUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library has a lot of usefulness for when you want to interface with the
//* last order a unit was given. This can be useful for simulating spell errors
//* and where you'd want to give them back the order they had prior to the spell
//* cast (whereas without this library, they'd just forget their orders).
//*
//* There are some handy interfacing options for your use here --
//* function GetLastOrderId takes unit u returns integer
//* function GetLastOrderString takes unit u returns string
//* function GetLastOrderType takes unit u returns integer
//* function GetLastOrderX takes unit u returns real
//* function GetLastOrderY takes unit u returns real
//* function GetLastOrderTarget takes unit u returns widget
//* function AbortOrder takes unit u returns boolean
//*
//* There are also some order commands that can be useful --
//* function IssueLastOrder takes unit u returns boolean
//* function IssueSecondLastOrder takes unit u returns boolean
//* function IsLastOrderFinished takes unit u returns boolean
//*
//* You can access any information you'd like about the orders for your own
//* order handling needs.
//*
globals
//* Storage for last order
private integer array Order
private integer array Type
private widget array Targ
private boolean array Flag
private real array X
private real array Y
//* Storage for second last order
private integer array P_Order
private integer array P_Type
private widget array P_Targ
private boolean array P_Flag
private real array P_X
private real array P_Y
//* Order type variables
constant integer ORDER_TYPE_TARGET = 1
constant integer ORDER_TYPE_POINT = 2
constant integer ORDER_TYPE_IMMEDIATE = 3
//* Trigger for the order catching
private trigger OrderTrg = CreateTrigger()
endglobals
//**********************************************************
function GetLastOrderId takes unit u returns integer
return Order[GetUnitId(u)]
endfunction
function GetLastOrderString takes unit u returns string
return OrderId2String(Order[GetUnitId(u)])
endfunction
function GetLastOrderType takes unit u returns integer
return Type[GetUnitId(u)]
endfunction
function GetLastOrderX takes unit u returns real
return X[GetUnitId(u)]
endfunction
function GetLastOrderY takes unit u returns real
return Y[GetUnitId(u)]
endfunction
function GetLastOrderTarget takes unit u returns widget
return Targ[GetUnitId(u)]
endfunction
//**********************************************************
private function OrderExclusions takes unit u, integer id returns boolean
//* Excludes specific orders or unit types from registering with the system
//*
//* 851972: stop
//* Stop is excluded from the system, but you can change it by
//* adding a check for it below. id == 851972
//*
//* 851971: smart
//* 851986: move
//* 851983: attack
//* 851984: attackground
//* 851990: patrol
//* 851993: holdposition
//* These are the UI orders that are passed to the system.
//*
//* >= 852055, <= 852762
//* These are all spell IDs from defend to incineratearrowoff with
//* a bit of leeway at the ends for orders with no strings.
//*
return id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or id == 851993 or (id >= 852055 and id <= 852762)
endfunction
private function LastOrderFilter takes unit u returns boolean
//* Some criteria for whether or not a unit's last order should be given
//*
//* INSTANT type orders are excluded because generally, reissuing an instant
//* order doesn't make sense. You can remove that check below if you'd like,
//* though.
//*
//* The Type check is really just to ensure that no spell recursion can
//* occur with IssueLastOrder. The problem with intercepting the spell cast
//* event is that it happens after the order is 'caught' and registered to
//* this system. Therefore, to just IssueLastOrder tells it to recast the
//* spell! That's a problem, so we need a method to eliminate it.
//*
local integer id = GetUnitId(u)
return u != null and GetWidgetLife(u) > 0.405 and Type[id] != ORDER_TYPE_IMMEDIATE
endfunction
private function SecondLastOrderFilter takes unit u returns boolean
//* Same as above but with regard to the second last order issued
local integer id = GetUnitId(u)
return u != null and GetWidgetLife(u) > 0.405 and P_Type[id] != ORDER_TYPE_IMMEDIATE and P_Order[id] != Order[id]
endfunction
//**********************************************************
function IsLastOrderFinished takes unit u returns boolean
return (GetUnitCurrentOrder(u) == 0 and Order[GetUnitId(u)] != 851972) or Flag[GetUnitId(u)]
endfunction
function IssueLastOrder takes unit u returns boolean
local integer id = GetUnitId(u)
local boolean b = false
if LastOrderFilter(u) and Order[id] != 0 and not Flag[id] then
if Type[id] == ORDER_TYPE_TARGET then
set b = IssueTargetOrderById(u, Order[id], Targ[id])
elseif Type[id] == ORDER_TYPE_POINT then
set b = IssuePointOrderById(u, Order[id], X[id], Y[id])
elseif Type[id] == ORDER_TYPE_IMMEDIATE then
set b = IssueImmediateOrderById(u, Order[id])
endif
endif
return b
endfunction
function IssueSecondLastOrder takes unit u returns boolean
//* This function has to exist because of spell recursion
local integer id = GetUnitId(u)
local boolean b = false
if SecondLastOrderFilter(u) and P_Order[id] != 0 and not P_Flag[id] then
if P_Type[id] == ORDER_TYPE_TARGET then
set b = IssueTargetOrderById(u, P_Order[id], P_Targ[id])
elseif P_Type[id] == ORDER_TYPE_POINT then
set b = IssuePointOrderById(u, P_Order[id], P_X[id], P_Y[id])
elseif P_Type[id] == ORDER_TYPE_IMMEDIATE then
set b = IssueImmediateOrderById(u, P_Order[id])
endif
endif
return b
endfunction
function AbortOrder takes unit u returns boolean
local boolean b = true
if IsUnitPaused(u) then
set b = false
else
call PauseUnit(u, true)
call IssueImmediateOrder(u, "stop")
call PauseUnit(u, false)
endif
return b
endfunction
//**********************************************************
private function Conditions takes nothing returns boolean
return OrderExclusions(GetTriggerUnit(), GetIssuedOrderId())
endfunction
private function Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer id = GetUnitId(u)
//* Store second to last order to eliminate spell recursion
set P_Order[id] = Order[id]
set P_Targ[id] = Targ[id]
set P_Type[id] = Type[id]
set P_Flag[id] = Flag[id]
set P_X[id] = X[id]
set P_Y[id] = Y[id]
set Flag[id] = false
set Order[id] = GetIssuedOrderId()
if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
set Targ[id] = GetOrderTarget()
set Type[id] = ORDER_TYPE_TARGET
set X[id] = GetWidgetX(GetOrderTarget())
set Y[id] = GetWidgetY(GetOrderTarget())
elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
set Targ[id] = null
set Type[id] = ORDER_TYPE_POINT
set X[id] = GetOrderPointX()
set Y[id] = GetOrderPointY()
elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
set Targ[id] = null
set Type[id] = ORDER_TYPE_IMMEDIATE
set X[id] = GetUnitX(u)
set Y[id] = GetUnitY(u)
debug else
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Order Doesn't Exist")
endif
set u = null
endfunction
//**********************************************************
private function SpellActions takes nothing returns nothing
set Flag[GetUnitId(GetTriggerUnit())] = true
endfunction
//**********************************************************
private function Init takes nothing returns nothing
local trigger trg = CreateTrigger()
call TriggerAddAction(OrderTrg, function Actions)
call TriggerAddCondition(OrderTrg, Condition(function Conditions))
call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerAddAction(trg, function SpellActions)
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
set trg = null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitIndexingUtils initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* -: RED FLAVOR :-
//*
//* This can be used to index units with a unique integer for use with arrays
//* and things like that. This has a limit of 8191 indexes allocated at once in
//* terms of actually being usable in arrays. It won't give you an error if you
//* exceed 8191, but that is an unrealistic limit anyways.
//*
//* The red flavor uses a periodic timer to automatically recycle a unit's index
//* when UnitUserData goes to 0 for that unit (when it is removed from the game).
//* This can be slower than the blue flavor if you have many units on the map at
//* a time because it uses an O(n) search, but it automatically recycles indexes
//* for units that get removed from the game by decaying or RemoveUnit. It will
//* run the timer for COUNT_PER_ITERATION units before ending. The timer will
//* pick up where it left off on the next run-through.
//*
//* To use, call GetUnitId on a unit to retrieve its unique integer id. This
//* library allocates a unique index to a unit the instant it is created, which
//* means you can call GetUnitId immediately after creating the unit with no
//* worry.
//*
//* Function Listing --
//* function GetUnitId takes unit u returns integer
//*
globals
private constant real TIMER_PERIODICITY = 5.
private constant integer COUNT_PER_ITERATION = 64
private integer POSITION = 0
private integer array STACK
private unit array UNIT_STACK
private integer STACK_SIZE = 0
private integer ASSIGNED = 1
private integer MAX_INDEX = 0
endglobals
//Function to get the unit's unique integer id, inlines to getting its userdata
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
//Filter for units to index
private function UnitFilter takes nothing returns boolean
return true
endfunction
private function Clear takes nothing returns nothing
local integer i = POSITION
loop
exitwhen (POSITION > MAX_INDEX or POSITION > i+COUNT_PER_ITERATION)
if UNIT_STACK[POSITION] != null and GetUnitUserData(UNIT_STACK[POSITION]) == 0 then
set STACK[STACK_SIZE] = POSITION
set STACK_SIZE = STACK_SIZE + 1
set UNIT_STACK[POSITION] = null
endif
set POSITION = POSITION + 1
endloop
if POSITION > MAX_INDEX then
set POSITION = 0
endif
endfunction
private function Add takes nothing returns boolean
local integer id = 0
if STACK_SIZE > 0 then
set STACK_SIZE = STACK_SIZE - 1
set id = STACK[STACK_SIZE]
else
set id = ASSIGNED
set ASSIGNED = ASSIGNED + 1
if ASSIGNED > MAX_INDEX then
set MAX_INDEX = ASSIGNED
endif
endif
call SetUnitUserData(GetFilterUnit(), id)
set UNIT_STACK[id] = GetFilterUnit()
return true
endfunction
private function GroupAdd takes nothing returns nothing
local integer id = 0
if STACK_SIZE > 0 then
set STACK_SIZE = STACK_SIZE - 1
set id = STACK[STACK_SIZE]
else
set id = ASSIGNED
set ASSIGNED = ASSIGNED + 1
if ASSIGNED > MAX_INDEX then
set MAX_INDEX = ASSIGNED
endif
endif
call SetUnitUserData(GetEnumUnit(), id)
set UNIT_STACK[id] = GetEnumUnit()
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local region r = CreateRegion()
local group g = CreateGroup()
local integer i = 0
//Use a filterfunc so units are indexed immediately
call RegionAddRect(r, bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(t, r, And(Condition(function UnitFilter), Condition(function Add)))
//Start the timer to recycle indexes
call TimerStart(CreateTimer(), TIMER_PERIODICITY, true, function Clear)
//Loop and group per player to grab all units, including those with locust
loop
exitwhen i > 15
call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function UnitFilter))
call ForGroup(g, function GroupAdd)
set i = i + 1
endloop
call DestroyGroup(g)
set r = null
set t = null
set g = null
endfunction
endlibrary
//TESH.scrollpos=37
//TESH.alwaysfold=0
library TerrainPathability initializer Initialization
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This can be used to detect the type of pathing a specific point of terrain
//* is, whether land, shallow water, or deep water. This type of detection
//* should have been easy to do using natives, but the IsTerrainPathable(...)
//* native is very counterintuitive and does not permit easy detection in one
//* call. For that reason, this library was developed.
//*
//* The system requires a dummy unit of some sort. There are no real
//* requirements upon the dummy unit, but it needs a non-zero movement speed.
//* More importantly than the dummy unit, though, it needs a custom windwalk
//* based unit ability with a 0.00 duration and no fade time. Both of those
//* raw id's (for the unit and windwalk dummy) need to be configured below.
//*
//* There is an objectmerger call available for those of you too lazy to build
//* your own windwalk based ability. Simply uncomment it below and save once,
//* then close and reopen your map and recomment the line.
//*
globals
constant integer TERRAIN_PATHING_DEEP = 1
constant integer TERRAIN_PATHING_SHALLOW = 2
constant integer TERRAIN_PATHING_LAND = 3
constant integer TERRAIN_PATHING_WALKABLE = 4
private unit Dummy = null
private constant integer DUMMY_UNIT_ID = 'hfoo'
private constant integer DUMMY_WINDWALK_ID = 'win&'
private constant player OWNING_PLAYER = Player(15)
//* These variables shouldn't be adjusted
private real WorldMinX = 0.
private real WorldMinY = 0.
endglobals
////! external ObjectMerger w3a ANwk win& anam "Collision Ability" ansf "" Owk3 1 0.0 Owk4 1 0 Owk2 1 0.0 Owk1 1 0.0 acdn 1 0.0 ahdu 1 0.0 adur 1 0. aher 0 amcs 1 0
function IsTerrainPathingType takes real x, real y, integer terrainPathingType returns boolean
local boolean b = false
if terrainPathingType == TERRAIN_PATHING_DEEP then
set b = not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
elseif terrainPathingType == TERRAIN_PATHING_SHALLOW then
set b = not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
elseif terrainPathingType == TERRAIN_PATHING_LAND then
set b = IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
elseif terrainPathingType == TERRAIN_PATHING_WALKABLE then
call SetUnitPosition(Dummy, x, y)
set b = GetUnitX(Dummy) == x and GetUnitY(Dummy) == y and not (not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY))
call SetUnitX(Dummy, WorldMinX)
call SetUnitY(Dummy, WorldMinY)
endif
return b
endfunction
private function Initialization takes nothing returns nothing
set WorldMinX = GetRectMinX(bj_mapInitialPlayableArea)+16
set WorldMinY = GetRectMinY(bj_mapInitialPlayableArea)+16
set Dummy = CreateUnit(OWNING_PLAYER, DUMMY_UNIT_ID, WorldMinX, WorldMinY, 0.)
call UnitAddAbility(Dummy, DUMMY_WINDWALK_ID)
call UnitAddAbility(Dummy, 'Avul')
call IssueImmediateOrderById(Dummy, 852129)
call SetUnitX(Dummy, WorldMinX)
call SetUnitY(Dummy, WorldMinY)
endfunction
endlibrary
//TESH.scrollpos=61
//TESH.alwaysfold=0
library_once TimerUtils initializer init
//*********************************************************************
//* TimerUtils (Blue flavor for 1.23b or later)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//* limit, which means that if more than 408000 handle ids
//* are used in your map, TimerUtils might fail, this
//* value is quite big and it is much bigger than the
//* timer limit in Red flavor.
//*
//********************************************************************
//==================================================================================================
globals
private hashtable hasht //I <3 blizz
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
call SaveInteger(hasht,0, GetHandleId(t), value)
endfunction
function GetTimerData takes timer t returns integer
return LoadInteger(hasht, 0, GetHandleId(t))
endfunction
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
set tT[0]=CreateTimer()
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
endif
if (tN==8191) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
set hasht = InitHashtable()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a simple implementation of a stack for groups that need to
//* be in the user's control for greater than an instant of time. Additionally,
//* this library provides a single, global group variable for use with user-end
//* enumerations. It is important to note that users should not be calling
//* DestroyGroup() on the global group, since then it may not exist for when it
//* it is next needed.
//*
//* The group stack removes the need for destroying groups and replaces it with
//* a recycling method.
//* function NewGroup takes nothing returns group
//* function ReleaseGroup takes group g returns boolean
//* function GroupRefresh takes group g returns nothing
//*
//* NewGroup grabs a currently unused group from the stack or creates one if the
//* stack is empty. You can use this group however you'd like, but always
//* remember to call ReleaseGroup on it when you are done with it. If you don't
//* release it, it will 'leak' and your stack may eventually overflow if you
//* keep doing that.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hash table. 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.
//*
globals
//* Group for use with all instant enumerations
group ENUM_GROUP = CreateGroup()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Assorted constants
private constant integer MAX_HANDLE_COUNT = 408000
private constant integer MIN_HANDLE_ID = 0x100000
//* Arrays and counter for the group stack
private group array Groups
private integer array Status[MAX_HANDLE_COUNT]
private integer Count = 0
endglobals
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
set Status[GetHandleId(Groups[Count])-MIN_HANDLE_ID] = 1
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer stat = Status[GetHandleId(g)-MIN_HANDLE_ID]
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif stat == 0 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif stat == 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 GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
set Status[GetHandleId(g)-MIN_HANDLE_ID] = 2
return true
endfunction
endlibrary
//TESH.scrollpos=20
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'e000'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=504
//TESH.alwaysfold=0
library xedamage initializer init requires xebasic
//************************************************************************
// xedamage 0.6
// --------
// For all your damage and targetting needs.
//
//************************************************************************
//===========================================================================================================
globals
private constant integer MAX_SUB_OPTIONS = 3
//=======================================================
private constant real EPSILON = 0.000000001
private unit dmger
private constant integer MAX_SPACE = 8190 // MAX_SPACE/MAX_SUB_OPTIONS is the instance limit for xedamage, usually big enough...
endglobals
private keyword structInit
struct xedamage[MAX_SPACE]
//----
// fields and methods for a xedamage object, they aid determining valid targets and special
// damage factor conditions.
//
// Notice the default values.
//
boolean damageSelf = false // the damage and factor methods usually have a source unit parameter
// xedamage would consider this unit as immune unless you set damageSelf to true
boolean damageAllies = false // Alliance dependent target options.
boolean damageEnemies = true // *
boolean damageNeutral = true // *
boolean ranged = true // Is the attack ranged? This has some effect on the AI of the affected units
// true by default, you may not really need to modify this.
boolean visibleOnly = false // Should only units that are visible for source unit's owner be affected?
boolean deadOnly = false // Should only corpses be affected by "the damage"? (useful when using xedamage as a target selector)
boolean alsoDead = false // Should even corpses and alive units be considered?
boolean damageTrees = false //Also damage destructables? Notice this is used only in certain methods.
//AOE for example targets a circle, so it can affect the destructables
//in that circle, a custom spell using xedamage for targetting configuration
//could also have an if-then-else implemented so it can verify if it is true
//then affect trees manually.
//
// Damage type stuff:
// .dtype : the "damagetype" , determines if the spell is physical, magical or ultimate.
// .atype : the "attacktype" , deals with armor.
// .wtype : the "weapontype" , determines the sound effect to be played when damage is done.
//
// Please use common.j/blizzard.j/ some guide to know what damage/attack/weapon types can be used
//
damagetype dtype = DAMAGE_TYPE_UNIVERSAL
attacktype atype = ATTACK_TYPE_NORMAL
weapontype wtype = WEAPON_TYPE_WHOKNOWS
//
// Damage type 'tag' people might use xedamage.isInUse() to detect xedamage usage, there are other static
// variables like xedamage.CurrentDamageType and xedamage.CurrentDamageTag. The tag allows you to specify
// a custom id for the damage type ** Notice the tag would aid you for some spell stuff, for example,
// you can use it in a way similar to Rising_Dusk's damage system.
//
integer tag = 0
//
// if true, forceDamage will make xedamage ignore dtype and atype and try as hard as possible to deal 100%
// damage.
boolean forceDamage = false
//
// Ally factor! Certain spells probably have double purposes and heal allies while harming enemies. This
// field allows you to do such thing.
//
real allyfactor = 1.0
//
// field: .exception = SOME_UNIT_TYPE
// This field adds an exception unittype (classification), if the unit belongs to this unittype it will
// be ignored.
//
method operator exception= takes unittype ut returns nothing
set this.use_ex=true
set this.ex_ut=ut
endmethod
//
// field: .required = SOME_UNIT_TYPE
// This field adds a required unittype (classification), if the unit does not belong to this unittype
// it will be ignored.
//
method operator required= takes unittype ut returns nothing
set this.use_req=true
set this.req_ut=ut
endmethod
private boolean use_ex = false
private unittype ex_ut = null
private boolean use_req = false
private unittype req_ut = null
private unittype array fct[MAX_SUB_OPTIONS]
private real array fc[MAX_SUB_OPTIONS]
private integer fcn=0
//
// method .factor(SOME_UNIT_TYPE, factor)
// You might call factor() if you wish to specify a special damage factor for a certain classification,
// for example call d.factor(UNIT_TYPE_STRUCTURE, 0.5) makes xedamage do half damage to structures.
//
method factor takes unittype ut, real fc returns nothing
if(this.fcn==MAX_SUB_OPTIONS) then
debug call BJDebugMsg("In one instance of xedamage, you are doing too much calls to factor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of factor() calls")
return
endif
set this.fct[this.fcn] = ut
set this.fc[this.fcn] = fc
set this.fcn = this.fcn+1
endmethod
private integer array abifct[MAX_SUB_OPTIONS]
private real array abifc[MAX_SUB_OPTIONS]
private integer abifcn=0
//
// method .abilityFactor('abil', factor)
// You might call abilityFactor() if you wish to specify a special damage factor for units that have a
// certain ability/buff.
// for example call d.abilityFactor('A000', 1.5 ) makes units that have the A000 ability take 50% more
// damage than usual.
//
method abilityFactor takes integer abilityId, real fc returns nothing
if(this.abifcn==MAX_SUB_OPTIONS) then
debug call BJDebugMsg("In one instance of xedamage, you are doing too much calls to abilityFactor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of abilityFactor() calls")
return
endif
set this.abifct[this.abifcn] = abilityId
set this.abifc[this.abifcn] = fc
set this.abifcn = this.abifcn+1
endmethod
private boolean usefx = false
private string fxpath
private string fxattach
//
// method .useSpecialEffect("effect\\path.mdl", "origin")
// Makes it add (and destroy) an effect when damage is performed.
//
method useSpecialEffect takes string path, string attach returns nothing
set this.usefx = true
set this.fxpath=path
set this.fxattach=attach
endmethod
//********************************************************************
//* Now, the usage stuff:
//*
//================================================================================
// static method xedamage.isInUse() will return true during a unit damaged
// event in case this damage was caused by xedamage, in this case, you can
// read variables like CurrentDamageType, CurrentAttackType and CurrentDamageTag
// to be able to recognize what sort of damage was done.
//
readonly static damagetype CurrentDamageType=null
readonly static attacktype CurrentAttackType=null
readonly static integer CurrentDamageTag =0
private static integer inUse = 0
static method isInUse takes nothing returns boolean
return (.inUse>0) //inline friendly.
endmethod
//========================================================================================================
// This function calculates the damage factor caused by a certain attack and damage
// type, it is static : xedamage.getDamageTypeFactor(someunit, ATTAcK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, 100)
//
static method getDamageTypeFactor takes unit u, attacktype a, damagetype d returns real
local real hp=GetWidgetLife(u)
local real mana=GetUnitState(u,UNIT_STATE_MANA)
local real r
//Since a unit is in that point, we don't need checks.
call SetUnitX(dmger,GetUnitX(u))
call SetUnitY(dmger,GetUnitY(u))
call SetUnitOwner(dmger,GetOwningPlayer(u),false)
set r=hp
if (hp<1) then
call SetWidgetLife(u,1)
set r=1
endif
call UnitDamageTarget(dmger,u,0.01,false,false,a,d,null)
call SetUnitOwner(dmger,Player(15),false)
if (mana>GetUnitState(u,UNIT_STATE_MANA)) then
//Unit had mana shield, return 1 and restore mana too.
call SetUnitState(u,UNIT_STATE_MANA,mana)
set r=1
else
set r= (r-GetWidgetLife(u))*100
endif
call SetWidgetLife(u,hp)
return r
endmethod
private method getTargetFactorCore takes unit source, unit target, boolean usetypes returns real
local player p=GetOwningPlayer(source)
local boolean allied=IsUnitAlly(target,p)
local boolean enemy =IsUnitEnemy(target,p)
local boolean neutral=allied
local real f
local real negf=1.0
local integer i
if(this.damageAllies != this.damageNeutral) then
set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_HELP_REQUEST ))
//I thought accuracy was not as important as speed , I think that REQUEST is false is enough to consider
// it neutral.
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_HELP_RESPONSE ))
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_SHARED_XP ))
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_SHARED_SPELLS ))
set allied= allied and not(neutral)
endif
if (not this.damageAllies) and allied then
return 0.0
elseif (not this.damageEnemies) and enemy then
return 0.0
elseif( (not this.damageSelf) and (source==target) ) then
return 0.0
elseif (not this.damageNeutral) and neutral then
return 0.0
elseif( this.use_ex and IsUnitType(target, this.ex_ut) ) then
return 0.0
elseif( this.visibleOnly and not IsUnitVisible(target,p) ) then
return 0.0
elseif ( this.deadOnly and not IsUnitType(target,UNIT_TYPE_DEAD) ) then
return 0.0
elseif ( not(this.alsoDead) and IsUnitType(target,UNIT_TYPE_DEAD) ) then
return 0.0
endif
set f=1.0
if ( IsUnitAlly(target,p) ) then
set f=f*this.allyfactor
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
if (this.use_req and not IsUnitType(target,this.req_ut)) then
return 0.0
endif
set i=.fcn-1
loop
exitwhen (i<0)
if( IsUnitType(target, this.fct[i] ) ) then
set f=f*this.fc[i]
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
set i=i-1
endloop
set i=.abifcn-1
loop
exitwhen (i<0)
if( GetUnitAbilityLevel(target,this.abifct[i] )>0 ) then
set f=f*this.abifc[i]
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
set i=i-1
endloop
set f=f*negf
if ( f<=EPSILON) and (f>=-EPSILON) then
return 0.0
endif
if( this.forceDamage or not usetypes ) then
return f
endif
set f=f*xedamage.getDamageTypeFactor(target,this.atype,this.dtype)
if ( f<=EPSILON) and (f>=-EPSILON) then
return 0.0
endif
return f
endmethod
//====================================================================
// With this you might decide if a unit is a valid target for a spell.
//
method getTargetFactor takes unit source, unit target returns real
return this.getTargetFactorCore(source,target,true)
endmethod
//======================================================================
// a little better, I guess
//
method allowedTarget takes unit source, unit target returns boolean
return (this.getTargetFactorCore(source,target,false)!=0.0)
endmethod
//=======================================================================
// performs damage to the target unit, for unit 'source'.
//
method damageTarget takes unit source, unit target, real damage returns boolean
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
local real f = this.getTargetFactorCore(source,target,false)
local real pl
if(f!=0.0) then
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
set .inUse = .inUse +1
set pl=GetWidgetLife(target)
call UnitDamageTarget(source,target, f*damage, true, .ranged, .atype, .dtype, .wtype )
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
return (pl!=GetWidgetLife(target))
endif
return false
endmethod
//=======================================================================================
// The same as damageTarget, but it forces a specific damage value, good if you already
// know the target.
//
method damageTargetForceValue takes unit source, unit target, real damage returns nothing
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
set .inUse = .inUse +1
call UnitDamageTarget(source,target, damage, true, .ranged, null, null, .wtype )
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
endmethod
//=====================================================================================
// Notice: this will not Destroy the group, but it will certainly empty the group.
//
method damageGroup takes unit source, group targetGroup, real damage returns integer
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
local unit target
local real f
local integer count=0
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
loop
set target=FirstOfGroup(targetGroup)
exitwhen (target==null)
call GroupRemoveUnit(targetGroup,target)
set f= this.getTargetFactorCore(source,target,false)
if (f!=0.0) then
set count=count+1
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
call UnitDamageTarget(source,target, f*damage, true, .ranged, .atype, .dtype, .wtype )
endif
endloop
set .inUse = .inUse -1
set .CurrentDamageTag=tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
return count
endmethod
private static xedamage instance
private integer countAOE
private unit sourceAOE
private real AOEx
private real AOEy
private real AOEradius
private real AOEdamage
private static boolexpr filterAOE
private static boolexpr filterDestAOE
private static group enumgroup
private static rect AOERect
private static method damageAOE_Enum takes nothing returns boolean
local unit target=GetFilterUnit()
local xedamage this=.instance //adopting a instance.
local real f
if( not IsUnitInRangeXY(target,.AOEx, .AOEy, .AOEradius) ) then
set target=null
return false
endif
set f=.getTargetFactorCore(.sourceAOE, target, false)
if(f!=0.0) then
set .countAOE=.countAOE+1
call UnitDamageTarget(.sourceAOE,target, f*this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
endif
set .instance= this //better restore, nesting IS possible!
set target=null
return false
endmethod
private static method damageAOE_DestructablesEnum takes nothing returns boolean
local destructable target=GetFilterDestructable()
local xedamage this=.instance //adopting a instance.
local real dx=.AOEx-GetDestructableX(target)
local real dy=.AOEy-GetDestructableY(target)
if( dx*dx + dy*dy >= .AOEradius+EPSILON ) then
set target=null
return false
endif
set .countAOE=.countAOE+1
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
call UnitDamageTarget(.sourceAOE,target, this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
set .instance= this //better restore, nesting IS possible!
set target=null
return false
endmethod
//==========================================================================================
// will affect trees if damageTrees is true!
//
method damageAOE takes unit source, real x, real y, real radius, real damage returns integer
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
set .instance=this
set .countAOE=0
set .sourceAOE=source
set .AOEx=x
set .AOEradius=radius
set .AOEy=y
set .AOEdamage=damage
call GroupEnumUnitsInRange(.enumgroup,x,y,radius+XE_MAX_COLLISION_SIZE, .filterAOE)
if(.damageTrees) then
call SetRect(.AOERect, x-radius, y-radius, x+radius, y+radius)
set .AOEradius=.AOEradius*.AOEradius
call EnumDestructablesInRect(.AOERect, .filterDestAOE, null)
endif
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
return .countAOE
endmethod
method damageAOELoc takes unit source, location loc, real radius, real damage returns integer
return .damageAOE(source, GetLocationX(loc), GetLocationY(loc), radius, damage)
endmethod
//==========================================================================================
// only affects trees, ignores damageTrees
//
method damageDestructablesAOE takes unit source, real x, real y, real radius, real damage returns integer
set .instance=this
set .countAOE=0
set .sourceAOE=source
set .AOEx=x
set .AOEradius=radius*radius
set .AOEy=y
set .AOEdamage=damage
//if(.damageTrees) then
call SetRect(.AOERect, x-radius, y-radius, x+radius, y+radius)
call EnumDestructablesInRect(.AOERect, .filterDestAOE, null)
//endif
return .countAOE
endmethod
method damageDestructablesAOELoc takes unit source, location loc, real radius, real damage returns integer
return .damageDestructablesAOE(source,GetLocationX(loc), GetLocationY(loc), radius, damage)
endmethod
//'friend' with the library init
static method structInit takes nothing returns nothing
set .AOERect= Rect(0,0,0,0)
set .filterAOE= Condition(function xedamage.damageAOE_Enum)
set .filterDestAOE = Condition( function xedamage.damageAOE_DestructablesEnum)
set .enumgroup = CreateGroup()
endmethod
endstruct
private function init takes nothing returns nothing
set dmger=CreateUnit(Player(15), XE_DUMMY_UNITID , 0.,0.,0.)
call UnitAddAbility(dmger,'Aloc')
call xedamage.structInit()
endfunction
endlibrary
library xepreload initializer init requires xebasic
//************************************************************************
// xepreload 0.4
// ---------
// Ah, the joy of preloading abilities, it is such a necessary evil...
// Notice you are not supposed to use this system in places outside map init
//
// This one does the preloading and tries to minimize the hit on loading time
// for example, it only needs one single native call per ability preloaded.
//
//************************************************************************
//===========================================================================================================
globals
private unit dum=null
endglobals
//inline friendly
function XE_PreloadAbility takes integer abilid returns nothing
call UnitAddAbility(dum, abilid)
endfunction
private function kill takes nothing returns nothing
call RemoveUnit(dum)
set dum=null
call DestroyTimer(GetExpiredTimer()) //I do hope this doesn't trigger the apocallypse. I didn't think
// it was a great idea to make this require a whole timer recycling
// system, so, just destroy. Anyone got a better idea on how to make
// this execute after all the other calls at init?
endfunction
private function init takes nothing returns nothing
set dum=CreateUnit(Player(15),XE_DUMMY_UNITID, 0,0,0)
call TimerStart(CreateTimer(),0.0,false,function kill)
endfunction
endlibrary
//TESH.scrollpos=182
//TESH.alwaysfold=0
//****************************************************************************\\
//** **\\
//** OVERRUN v2.1 **\\
//** by Kricz **\\
//** **\\
//** Requires: **\\
//** vJass, this trigger & Wc3 version 1.24 **\\
//** **\\
//** Credits: **\\
//** Vexorian: vJass, TimerUtils and xe **\\
//** Rising Duks: Knockback, Last Order & Group Utils **\\
//** Antinarf: IsTerrainWalkable **\\
//** **\\
//** **\\
//** Discription: **\\
//** The caster runs towards the target location, **\\
//** getting more faster and transparent the longer he runs. **\\
//** Damaged every enemy he strifes on his way depending on the **\\
//** moved distance and level. When the caster reaches his target, **\\
//** all enemy units get knocked back. **\\
//** **\\
//****************************************************************************\\
scope Overrun //requires TimerUtils, Knockback, Last Order, Group Utils, xebasic, xedamage, xepreload
globals
//Raw-Code of the Spell
private constant integer SPELL_ID = 'A000'
//Rawcode of the Spellbook for Spell Immunity
private constant integer SPELLBOOK_ID = 'A001'
//The acceleration with each interval
private constant real ACCELERATION = 375.
//Should trees get destroyed (ignores collosion with them!)
private constant boolean DESTROY_TREES = true
//The max transparency (0 = transparent, 255 = normal)
private constant real MAX_TRANSPARENT = 50.
//The Start-Factor of the units animation
private constant real ANIM_START_FACTOR = 1.25
//The End-Factor the unit will have when it reaches his target or the max range for this level
private constant real ANIM_END_FACTOR = 3.
//The Animation-Id you want to have while the unit moves
private constant integer AMIN_ID = 0
//The effect created at the start
private constant string EFFECT_START = "Abilities\\Spells\\Human\\Defend\\DefendCaster.mdl"
private constant string EFFECT_START_ATTACH = "origin"
//The effect attached to the unit while moving
private constant string EFFECT_MOVE = "MDX\\KnockbackDust.mdx"
private constant string EFFECT_MOVE_ATTACH = "origin"
//The effect when the unit reaches its target / max range for the level
private constant string EFFECT_END = "Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl"
private constant string EFFECT_END_ATTACH = "origin"
//The "collision-size" of the unit while it move.
private constant real DAMAGE_AOE = 165.
//The knockback-AoE at the end
private constant real KNOCKBACK_AOE = 250.
//The knockback-StartSpeed at the end
private constant real KNOCKBACK_START_SPEED = 675.
//The knockback-speed-decrement
//Rising Dusk's KnockbackSystem will change this value itself (!!!) :
//KNOCKBACK_DECREMENT * 0.05 * 0.05
private constant real KNOCKBACK_DECREMENT = 1400.
endglobals
//In this function, you can setup the damage
//Please note, that the damage depends on the distance the unit moved! So this function uses the damage for 100% moved
private function DAMAGE takes integer lvl returns real
return 100. + 75. * lvl
endfunction
//The max-range of the spell
private function MAX_RANGE takes integer lvl returns real
return 800. + 200. * lvl
endfunction
//! textmacro OR_SETUP_DAMAGE
set .dtype = DAMAGE_TYPE_NORMAL
set .atype = ATTACK_TYPE_HERO
set .wtype = WEAPON_TYPE_WOOD_HEAVY_SLICE
//! endtextmacro
//Don't change things below here unless you really know what you're doing!!!
private struct spelldata
unit caster = null
integer lvl = 0
real cos = 0.
real sin = 0.
real dist = 0.
real speed = 0.
real angle = 0.
real x = 0.
real y = 0.
real moved = 0.
real percent = 0.
timer t = null
group g = null
effect fx = null
static group group
static boolexpr filter
static thistype temp
static delegate xedamage dmg
static method create takes unit caster, real distance, real angle returns thistype
local thistype this = thistype.allocate()
set .caster = caster
set .lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
set .dist = distance
set .cos = Cos(angle)
set .sin = Sin(angle)
set .angle = angle
set .x = GetUnitX(.caster)
set .y = GetUnitY(.caster)
set .speed = GetUnitMoveSpeed(.caster)
set .t = NewTimer()
set .g = NewGroup()
set .fx = AddSpecialEffectTarget(EFFECT_MOVE, .caster, EFFECT_MOVE_ATTACH)
call SetUnitPathing(.caster, false)
call SetUnitTimeScale(.caster, ANIM_START_FACTOR)
call SetTimerData(.t, this)
call UnitAddAbility(.caster, SPELLBOOK_ID)
call DestroyEffect(AddSpecialEffectTarget(EFFECT_START, .caster, EFFECT_START_ATTACH))
call TimerStart(.t, XE_ANIMATION_PERIOD, true, function thistype.periodic)
return this
endmethod
private method onDestroy takes nothing returns nothing
local unit u = null
local real angle = 0
call DestroyEffect(.fx)
call SetUnitTimeScale(.caster, 1.)
call SetUnitPathing(.caster, true)
call SetUnitVertexColor(.caster, 255, 255, 255, 255)
call UnitRemoveAbility(.caster, SPELLBOOK_ID)
call ReleaseGroup(.g)
if GetWidgetLife(.caster) > 0.405 then
call DestroyEffect(AddSpecialEffectTarget(EFFECT_END, .caster, EFFECT_END_ATTACH))
call SetUnitAnimation(.caster, "stand")
set .temp = this
call GroupEnumUnitsInRange(.group, .x, .y, KNOCKBACK_AOE, .filter)
call IssueLastOrder(.caster)
loop
set u = FirstOfGroup(.group)
exitwhen u == null
set angle = Atan2(GetUnitY(u)- .y, GetUnitX(u) - .x) * bj_RADTODEG
call KnockbackTarget(.caster, u, angle, KNOCKBACK_START_SPEED, KNOCKBACK_DECREMENT, true, true, false)
call GroupRemoveUnit(.group, u)
endloop
call GroupClear(.group)
endif
endmethod
private static method FilterFunc takes nothing returns boolean
return GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnit(GetFilterUnit(), .temp.caster) and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(.temp.caster)) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)
endmethod
private static method periodic takes nothing returns nothing
local thistype this = thistype(GetTimerData(GetExpiredTimer()))
local real damage = 0
local unit u = null
local real nx = 0.
local real ny = 0.
set .speed = .speed + ACCELERATION * XE_ANIMATION_PERIOD
set nx = .x + (.speed * XE_ANIMATION_PERIOD) * .cos
set ny = .y + (.speed * XE_ANIMATION_PERIOD) * .sin
if DESTROY_TREES then
call .damageDestructablesAOE(.caster, nx, ny, DAMAGE_AOE, 100000.00)
endif
if .moved >= .dist or .moved >= MAX_RANGE(.lvl) or GetWidgetLife(.caster) < 0.405 or not IsTerrainWalkable(nx, ny) then
call ReleaseTimer(.t)
call .destroy()
else
call SetUnitAnimationByIndex(.caster, AMIN_ID)
set .moved = .moved + .speed * XE_ANIMATION_PERIOD
call SetUnitPosition(.caster, nx, ny)
set .x = GetUnitX(.caster)
set .y = GetUnitY(.caster)
set .percent = (.moved / MAX_RANGE(.lvl))
set damage = DAMAGE(.lvl) * .percent
set .temp = this
call GroupEnumUnitsInRange(.group, .x, .y, DAMAGE_AOE, .filter)
loop
set u = FirstOfGroup(.group)
exitwhen u == null
if not IsUnitInGroup(u, .g) then
call .damageTarget(.caster, u, damage)
endif
call GroupRemoveUnit(.group, u)
call GroupAddUnit(.g, u)
endloop
call GroupClear(.group)
call SetUnitFacing(.caster, .angle * bj_RADTODEG)
call SetUnitVertexColor(.caster, 255, 255, 255, R2I(255. * (1. - .percent) + MAX_TRANSPARENT * .percent))
call SetUnitTimeScale(.caster, ANIM_START_FACTOR * (1. - .percent) + ANIM_END_FACTOR * .percent)
endif
endmethod
private static method register takes nothing returns boolean
local unit caster = null
local real dx = 0.
local real dy = 0.
local real distance = 0.
local real angle = 0.
if GetSpellAbilityId() == SPELL_ID then
set caster = GetSpellAbilityUnit()
set dx = GetSpellTargetX() - GetUnitX(caster)
set dy = GetSpellTargetY() - GetUnitY(caster)
set distance = SquareRoot(dx * dx + dy * dy)
set angle = Atan2(GetSpellTargetY() - GetUnitY(caster), GetSpellTargetX() - GetUnitX(caster))
call thistype.create(caster, distance, angle)
set caster = null
return true
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.register))
set .filter = Condition(function thistype.FilterFunc)
set .group = NewGroup()
set .dmg = xedamage.create()
//! runtextmacro OR_SETUP_DAMAGE()
call XE_PreloadAbility(SPELL_ID)
call XE_PreloadAbility(SPELLBOOK_ID)
loop
call SetPlayerAbilityAvailable(Player(i), SPELLBOOK_ID, false)
set i = i + 1
exitwhen i >= bj_MAX_PLAYER_SLOTS
endloop
set t = null
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
function i1 takes nothing returns nothing
set udg_test = ( udg_test + 1 )
call BJDebugMsg(I2S(udg_test))
call SetUnitAnimationByIndex(udg_Hero, udg_test)
endfunction
function i2 takes nothing returns nothing
set udg_test = ( udg_test - 1 )
call BJDebugMsg(I2S(udg_test))
call SetUnitAnimationByIndex(udg_Hero, udg_test)
endfunction
//===========================================================================
function InitTrig_TestMap takes nothing returns nothing
local trigger i = CreateTrigger( )
local trigger ii = CreateTrigger( )
call TriggerRegisterPlayerChatEvent(i, Player(0), "+", true )
call TriggerRegisterPlayerChatEvent(ii, Player(0), "-", true )
call TriggerAddAction(i, function i1)
call TriggerAddAction(ii, function i2)
set udg_Hero = gg_unit_H000_0038
call DisplayTimedTextToPlayer(Player(0), 0, 0, 20, "Please give credits to me if you use this spell and credits to the creators of the required systems.")
endfunction