//TESH.scrollpos=0
//TESH.alwaysfold=0
//
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
//==============================================================================
// PUI -- Perfect Unit Indexing by Cohadar -- v5.1
//==============================================================================
//
// PURPOUSE:
// * Extending UnitUserData()
// * This is basically perfect hashing algorithm for units
//
// HOW TO USE:
// * You have only one function at your disposal GetUnitIndex(unit)
// It will return a unique index for each unit in range 1..8190
//
// * What you will do with that index is all up to you
// Of course using global arrays is the most obvious choice
// Advanced jassers will think of a couple of more clever ones ofc.
//
// * There are also 2 textmacros available at the end of library code
// They can be used for easier attaching to units
// PUI for structs
// PUI_PROPERTY for unit, integer, real, boolean and string variables
//
// PROS:
// * You can use any number of systems that previously could not work together
// because they all required exclusive access of UnitUserData()
//
// * You can also use this to attach spell data structs to casters
//
// * There are no SetUnitIndex() or ClearUnitIndex() functions here
// Each unit gets assigned one index that cannot be changed
// That index will be automatically recycled when unit is removed from the game.
//
// CONS:
// * This system uses UnitUserData() itself
// That means that if you want to use PUI you must recode
// any other system that uses UnitUserData() to use GetUnitIndex() instead
//
// * If you use UnitIndex for arrays of non-native types (timers, effects and similar)
// you must check if timer on that index already exists before you create a new one.
// This can happen if GetUnitIndex() assigns a recycled index (index of some dead and removed unit)
// to the newly created unit for which you intended to use timer for
//
// * All in all this is not a sys for newbies, it gives great power,
// but it requires knowledge and carefull handling
//
// DETAILS:
// * System is using unit array to keep track of all units with an index.
// Array is periodically checked for removed units,
// when removed unit is found, index is recycled.
// Indexes have "decay time" to prevent bugs
// caused by attaching to "Can't Raise, Does not decay" type units,
// or by using RemoveUnit() function
//
// SYSTEM COMMANDS: (debug mode only, red player only)
//
// * type -pui to display indexes of currently selected units
// * type -puistats to display some stats
//
// THANKS TO:
// * Vexorian - for his help with PUI textmacro
//
// HOW TO IMPORT:
// * Just create a trigger named PUI
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
library PUI initializer Init
//==============================================================================
globals
//-----------------------------------------------
private constant real INDEX_DECAY_TIME = 5. // seconds
//-----------------------------------------------
private constant real PERIOD = 0.03125 // 32 fps
//-----------------------------------------------
private constant integer DECAY_TICKS = R2I(INDEX_DECAY_TIME/PERIOD)
//-----------------------------------------------
private integer array Indexz
private unit array Unitz
private integer array Decayz
private integer array Tickz
private integer maxindex = 0
private integer topindex = 0
private integer decayindex = 0
private integer checker = 0
private integer decayer = 0
private integer tick = 0
endglobals
//==============================================================================
private function PeriodicRecycler takes nothing returns boolean
local integer temp
set tick = tick + 1
if topindex > decayindex then
set checker = checker + 1
if checker > topindex then
set checker = decayindex + 1
endif
if (GetUnitUserData(Unitz[checker])==0) then
set decayindex = decayindex + 1
set Unitz[checker] = Unitz[decayindex]
// swap(checker, decayindex)
set temp = Indexz[checker]
set Indexz[checker] = Indexz[decayindex]
set Indexz[decayindex] = temp
set Decayz[decayindex] = DECAY_TICKS
set Tickz[decayindex] = tick
endif
endif
if decayindex > 0 then
set decayer = decayer + 1
if decayer > decayindex then
set decayer = 1
endif
set Decayz[decayer] = Decayz[decayer] - (tick-Tickz[decayer])
if Decayz[decayer] > 0 then
set Tickz[decayer] = tick
else
// swap(decayer, decayindex)
set temp = Indexz[decayer]
set Indexz[decayer] = Indexz[decayindex]
set Indexz[decayindex] = temp
set Unitz[decayindex] = Unitz[topindex]
// swap(decayindex, topindex)
set temp = Indexz[decayindex]
set Indexz[decayindex] = Indexz[topindex]
set Indexz[topindex] = temp
set decayindex = decayindex - 1
set topindex = topindex - 1
endif
endif
return false
endfunction
//==============================================================================
// Main and only function exported by this library
//==============================================================================
function GetUnitIndex takes unit whichUnit returns integer
local integer index
debug if whichUnit == null then
debug call BJDebugMsg("|c00FF0000ERROR: PUI - Index requested for null unit")
debug return 0
debug endif
set index = GetUnitUserData(whichUnit)
if index == 0 then
set topindex = topindex + 1
if topindex > maxindex then
set maxindex = topindex
set Indexz[topindex] = topindex
endif
set index = Indexz[topindex]
set Unitz[topindex] = whichUnit
call SetUnitUserData(whichUnit, index)
set index = GetUnitUserData(whichUnit)
// this happens when requesting unit index for removed unit
debug if index == 0 then
debug call BJDebugMsg("|c00FFCC00WARNING: PUI - Bad unit handle")
debug endif
//debug call BJDebugMsg("|c00FFCC00PUI: Index assigned #" + I2S(index))
endif
return index
endfunction
//==============================================================================
private function DisplayStats takes nothing returns nothing
call BJDebugMsg("=============================================")
call BJDebugMsg("Biggest index ever = " + I2S(maxindex))
call BJDebugMsg("Indexes in use = " + I2S(topindex-decayindex))
call BJDebugMsg("Decaying indexes = " + I2S(decayindex))
call BJDebugMsg("Released indexes = " + I2S(maxindex-topindex))
call BJDebugMsg("=============================================")
endfunction
//===========================================================================
private function DisplaySelectedEnum takes nothing returns nothing
call BJDebugMsg( "PUI(" + ( GetUnitName(GetEnumUnit()) + ( ") = " + I2S(GetUnitUserData(GetEnumUnit())) ) ) )
endfunction
//===========================================================================
private function DisplaySelected takes nothing returns nothing
local group g = CreateGroup()
call SyncSelections()
call GroupEnumUnitsSelected(g, Player(0), null)
call ForGroup(g, function DisplaySelectedEnum)
call DestroyGroup(g)
set g = null
endfunction
//==============================================================================
private function Init takes nothing returns nothing
local trigger trig
set trig = CreateTrigger()
call TriggerRegisterTimerEvent( trig, PERIOD, true )
call TriggerAddCondition( trig, Condition(function PeriodicRecycler) )
debug set trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-pui", true )
debug call TriggerAddAction( trig, function DisplaySelected )
debug set trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-puistats", true )
debug call TriggerAddAction( trig, function DisplayStats )
endfunction
endlibrary
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitIndex(whichUnit)
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
if .pui_unit[pui] != whichUnit then
// recycled handle detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
//TESH.scrollpos=64
//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")
return
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!")
return
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=21
//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=3
//TESH.alwaysfold=0
library xepreload initializer init requires xebasic, optional TimerUtils
//******************************************************************************
// xepreload 0.8
// ---------
// 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
private keyword DebugIdInteger2IdString
//inline friendly (when debug mode is off..)
function XE_PreloadAbility takes integer abilid returns nothing
call UnitAddAbility(dum, abilid)
static if DEBUG_MODE then
if(dum==null) then
call BJDebugMsg("XE_PreloadAbility: do not load abilities after map init or during structs' onInit")
elseif GetUnitAbilityLevel(dum, abilid) == 0 then
call BJDebugMsg("XE_PreloadAbility: Ability "+DebugIdInteger2IdString.evaluate(abilid)+" does not exist.")
endif
endif
endfunction
// ................................................................................
//================================================================================
// Convert a integer id value into a 4-letter id code.
// * Taken from cheats.j so I don't have to code it again.
// * Used only on debug so making a whole library for it seemed silly
// * Private so people don't begin using xepreload just to call this function....
// * It will not work correctly if you paste this code in the custom script section
// due to the infamous % bug. Then again, if you do that then you probably
// deserve it....
//
private function DebugIdInteger2IdString takes integer value returns string
local string charMap = ".................................!.#$%&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
local string result = ""
local integer remainingValue = value
local integer charValue
local integer byteno
set byteno = 0
loop
set charValue = ModuloInteger(remainingValue, 256)
set remainingValue = remainingValue / 256
set result = SubString(charMap, charValue, charValue + 1) + result
set byteno = byteno + 1
exitwhen byteno == 4
endloop
return result
endfunction
//--------------------------------
private function kill takes nothing returns nothing
call RemoveUnit(dum)
set dum=null
static if (LIBRARY_TimerUtils ) then
call ReleaseTimer( GetExpiredTimer() )
else
call DestroyTimer(GetExpiredTimer())
endif
endfunction
private function init takes nothing returns nothing
local timer t
set dum = CreateUnit( Player(15), XE_DUMMY_UNITID, 0,0,0)
if( dum == null) then
debug call BJDebugMsg("xePreload : XE_DUMMY_UNITID ("+DebugIdInteger2IdString.evaluate(XE_DUMMY_UNITID)+") not added correctly to the map.")
endif
static if (LIBRARY_TimerUtils) then
set t=NewTimer()
else
set t=CreateTimer()
endif
call TimerStart(t,0.0,false,function kill)
set t=null
endfunction
endlibrary
//TESH.scrollpos=51
//TESH.alwaysfold=0
library xecast initializer init requires xebasic
//******************************************************************************
// xecast 0.7
// ------
// Because dummy casters REALLY ARE this complicated!
//
//******************************************************************************
//==============================================================================
globals
private constant integer MAXINSTANCES = 8190
//this is a lot, unless you leak xecast objects (which is a bad thing)
private constant integer INITIAL_DUMMY_COUNT = 12
//don't allow to keep more than DUMMY_STACK_LIMIT innactive dummy units :
private constant integer DUMMY_STACK_LIMIT = 50
// If your map does not give visibility to all players, or
// for other reasons, you might want xecast to work on
// units that are not visible to the player, in that case
// change this to true, else it is just a performance loss.
private constant boolean FORCE_INVISIBLE_CAST = false
//When AUTO_RESET_MANA_COOLDOWN is set to true, xecast will reset
// the dummy unit's cooldown and mana before casting every spell.
// it is a performance penalty, so if you are sure that all dummy spells
// in your map got 0 mana and cooldown cost, you may set it to false.
private constant boolean AUTO_RESET_MANA_COOLDOWN = true
endglobals
//=========================================================================
// Please notice all textmacros in this library are considered private.
// in other words: DON'T RUN THOSE TEXTMACROS!
//
private keyword structinit
globals
private real EPSILON=0.001 //noticed in war3 this is the sort of precision we want...
endglobals
struct xecast[MAXINSTANCES]
public integer abilityid = 0 //ID (rawcode) of the ability to cast
public integer level = 1 //Level of the ability to cast
public real recycledelay = 0.0 //Please notice, some spells need a recycle delay
// This is, a time period before they get recycle.
// For example, some spells are not instant, there is
// also the problem with damaging spells, this recycle
// delay must be large enough to contain all the time
// in which the spell can do damage.
public player owningplayer=Player(15) //which player to credit for the ability cast?
//notice this can also affect what units are targeteable
//==================================================================================================
// You need an order id for the ability so the dummy unit is able to cast it, two ways to assign it
// set instance.orderid = 288883 //would assign an integer orderid
// set instance.orderstring = "chainlightning" //would assign an orderstring
// (as those in the object editor)
//
method operator orderid= takes integer v returns nothing
set .oid=v
endmethod
method operator orderstring= takes string s returns nothing
set .oid=OrderId(s)
endmethod
//=================================================================================================
// Finally, you can determine from which point to cast the ability: z is the height coordinate.
//
public boolean customsource=false //Use a custom casting source?
public real sourcex // Determine the casting source for the dummy spell, require customsource =true
public real sourcey // You might prefer to use the setSourcePoint method
public real sourcez=0.0 //
method setSourcePoint takes real x, real y, real z returns nothing
set .sourcex=x
set .sourcey=y
set .sourcez=z
set .customsource=true
endmethod
method setSourceLoc takes location loc, real z returns nothing
set .sourcex=GetLocationX(loc)
set .sourcey=GetLocationY(loc)
set .sourcez=z
set .customsource=true
endmethod
private boolean autodestroy = false
//========================================================================================================
// you are always allowed to use .create() but you can also use createBasic which sets some things that
// are usually necessary up.
//
public static method createBasic takes integer abilityID, integer orderid, player owner returns xecast
local xecast r=xecast.allocate()
if(r==0) then
debug call BJDebugMsg("Warning: unbelievable but you actually used all xecast instances in your map! Please make sure you are not forgetting to destroy those what you create intensively, if that's not the case, then you'll have to increase xecast MAXINSTANCES")
endif
set r.oid=orderid
set r.abilityid=abilityID
set r.owningplayer=owner
return r
endmethod
//========================================================================================================
// Just like the above one, but the instance will self destruct after a call to any cast method
// (recommended)
//
public static method createBasicA takes integer abilityID, integer orderid, player owner returns xecast
local xecast r=xecast.allocate()
if(r==0) then
debug call BJDebugMsg("Warning: unbelievable but you actually used all xecast instances in your map! Please make sure you are not forgetting to destroy those what you create intensively, if that's not the case, then you'll have to increase xecast MAXINSTANCES")
endif
set r.oid=orderid
set r.abilityid=abilityID
set r.owningplayer=owner
set r.autodestroy=true
return r
endmethod
//==========================================================================================================
// Just like create, but the struct instance self destructs after a call to any cast method
// (Recommended)
//
public static method createA takes nothing returns xecast
local xecast r=xecast.allocate()
set r.autodestroy=true
return r
endmethod
//==========================================================================================================
// So, create the dummy, assign options and cast the skill!
// .castOnTarget(w) : If you want to hit a widget w with the ability
// .castOnPoint(x,y) : If you want to hit a point (x,y) with the ability
// .castInPoint(x,y) : For spells like warstomp which do not have a target.
// .castOnAOE(x,y,radius) : Classic area of effect cast. Considers collision size
// .castOnGroup(g) : Cast unit the unit group g, notice it will empty the group yet not destroy it.
//
//**********************************************************************************************************
// The implementation of such methods follows:
private static unit array dummystack
private static integer top=0
private static unit instantdummy
private integer oid=0
private static timer gametime
private static timer T
private static unit array recycle
private static real array expiretime
private static integer rn=0
//==========================================================================================================
// private dorecycle method, sorry but I need this up here.
//
private static method dorecycle takes nothing returns nothing
local unit u =.recycle[0]
local integer l
local integer r
local integer p
local real lt
call UnitRemoveAbility(u,GetUnitUserData(u))
call SetUnitUserData(u,0)
call SetUnitFlyHeight(u,0,0)
call PauseUnit(u,false)
if(.top==DUMMY_STACK_LIMIT) then
call RemoveUnit(u)
else
set .dummystack[.top]=u
set .top=.top+1
endif
set .rn=.rn-1
if(.rn==0) then
return
endif
set p=0
set lt=.expiretime[.rn]
loop
set l=p*2+1
exitwhen l>=.rn
set r=p*2+2
if(r>=.rn)then
if(.expiretime[l]<lt) then
set .expiretime[p]=.expiretime[l]
set .recycle[p]=.recycle[l]
set p=l
else
exitwhen true
endif
elseif (lt<=.expiretime[l]) and (lt<=.expiretime[r]) then
exitwhen true
elseif (.expiretime[l]<.expiretime[r]) then
set .expiretime[p]=.expiretime[l]
set .recycle[p]=.recycle[l]
set p=l
else
set .expiretime[p]=.expiretime[r]
set .recycle[p]=.recycle[r]
set p=r
endif
endloop
set .recycle[p]=.recycle[.rn]
set .expiretime[p]=lt
call TimerStart(.T, .expiretime[0]-TimerGetElapsed(.gametime), false, function xecast.dorecycle)
endmethod
private static trigger abilityRemove
// Repetitive process and no inline implemented for large functions, so for now it is a textmacro:
//! textmacro xecast_allocdummy
if(.recycledelay<EPSILON) then
set dummy=.instantdummy
call SetUnitOwner(dummy,.owningplayer,false)
elseif (.top>0) then
set .top=.top-1
set dummy=.dummystack[.top]
call SetUnitOwner(dummy,.owningplayer,false)
else
set dummy=CreateUnit(.owningplayer,XE_DUMMY_UNITID,0,0,0)
call TriggerRegisterUnitEvent(.abilityRemove,dummy,EVENT_UNIT_SPELL_ENDCAST)
call UnitAddAbility(dummy,'Aloc')
call UnitAddAbility(dummy,XE_HEIGHT_ENABLER)
call UnitRemoveAbility(dummy,XE_HEIGHT_ENABLER)
endif
call UnitAddAbility(dummy, abilityid)
static if AUTO_RESET_MANA_COOLDOWN then
call UnitResetCooldown(dummy)
call SetUnitState(dummy, UNIT_STATE_MANA, 10000.0)
endif
if(level>1) then
call SetUnitAbilityLevel(dummy, abilityid, level)
endif
//! endtextmacro
private static integer cparent
private static integer current
private static real cexpire
//! textmacro xecast_deallocdummy
if(.recycledelay>=EPSILON) then
set .cexpire=TimerGetElapsed(.gametime)+.recycledelay
set .current=.rn
set .rn=.rn+1
loop
exitwhen (.current==0)
set .cparent=(.current-1)/2
exitwhen (.expiretime[.cparent]<=.cexpire)
set .recycle[.current]=.recycle[.cparent]
set .expiretime[.current]=.expiretime[.cparent]
set .current=.cparent
endloop
set .expiretime[.current]=.cexpire
set .recycle[.current]=dummy
call SetUnitUserData(dummy,.abilityid)
call TimerStart(.T, .expiretime[0]-TimerGetElapsed(.gametime), false, function xecast.dorecycle)
else
call SetUnitUserData(dummy,0)
call SetUnitFlyHeight(dummy,0,0)
call UnitRemoveAbility(dummy,.abilityid)
endif
//! endtextmacro
method castOnTarget takes unit target returns nothing
local unit dummy
local unit tar
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,GetWidgetX(target))
call SetUnitY(dummy,GetWidgetY(target))
endif
if (FORCE_INVISIBLE_CAST) then
call UnitShareVision(target, .owningplayer, true)
call IssueTargetOrderById(dummy,this.oid,target)
call UnitShareVision(target, .owningplayer, false)
else
call IssueTargetOrderById(dummy,this.oid,target)
endif
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
//accepts units, items and destructables, if you know it is
// a unit it is better to use castOnTarget since that would
// be able to use FORCE_INVISIBLE_CAST if necessary.
//
method castOnWidgetTarget takes widget target returns nothing
local unit dummy
local unit tar
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,GetWidgetX(target))
call SetUnitY(dummy,GetWidgetY(target))
endif
call IssueTargetOrderById(dummy,this.oid,target)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castOnPoint takes real x, real y returns nothing
local unit dummy
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,x)
call SetUnitY(dummy,y)
endif
call IssuePointOrderById(dummy,this.oid,x,y)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castOnLoc takes location loc returns nothing
//debug call BJDebugMsg("Warning: Locations are in use")
//nah but I should
call .castOnPoint(GetLocationX(loc),GetLocationY(loc))
endmethod
//ignores custom source x and y (for obvious reasons)
method castInPoint takes real x, real y returns nothing
local unit dummy
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitFlyHeight(dummy,.sourcez,0.0)
endif
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
call IssueImmediateOrderById(dummy,this.oid)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castInLoc takes location loc returns nothing
//debug call BJDebugMsg("Warning: Locations are in use")
//nah but I should
call .castInPoint(GetLocationX(loc),GetLocationY(loc))
endmethod
//===================================================================================================
// For method castOnAOE:
//
private static group enumgroup
private static real aoex
private static real aoey
private static real aoeradius
private static xecast cinstance
private static boolexpr aoefunc
// Might look wrong, but this is the way to make it consider collision size, a spell that
// got a target circle and uses this method will let the user know which units it will
// hit with the mass cast.
static method filterAOE takes nothing returns boolean
local unit u=GetFilterUnit()
if IsUnitInRangeXY(u, .aoex, .aoey, .aoeradius) then
call .cinstance.castOnTarget(u)
endif
set u=null
return false
endmethod
//
method castOnAOE takes real x, real y, real radius returns nothing
local boolean ad=this.autodestroy
if(ad) then
set this.autodestroy=false
endif
set .aoex=x
set .aoey=y
set .aoeradius=radius
set .cinstance=this
call GroupEnumUnitsInRange(.enumgroup,x,y,radius + XE_MAX_COLLISION_SIZE , .aoefunc)
if(ad) then
call this.destroy()
endif
endmethod
method castOnAOELoc takes location loc,real radius returns nothing
call .castOnAOE(GetLocationX(loc),GetLocationY(loc),radius)
endmethod
//==================================================================================================
// A quick and dirt castOnGroup method, perhaps it'll later have castOntarget inlined, but not now
//
method castOnGroup takes group g returns nothing
local boolean ad=this.autodestroy
local unit t
if(ad) then
set this.autodestroy=false
endif
loop
set t=FirstOfGroup(g)
exitwhen(t==null)
call GroupRemoveUnit(g,t)
call .castOnTarget(t)
endloop
if(ad) then
call this.destroy()
endif
endmethod
private static method removeAbility takes nothing returns boolean
local unit u=GetTriggerUnit()
if(GetUnitUserData(u)!=0) then
call PauseUnit(u,true)
endif
//This is necessary, picture a value for recycle delay that's higher than the casting time,
//for example if the spell does dps, if you leave the dummy caster with the ability and it
//is owned by an AI player it will start casting the ability on player units, so it is
// a good idea to pause it...
set u=null
return true
endmethod
//===================================================================================================
// structinit is a scope private keyword.
//
static method structinit takes nothing returns nothing
local integer i=INITIAL_DUMMY_COUNT+1
local unit u
set .aoefunc=Condition(function xecast.filterAOE)
set .enumgroup=CreateGroup()
set .abilityRemove = CreateTrigger()
loop
exitwhen (i==0)
set u=CreateUnit(Player(15),XE_DUMMY_UNITID,0,0,0)
call TriggerRegisterUnitEvent(.abilityRemove,u,EVENT_UNIT_SPELL_ENDCAST)
call UnitAddAbility(u,'Aloc')
call UnitAddAbility(u,XE_HEIGHT_ENABLER)
call UnitRemoveAbility(u,XE_HEIGHT_ENABLER)
set .dummystack[.top]=u
set .top=.top+1
set i=i-1
endloop
call TriggerAddCondition(.abilityRemove, Condition(function xecast.removeAbility ) )
set .top=.top-1
set .instantdummy=.dummystack[.top]
set .T=CreateTimer()
set .gametime=CreateTimer()
call TimerStart(.gametime,12*60*60,false,null)
endmethod
endstruct
private function init takes nothing returns nothing
call xecast.structinit()
endfunction
endlibrary
library xefx initializer init requires xebasic
//**************************************************
// xefx 0.7
// --------
// Recommended: ARGB (adds ARGBrecolor method)
// For your movable fx needs
//
//**************************************************
//==================================================
globals
private constant integer MAX_INSTANCES = 8190 //change accordingly.
private constant real RECYCLE_DELAY = 4.0
//recycling, in order to show the effect correctly, must wait some time before
//removing the unit.
private timer recycler
private timer NOW
endglobals
private struct recyclebin extends array
unit u
real schedule
static recyclebin end=0
static recyclebin begin=0
static method Recycle takes nothing returns nothing
call RemoveUnit(.begin.u) //this unit is private, systems shouldn't mess with it.
set .begin.u=null
set .begin=recyclebin(integer(.begin)+1)
if(.begin==.end) then
set .begin=0
set .end=0
else
call TimerStart(recycler, .begin.schedule-TimerGetElapsed(NOW), false, function recyclebin.Recycle)
endif
endmethod
endstruct
private function init takes nothing returns nothing
set recycler=CreateTimer()
set NOW=CreateTimer()
call TimerStart(NOW,43200,true,null)
endfunction
struct xefx[MAX_INSTANCES]
public integer tag=0
private unit dummy
private effect fx=null
private real zang=0.0
private integer r=255
private integer g=255
private integer b=255
private integer a=255
private integer abil=0
static method create takes real x, real y, real facing returns xefx
local xefx this=xefx.allocate()
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, facing*bj_RADTODEG)
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
return this
endmethod
method operator owner takes nothing returns player
return GetOwningPlayer(this.dummy)
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(this.dummy,p,false)
endmethod
method operator teamcolor= takes playercolor c returns nothing
call SetUnitColor(this.dummy,c)
endmethod
method operator scale= takes real value returns nothing
call SetUnitScale(this.dummy,value,value,value)
endmethod
//! textmacro XEFX_colorstuff takes colorname, colorvar
method operator $colorname$ takes nothing returns integer
return this.$colorvar$
endmethod
method operator $colorname$= takes integer value returns nothing
set this.$colorvar$=value
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
//! endtextmacro
//! runtextmacro XEFX_colorstuff("red","r")
//! runtextmacro XEFX_colorstuff("green","g")
//! runtextmacro XEFX_colorstuff("blue","b")
//! runtextmacro XEFX_colorstuff("alpha","a")
method recolor takes integer r, integer g , integer b, integer a returns nothing
set this.r=r
set this.g=g
set this.b=b
set this.a=a
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
implement optional ARGBrecolor
method operator abilityid takes nothing returns integer
return this.abil
endmethod
method operator abilityid= takes integer a returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(a!=0) then
call UnitAddAbility(this.dummy,a)
endif
set this.abil=a
endmethod
method operator abilityLevel takes nothing returns integer
return GetUnitAbilityLevel( this.dummy, this.abil)
endmethod
method operator abilityLevel= takes integer newLevel returns nothing
call SetUnitAbilityLevel(this.dummy, this.abil, newLevel)
endmethod
method flash takes string fx returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx,this.dummy,"origin"))
endmethod
method operator xyangle takes nothing returns real
return GetUnitFacing(this.dummy)*bj_DEGTORAD
endmethod
method operator xyangle= takes real value returns nothing
call SetUnitFacing(this.dummy,value*bj_RADTODEG)
endmethod
method operator zangle takes nothing returns real
return this.zang
endmethod
method operator zangle= takes real value returns nothing
local integer i=R2I(value*bj_RADTODEG+90.5)
set this.zang=value
if(i>=180) then
set i=179
elseif(i<0) then
set i=0
endif
call SetUnitAnimationByIndex(this.dummy, i )
endmethod
method operator x takes nothing returns real
return GetUnitX(this.dummy)
endmethod
method operator y takes nothing returns real
return GetUnitY(this.dummy)
endmethod
method operator z takes nothing returns real
return GetUnitFlyHeight(this.dummy)
endmethod
method operator z= takes real value returns nothing
call SetUnitFlyHeight(this.dummy,value,0)
endmethod
method operator x= takes real value returns nothing
call SetUnitX(this.dummy,value)
endmethod
method operator y= takes real value returns nothing
call SetUnitY(this.dummy,value)
endmethod
method operator fxpath= takes string newpath returns nothing
if (this.fx!=null) then
call DestroyEffect(this.fx)
endif
if (newpath=="") then
set this.fx=null
else
set this.fx=AddSpecialEffectTarget(newpath,this.dummy,"origin")
endif
endmethod
method hiddenReset takes string newfxpath, real newfacing returns nothing
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
local real z = this.z
local real za = this.zangle
local integer level = this.abilityLevel
set fxpath=null
call RemoveUnit(this.dummy)
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, newfacing*bj_RADTODEG)
if(level != 0) then
call UnitAddAbility(this.dummy, abilityid)
endif
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
set this.z = z
set zangle = za
endmethod
private method onDestroy takes nothing returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(this.fx!=null) then
call DestroyEffect(this.fx)
set this.fx=null
endif
if (recyclebin.end==MAX_INSTANCES) then
//I'd like to see this happen...
call TimerStart(recycler,0,false,function recyclebin.Recycle)
call ExplodeUnitBJ(this.dummy)
else
set recyclebin.end.u=this.dummy
set recyclebin.end.schedule=TimerGetElapsed(NOW)+RECYCLE_DELAY
set recyclebin.end= recyclebin( integer(recyclebin.end)+1)
if( recyclebin.end==1) then
call TimerStart(recycler, RECYCLE_DELAY, false, function recyclebin.Recycle)
endif
call SetUnitOwner(this.dummy,Player(15),false)
endif
set this.dummy=null
endmethod
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=540
//TESH.alwaysfold=0
library xedamage initializer init requires xebasic
//************************************************************************
// xedamage 0.8
// --------
// For all your damage and targetting needs.
//
//************************************************************************
//===========================================================================================================
globals
private constant integer MAX_SUB_OPTIONS = 3
private constant real FACTOR_TEST_DAMAGE = 0.01
// a low damage to do on units to test their damage factors for specific
// attacktype/damagetype combos.
// You'll need something as high as 20.0 to make it work well with armor resistances.
// (Yes, you read it correctly, 20 ...
//
// If you use such a large value, there may be conflicts with some things relying on damage
// (ie they are not away of the isDummyDamage tag that xedamage posseses.) which may be quite bad if you think about it...
// then maybe it is better to change it to 0.01 ... then will work fine, just fine - but it will generally ignore armor -
// I am leaving it as 0.01 by default, because honestly, I'd rather make it ignore armor than have a lot of people sending me
// rants about how they detect 20.0 damage where none is visible...
private constant real MAX_DAMAGE_FACTOR = 3.00
// The maximum damage factor in the map. I think 3 is fine.
//=======================================================
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
readonly static boolean isDummyDamage = false
//========================================================================================================
// 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
local real fc = FACTOR_TEST_DAMAGE
//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< FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR) then
call SetWidgetLife(u, hp + FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR )
set r = hp + FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR
set fc = GetWidgetLife(u)-hp + EPSILON
endif
set isDummyDamage = true
call UnitDamageTarget(dmger,u, fc ,false,false,a,d,null)
static if DEBUG_MODE then
if IsUnitType(u, UNIT_TYPE_DEAD) and (hp>0.405) then
call BJDebugMsg("xedamage: For some reason, the unit being tested by getDamageTypeFactor has died. Verify MAX_DAMAGE_FACTOR is set to a correct value. ")
endif
endif
set isDummyDamage = false
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)) / fc
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
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
if(pl != GetWidgetLife(target) ) then
if(usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
return true
endif
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
local real hp
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
set hp = GetWidgetLife(target)
endif
call UnitDamageTarget(source,target, f*damage, true, .ranged, .atype, .dtype, .wtype )
if(usefx and (hp > GetWidgetLife(target)) ) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
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
local real hp
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
if(this.usefx) then
set hp =GetWidgetLife(target)
endif
call UnitDamageTarget(.sourceAOE,target, f*this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
if(this.usefx and (hp > GetWidgetLife(target) ) ) 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
//TESH.scrollpos=46
//TESH.alwaysfold=0
library xecollider initializer init requires xefx, xebasic
//****************************************************************
//*
//* xecollider 0.8
//* --------------
//* A xecollider object is a special effect that has a collision
//* size that can trigger a hit event and also many options to
//* configure its automatic movement.
//*
//* Please use .terminate() instead of .destroy() this ensures
//* that it will be safe to destroy it (else you would have to
//* worry about destroying it during the animation loop/etc.)
//*
//* To use this struct is a little different than the other
//* current parts of xe. Instead of just creating the xecollider
//* (which works, but it would only be a xefx that can have speed)
//* you probably need to make it do something special on the
//* unit hit event... For this reason, you need to make a new
//* struct extending xecollider that declares an onUnitHit method
//* you may also declare a loopControl method, very useful, can
//* help you reduce 'attaching'.
//*
//****************************************************************
//================================================================
globals
private constant real DEFAULT_COLLISION_SIZE = 50.0 // These are defaults, on one hand you can change them
private constant real DEFAULT_MAX_SPEED = 1500.0 // on the other hand, if a spell relies on the defaults
private constant real DEFAULT_EXPIRATION_TIME = 5.0 // changing them would make the behavior vary...
private constant real PI2 = 6.28318 //It might not be wise to change this
endglobals
//===========================================================================
// So, this exists merely so you can declare your own event handler methods
// if interfaces make your brain blow out, please skip the next four lines.
//
private interface eventHandler
method onUnitHit takes unit hitTarget returns nothing defaults nothing
method loopControl takes nothing returns nothing defaults nothing
endinterface
//===========================================================================
struct xecollider extends eventHandler
// use terminate() instead of .destroy() to "kill" the collider.
// don't worry, terminate will call destroy automatically.
//============================================================================
// delegates:
// We are delegating a xefx object so that people call all the xefx methods
// and member from a xecollider object. This means that from a user
// perspective a xecollider is also an instance of xefx.
//
// Notable ones are: .x , .y , .fxpath and .z ,
// check out xefx's documentation for more info.
//
private delegate xefx fx
//##==========================================================================
// public variables:
//
public real expirationTime = DEFAULT_EXPIRATION_TIME
// Movement speed for the missile.
public real speed = 0.0
// Speed added per second (notice you can use a negative value here)
public real acceleration = 0.0
// If there is acceleration, it is wise to have a cap...
public real maxSpeed = DEFAULT_MAX_SPEED
public real minSpeed = 0.0
public real angleSpeed = 0.0 //The increment in radians per second to the
// direction angle, allows curved movement.
private static integer lastSeen = 0
private group seen
private boolean silent = false
//##==========================================================================
// public methods:
//
//----
// Well, it is a good idea to actually create the missiles.
// notice that if your custom missile struct needs to declare its own create
// method, you can call this as allocate(x,y,dir).
//
// Sorry, no Loc version.
//
public static method create takes real x, real y, real dir returns xecollider
local xecollider xc= xecollider.allocate()
set xc.fx = xefx.create(x,y,dir)
set xc.dir=dir
set xecollider.V[xecollider.N]=xc
set xecollider.N=xecollider.N+1
if(xecollider.N==1) then
call TimerStart(xecollider.T, XE_ANIMATION_PERIOD, true, xecollider.timerLoopFunction )
endif
if(.lastSeen < integer(xc)) then //with this I do group recycling
set .lastSeen = integer(xc)
set xc.seen = CreateGroup()
endif
return xc
endmethod
//----
// The direction is just the angle in radians to which the missile's model faces
// and the automatic movement uses.
//
method operator direction takes nothing returns real
return this.dir
endmethod
method operator direction= takes real v returns nothing
set this.dir=v
set this.fx.xyangle=v
endmethod
//----
// The collisionSize
//
method operator collisionSize takes nothing returns real
return this.csize
endmethod
method operator collisionSize= takes real value returns nothing
set this.csize = value
//good long attribute name, but we use csize in the loop
//don't worry this gets inlined, it would also be helpful if
//I ever decide to add a control for assignment.
endmethod
//---
// targetUnit is a unit to follow (or try to follow), notice that homing
// options require an angleSpeed different to 0.0
//
public method operator targetUnit takes nothing returns unit
return this.homingTargetUnit
endmethod
public method operator targetUnit= takes unit u returns nothing
if(u==null) then
set this.angleMode= ANGLE_NO_MOVEMENT
else
set this.angleMode= ANGLE_HOMING_UNIT
endif
set this.homingTargetUnit=u
endmethod
//----
// targetPoint is a point to reach (or try to reach), notice that homing
// options require an angleSpeed different to 0.0
//
public method setTargetPoint takes real x, real y returns nothing
set this.angleMode= ANGLE_HOMING_POINT
set this.homingTargetX=x
set this.homingTargetY=y
endmethod
public method setTargetPointLoc takes location loc returns nothing
set this.angleMode= ANGLE_HOMING_POINT
set this.homingTargetX=GetLocationX(loc)
set this.homingTargetY=GetLocationY(loc)
endmethod
//----
// Call this in case you used targetUnit or TargetPoint so the missile
// forgets the order to home that target.
//
public method forgetTarget takes nothing returns nothing
set this.angleMode = ANGLE_NO_MOVEMENT
endmethod
public method operator rotating takes nothing returns boolean
return (angleMode ==ANGLE_ROTATING)
endmethod
public method operator rotating= takes boolean val returns nothing
if(val) then
set angleMode = ANGLE_ROTATING
elseif (angleMode == ANGLE_ROTATING) then
set angleMode = ANGLE_NO_MOVEMENT
endif
endmethod
method terminate takes nothing returns nothing
set this.dead=true
set this.fxpath=""
endmethod
/* declare hiddenDestroy so people don't call directly on the delegate xefx */
method hiddenDestroy takes nothing returns nothing
set silent = true
call terminate()
endmethod
//--------
private static timer T
private static integer N=0
private static xecollider array V
private static code timerLoopFunction //I use a code var so create can be above the timerloop function, more readable
private boolean dead=false
private real csize = DEFAULT_COLLISION_SIZE
private real dir
private static constant integer ANGLE_HOMING_UNIT =1
private static constant integer ANGLE_HOMING_POINT=2
private static constant integer ANGLE_NO_MOVEMENT=0
private static constant integer ANGLE_ROTATING=3
private integer angleMode =0
private unit homingTargetUnit = null
private real homingTargetX
private real homingTargetY
private method onDestroy takes nothing returns nothing
call GroupClear(this.seen)
if(this.silent) then
call this.fx.hiddenDestroy()
else
call this.fx.destroy()
endif
endmethod
private static xecollider cinstance
private static real newx
private static real newy
private static group enumGroup
private static boolexpr enumBoolexpr
private static unit array picked
private static integer pickedN
static method timerLoop takes nothing returns nothing
local integer i=0
local integer j=0
local integer c=0
local xecollider this
local real d
local real ns
local real wa
local real df1
local real df2
local unit u
loop
exitwhen (i== xecollider.N )
set this=.V[i] //adopt-a-instance
set this.expirationTime = this.expirationTime - XE_ANIMATION_PERIOD
if(.dead or (this.expirationTime <=0.0) ) then
call this.destroy()
else
set ns=this.angleSpeed*XE_ANIMATION_PERIOD
if (ns!=0.0) then
if(this.angleMode== ANGLE_HOMING_UNIT ) then
set u=this.homingTargetUnit
if ( (GetUnitTypeId(u)==0) or IsUnitType(u, UNIT_TYPE_DEAD) ) then
set this.angleMode= ANGLE_NO_MOVEMENT
set this.homingTargetUnit = null
else
set this.homingTargetX=GetUnitX(u)
set this.homingTargetY=GetUnitY(u)
endif
set u=null
endif
if (this.angleMode == ANGLE_ROTATING) then
//nothing (ns is already ns)
elseif( this.angleMode != ANGLE_NO_MOVEMENT) then
if(ns<=0) then
set ns=-ns
endif
set wa=Atan2(this.homingTargetY - this.y , this.homingTargetX-this.x)
//if(wa<0.0) then
// set wa=wa+PI2
//endif
set df1=wa-this.dir
set df2=(PI2+wa)-this.dir
if (df1<=0) then
if(df2<=0) then
if(df2>=df1) then
set df1=df2
endif
else
if(-df1>=df2) then
set df1=df2
endif
endif
else
if(df2<=0) then
if(-df2<=df1) then
set df1=df2
endif
else
if(df2<=df1) then
set df1=df2
endif
endif
endif
if(df1<=0) then
if(-df1>=ns) then
set ns=-ns
else
set ns=df1
endif
else
if(df1<=ns) then
set ns=df1
endif
endif
else
set ns = 0
endif
set d=this.dir
set d = d + ns
if(d>=PI2) then
set d=d - PI2
elseif(d<0) then
set d=d + PI2
endif
set this.dir = d
set this.xyangle = d
endif
// function calls are expensive, damned we are, long code inside of loop
// correct software dev. tells us this should go to another function,
// but this is Jass, not real life.
set .cinstance = this
set ns = this.speed + this.acceleration*XE_ANIMATION_PERIOD
if ( ns<this.minSpeed) then
set ns=this.minSpeed
elseif (ns>this.maxSpeed) then
set ns=this.maxSpeed
endif
set d=((this.speed+ns)/2) * XE_ANIMATION_PERIOD
set this.speed=ns
set .newx= .x+d*Cos(this.dir)
set .newy= .y+d*Sin(this.dir)
set .x=.newx
set .y=.newy
set xecollider.pickedN = 0
call GroupEnumUnitsInRange( .enumGroup, .newx, .newy, .csize + XE_MAX_COLLISION_SIZE, .enumBoolexpr)
call GroupClear(this.seen)
set j=0
loop
exitwhen (j==xecollider.pickedN)
call GroupAddUnit( this.seen, xecollider.picked[j])
set j=j+1
endloop
set .V[c]=this
set c=c+1
if( this.loopControl.exists and not this.dead ) then
call this.loopControl()
endif
endif
set i=i+1
endloop
//call BJDebugMsg("}")
set xecollider.N=c
if(c==0) then
call PauseTimer(xecollider.T)
endif
endmethod
private static method inRangeEnum takes nothing returns boolean
local xecollider this= .cinstance //adopt-a-instance
local unit u=GetFilterUnit()
if not IsUnitType(u, UNIT_TYPE_DEAD) and not(this.dead) and (GetUnitTypeId(u)!=XE_DUMMY_UNITID) and IsUnitInRangeXY(u, .newx, .newy, .csize) then
// ah, the advantages of a standardized unit id...
set xecollider.picked[xecollider.pickedN] = u
set xecollider.pickedN=xecollider.pickedN + 1
if not IsUnitInGroup (u, this.seen ) then
call this.onUnitHit(u)
endif
endif
set u=null
return false
endmethod
//============================================================================
// you aren't supposed to call doInit yourself, try not to do it.
//
static method doInit takes nothing returns nothing
set xecollider.enumGroup = CreateGroup()
set xecollider.enumBoolexpr = Condition( function xecollider.inRangeEnum)
set xecollider.timerLoopFunction = (function xecollider.timerLoop)
set xecollider.T=CreateTimer()
endmethod
endstruct
private function init takes nothing returns nothing
call xecollider.doInit()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MapUtils initializer init requires TimerUtils
globals
private constant integer HERO_ID='Emoo'
private constant string MAP_NAME=" |cff8b814c A R A C H N I A N Q U E E N|r"
private unit hero
private integer lvl=1
private dialog dg=DialogCreate()
private button array bt[5]
private real x=GetStartLocationX(0)
private real y=GetStartLocationY(0)
private camerasetup CS=CreateCameraSetup()
endglobals
private function CreateHero takes nothing returns nothing
set hero=CreateUnit(Player(0),HERO_ID,x,y,GetRandomReal(0.,360.))
call SetCameraPosition(x,y)
call ClearSelection()
call SelectUnit(hero,true)
call SetMapMusic("Sound\\Music\\mp3Music\\IllidansTheme.mp3",false,0)
call DisplayTimedTextToPlayer(Player(0),0,0,20.,MAP_NAME)
call DisplayTimedTextToPlayer(Player(0),0,0,20.," |cffcdbe70 By: scorpion182 aka ranzi")
call DisplayTimedTextToPlayer(Player(0),0,0,20.,"|cffcdbe70Press Esc to show dialog stuff.")
endfunction
private function CameraLock takes nothing returns nothing
call CameraSetupApplyForceDuration(CS,false,0)
endfunction
private function Stuff takes nothing returns nothing
local real minX = GetRectMinX(gg_rct_Arena)
local real minY = GetRectMinY(gg_rct_Arena)
local real maxX = GetRectMaxX(gg_rct_Arena)
local real maxY = GetRectMaxY(gg_rct_Arena)
local timer t=NewTimer()
call SetCameraBounds(minX, minY, minX, maxY, maxX, maxY, maxX, minY)
call FogEnable(false)
call FogMaskEnable(false)
call CameraSetupSetField(CS,CAMERA_FIELD_TARGET_DISTANCE,2500.0,.0)
call CameraSetupApplyForceDuration(CS,false,0)
call TimerStart(t,0.1,true,function CameraLock)
set t=null
endfunction
private function StartDialog takes nothing returns nothing
call DialogDisplay(Player(0),dg,false)
call DialogClear(dg)
call DialogSetMessage(dg,"Setup")
set bt[0]=DialogAddButton(dg,"Level: "+I2S(lvl),0)
set bt[1]=DialogAddButton(dg,"Refresh",0)
set bt[2]=DialogAddButton(dg,"Reset Cooldown",0)
set bt[3]=DialogAddButton(dg,"Revive",0)
set bt[4]=DialogAddButton(dg,"Close",0)
call DialogDisplay(Player(0),dg,true)
endfunction
private function DialogSetup takes nothing returns nothing
if (GetClickedButton()!=bt[4]) then
if (GetClickedButton()==bt[0]) then
set lvl=lvl+1
if (lvl>25) then
set lvl=1
endif
call SetHeroLevel(hero,lvl,false)
elseif (GetClickedButton()==bt[1]) then
call SetUnitState(hero,UNIT_STATE_LIFE,GetUnitState(hero,UNIT_STATE_MAX_LIFE))
call SetUnitState(hero,UNIT_STATE_MANA,GetUnitState(hero,UNIT_STATE_MAX_MANA))
elseif (GetClickedButton()==bt[2]) then
call UnitResetCooldown(hero)
elseif (GetClickedButton()==bt[3]) then
call ReviveHero(hero,x,y,false)
endif
call StartDialog()
endif
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterPlayerEventEndCinematic(t, Player(0) )
call TriggerAddAction(t, function StartDialog)
set t= CreateTrigger( )
call TriggerRegisterDialogEventBJ(t, dg )
call TriggerAddAction(t, function DialogSetup )
call CreateHero()
call Stuff()
set t=null
endfunction
endlibrary
//TESH.scrollpos=44
//TESH.alwaysfold=0
library CreepRevival initializer init requires PUI
globals
private integer array UnitData
private real array CreepX
private real array CreepY
endglobals
//set unit's integer
private function AttachIntegerToUnit takes unit u, integer i returns nothing
set UnitData[GetUnitIndex(u)] = i
endfunction
//get unit's integer
private function GetIntegerFromUnit takes unit u returns integer
return UnitData[GetUnitIndex(u)]
endfunction
private function Conditions takes nothing returns boolean
return GetOwningPlayer(GetTriggerUnit())==Player(PLAYER_NEUTRAL_AGGRESSIVE)
endfunction
private function Creeps takes nothing returns boolean
return(IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)==false)and(GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE)>0) and GetOwningPlayer(GetFilterUnit())==Player(PLAYER_NEUTRAL_AGGRESSIVE)
endfunction
private function Actions takes nothing returns nothing
local unit u=GetDyingUnit()
local unit d
local integer idx=GetIntegerFromUnit(u)
local real x=CreepX[idx]
local real y=CreepY[idx]
call TriggerSleepAction(45.00)
set d=CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE),GetUnitTypeId(u), x,y, GetRandomReal(0, 360))
call AttachIntegerToUnit(d,idx)
set d=null
set u=null
endfunction
private function CreepInit takes nothing returns nothing
local unit u=GetEnumUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
call AttachIntegerToUnit(u,GetUnitIndex(u))
set CreepX[GetIntegerFromUnit(u)]=x
set CreepY[GetIntegerFromUnit(u)]=y
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local group CreepG=CreateGroup()
local boolexpr b=Condition(function Creeps)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition(t, Condition( function Conditions ) )
call TriggerAddAction(t, function Actions )
call GroupEnumUnitsInRect(CreepG,bj_mapInitialPlayableArea,b)
call ForGroup(CreepG,function CreepInit)
call DestroyGroup(CreepG)
call DestroyBoolExpr(b)
set CreepG=null
set t=null
endfunction
endlibrary
//TESH.scrollpos=67
//TESH.alwaysfold=0
library SplitShot initializer init requires xedamage, xecast, xecollider
//******************************************************************************
//* *
//* Arachnian Queen Spellpack *
//* v1.01b *
//* *
//* By: scorpion182 aka ranzi aka ada_aja *
//* http://www.jade-wars.com *
//* *
//******************************************************************************
//*************************************************************
//* Configuration Constants
globals
private constant integer SPELL_ID='A000' //split shot ability rawcode
private constant integer SPELL_ID2='Apo2' //poison sting (orb of venom) rawcode
private constant integer DUMMY_SPELL_ID='A004' //dummy slow rawcode
private constant string DUMMY_ORDER="slow" //dummy slow order string
private constant real SCALE=2.0 //missile scale
private constant real RADIUS=75. //misssile radius
private constant real TIME=1.5 //missile expirationTime
private constant real SPEED=1000. //missile speed
private constant real START_DISTANCE=50. //arrow distance from the caster
private constant string NORMAL_FX="Abilities\\Weapons\\MoonPriestessMissile\\MoonPriestessMissile.mdl" //normal arrows
private constant string POISON_A_FX="Abilities\\Weapons\\PoisonArrow\\PoisonArrowMissile.mdl" //poison arrows
private constant string HIT_FX1="Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl" //normal arrows
private constant string HIT_FX2="Objects\\Spawnmodels\\Naga\\NagaBlood\\NagaBloodWindserpent.mdl" //poison arrows
endglobals
private constant function GetArrows takes integer lvl returns integer
return 2+1*lvl //how many arrows each level
endfunction
private constant function GetDamage takes integer lvl returns real
return 20.+10.*lvl// amount damage each missile
endfunction
private constant function GetConeAngle takes integer lvl returns real
return 40. //total cone angle each level
endfunction
private function DamageOptions takes xedamage spellDamage returns nothing
set spellDamage.dtype=DAMAGE_TYPE_UNIVERSAL
set spellDamage.atype=ATTACK_TYPE_NORMAL
set spellDamage.exception=UNIT_TYPE_STRUCTURE
set spellDamage.visibleOnly=true
set spellDamage.damageAllies=false
endfunction
//* Configuration End
//*****************************************************************
globals
private xedamage xed
private xecast cast
endglobals
private function SetDummy takes xecast DummySpell returns nothing
set DummySpell.abilityid=DUMMY_SPELL_ID
set DummySpell.orderstring=DUMMY_ORDER
endfunction
private function IsEven takes real r returns boolean
local integer i = R2I(r)
set i = i/2
set i= i+i
return i == r
endfunction
//distance between X cord
private function PolarProjectionX takes real x, real distance, real angle returns real
return x+distance*Cos(angle * bj_DEGTORAD)
endfunction
//distance between Y cord
private function PolarProjectionY takes real y, real distance, real angle returns real
return y+distance*Sin(angle * bj_DEGTORAD)
endfunction
private struct missile extends xecollider
unit caster
integer lvl
method onUnitHit takes unit target returns nothing
if (this.caster != target) then
call xed.damageTarget(this.caster,target, GetDamage(this.lvl))
if (GetUnitAbilityLevel(this.caster,SPELL_ID2)==0) then
call DestroyEffect(AddSpecialEffectTarget(HIT_FX1,target,"origin"))
else
call DestroyEffect(AddSpecialEffectTarget(HIT_FX2,target,"origin"))
set cast.owningplayer = GetOwningPlayer(this.caster)
call cast.castOnTarget(target)
endif
endif
endmethod
endstruct
private function SpellEffect takes nothing returns nothing
local missile d
local unit u
local integer i=0
local integer j=0
local integer lvl
local real x
local real y
local integer count
local real angle
local real facing=0.
if GetSpellAbilityId() == SPELL_ID then
set u=GetSpellAbilityUnit()
set lvl=GetUnitAbilityLevel(u,SPELL_ID)
set x=PolarProjectionX(GetUnitX(u),START_DISTANCE,GetUnitFacing(u))
set y=PolarProjectionY(GetUnitY(u),START_DISTANCE,GetUnitFacing(u))
set count=GetArrows(lvl)
set angle=Atan2(GetSpellTargetY() - y, GetSpellTargetX() - x)
if not IsEven(count) then
set d=missile.create(x,y,angle+facing*bj_DEGTORAD)
set d.scale=SCALE
set d.collisionSize=RADIUS
set d.direction=angle+facing*bj_DEGTORAD
set d.expirationTime = TIME
set d.speed = SPEED
set d.caster=u
set d.lvl=lvl
if (GetUnitAbilityLevel(d.caster,SPELL_ID2)==0) then
set d.fxpath=NORMAL_FX
else
set d.fxpath=POISON_A_FX
endif
set i=i+1
set facing=GetConeAngle(lvl)/count
loop
exitwhen j==count/2
set d=missile.create(x,y,angle+facing*bj_DEGTORAD)
set d.scale=SCALE
set d.collisionSize=RADIUS
set d.direction=angle+facing*bj_DEGTORAD
set d.expirationTime = TIME
set d.speed = SPEED
set d.caster=u
set d.lvl=lvl
if (GetUnitAbilityLevel(d.caster,SPELL_ID2)==0) then
set d.fxpath=NORMAL_FX
else
set d.fxpath=POISON_A_FX
endif
set facing=facing/2.
set i=i+1
set j=j+1
endloop
set j=1
set facing=GetConeAngle(lvl)/count
set facing=facing*-1
loop
exitwhen j>count/2
set d=missile.create(x,y,angle+facing*bj_DEGTORAD)
set d.scale=SCALE
set d.collisionSize=RADIUS
set d.direction=angle+facing*bj_DEGTORAD
set d.expirationTime = TIME
set d.speed = SPEED
set d.caster=u
set d.lvl=lvl
if (GetUnitAbilityLevel(d.caster,SPELL_ID2)==0) then
set d.fxpath=NORMAL_FX
else
set d.fxpath=POISON_A_FX
endif
set facing=facing/2.
set j=j+1
set i=i+1
endloop
else
set facing=GetConeAngle(lvl)/count
loop
exitwhen j==count/2
set d=missile.create(x,y,angle+facing*bj_DEGTORAD)
set d.scale=SCALE
set d.collisionSize=RADIUS
set d.direction=angle+facing*bj_DEGTORAD
set d.expirationTime = TIME
set d.speed = SPEED
set d.caster=u
set d.lvl=lvl
if (GetUnitAbilityLevel(d.caster,SPELL_ID2)==0) then
set d.fxpath=NORMAL_FX
else
set d.fxpath=POISON_A_FX
endif
set facing=facing/2.
set i=i+1
set j=j+1
endloop
set j=1
set facing=GetConeAngle(lvl)/count
set facing=facing*-1
loop
exitwhen j>count/2
set d=missile.create(x,y,angle+facing*bj_DEGTORAD)
set d.scale=SCALE
set d.collisionSize=RADIUS
set d.direction=angle+facing*bj_DEGTORAD
set d.expirationTime = TIME
set d.speed = SPEED
set d.caster=u
set d.lvl=lvl
if (GetUnitAbilityLevel(d.caster,SPELL_ID2)==0) then
set d.fxpath=NORMAL_FX
else
set d.fxpath=POISON_A_FX
endif
set facing=facing/2.
set j=j+1
set i=i+1
endloop
endif
endif
set u=null
endfunction
private function init takes nothing returns nothing
//init spellcast trigger
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t,function SpellEffect)
//init xedamage
set xed=xedamage.create()
call DamageOptions(xed)
//init xecast
set cast = xecast.create()
call SetDummy(cast)
//preload dummy ability
call XE_PreloadAbility(DUMMY_SPELL_ID)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//******************************************************************************
//* *
//* Arachnian Queen Spellpack *
//* v1.01b *
//* *
//* By: scorpion182 aka ranzi aka ada_aja *
//* http://www.jade-wars.com *
//* *
//******************************************************************************
library Web initializer init requires xecollider, xecast, TimerUtils
//*************************************************************
//* Configuration Constants
globals
private constant integer SPELL_ID='A005' //web ability rawcode
private constant integer BUFF_ID='B002' //web buff rawcode
private constant integer SLOW_BUFF='Bslo' //slow buff rawcode
private constant integer DUMMY_SPELL_ID='A001' //dummy slow rawcode
private constant string DUMMY_ORDER="slow" //dummy slow order string
private constant real RADIUS=150. //web collosion size radius
private constant real HEIGHT=100. //web height
private constant string FX="Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl" //caster fx
private constant string WEB_FX="Abilities\\Spells\\Undead\\Web\\WebTarget.mdl" //web fx
private constant real SCALE=1.0 //web scale
private constant string ATT_POINT="origin" //caster fx attachment point
private constant real WEB_DURATION=10. //web duration
private constant real MIN_DIST=100. //minimum distance between webs
private constant real WEB_INTERVAL=0.35 //web create interval
endglobals
//* Configuration End
//*****************************************************************
globals
private xecast cast
endglobals
private struct data
unit caster
timer t
real x1=0
real x2=0
real y1=0
real y2=0
static method create takes unit u, timer t returns data
local data d=data.allocate()
set d.caster=u
set d.t=t
return d
endmethod
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
private function SetDummy takes xecast DummySpell returns nothing
set DummySpell.abilityid=DUMMY_SPELL_ID
set DummySpell.orderstring=DUMMY_ORDER
endfunction
private function IsSlowed takes unit u returns boolean
return GetUnitAbilityLevel(u,SLOW_BUFF)>0
endfunction
private struct web extends xecollider
unit caster
method onUnitHit takes unit target returns nothing
local xecast xc
if (this.caster != target and IsSlowed(target)==false) then
set xc=xecast.createA()
set cast.owningplayer = GetOwningPlayer(this.caster)
call cast.castOnTarget(target)
endif
endmethod
endstruct
private function Check takes nothing returns nothing
local timer t=GetExpiredTimer()
local data d=data(GetTimerData(t))
local real dx = d.x2 - d.x1
local real dy = d.y2 - d.y1
local real dis= SquareRoot(dx * dx + dy * dy)
local web xc
if (GetUnitAbilityLevel(d.caster,BUFF_ID)>0) then
set d.x1=d.x2
set d.y1=d.y2
if (dis>MIN_DIST) then
set xc=web.create(GetUnitX(d.caster),GetUnitY(d.caster),GetUnitFacing(d.caster))
set xc.scale=SCALE
set xc.collisionSize=RADIUS
set xc.expirationTime=WEB_DURATION
set xc.fxpath=WEB_FX
set xc.z=HEIGHT
set xc.caster=d.caster
endif
set d.x2=GetUnitX(d.caster)
set d.y2=GetUnitY(d.caster)
call DestroyEffect(AddSpecialEffectTarget(FX,d.caster,ATT_POINT))
else
call d.destroy()
endif
endfunction
private function Actions takes nothing returns nothing
local unit u
local timer t
local data d
if GetSpellAbilityId() == SPELL_ID then
set u=GetSpellAbilityUnit()
set t=NewTimer()
set d=data.create(u,t)
call SetTimerData(t,integer(d))
call TimerStart(t,WEB_INTERVAL,true,function Check)
endif
set u=null
endfunction
private function init takes nothing returns nothing
//init spellcast trigger
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddAction(t, function Actions )
//init xecast
set cast = xecast.create()
call SetDummy(cast)
//preload dummy ability
call XE_PreloadAbility(DUMMY_SPELL_ID)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//******************************************************************************
//* *
//* Arachnian Queen Spellpack *
//* v1.01b *
//* *
//* By: scorpion182 aka ranzi aka ada_aja *
//* http://www.jade-wars.com *
//* *
//******************************************************************************
library Eggshack initializer init requires xefx, TimerUtils
//*************************************************************
//* Configuration Constants
globals
private constant integer SPELL_ID='A006' //eggshack ability rawcode
private constant integer BUFF_ID='B004' //eggshack buff
private constant integer BEETLE='ucs1' //beetle unit rawcode
private constant integer DUMMY_ID='e000' //dummy unit rawcode
private constant string FX="Doodads\\Dungeon\\Terrain\\EggSack\\EggSack1.mdl" //eggshack fx
private constant integer NUM=3 //how many beetle spawns
private constant real EGG_DISTANCE=-35.0 //egg distance from caster
private constant real EGG_HEIGHT=100. //egg height
private constant real PATROL_RADIUS=500. //beetle spawns patrol radius
private integer array B_ABILITY[3]
endglobals
//beetle spawn abilities
private function InitBSpells takes nothing returns nothing
//set B_ABILITY[0]='ACct'
set B_ABILITY[1]='ACct' //critical strike
set B_ABILITY[2]='ACbh' //bash
//...and so on
endfunction
private constant function Duration takes integer lvl returns real
return 30.+0.*lvl //beetle spawns duration
endfunction
private constant function LifeCost takes integer lvl returns real
return 5.*lvl //life cost per second
endfunction
//* Configuration End
//*****************************************************************
private struct data
unit caster
timer t
unit Egg
unit array Beetle[NUM]
boolean patrol=false
real counter=0.
real hpcost=0.
integer lvl
xefx fx
static method create takes unit c, timer t returns data
local data d=data.allocate()
local integer lvl=GetUnitAbilityLevel(c,SPELL_ID)
set d.caster=c
set d.t=t
set d.counter=Duration(lvl)
set d.hpcost=LifeCost(lvl)
set d.lvl=lvl
set d.fx=xefx.create(GetUnitX(c),GetUnitY(c),GetUnitFacing(c))
set d.fx.z=EGG_HEIGHT
set d.fx.fxpath=FX
return d
endmethod
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
//distance between X cord
private function PolarProjectionX takes real x, real distance, real angle returns real
return x+distance*Cos(angle * bj_DEGTORAD)
endfunction
//distance between Y cord
private function PolarProjectionY takes real y, real distance, real angle returns real
return y+distance*Sin(angle * bj_DEGTORAD)
endfunction
private function Check takes nothing returns nothing
local timer t=GetExpiredTimer()
local data d=data(GetTimerData(t))
local real x=PolarProjectionX(GetUnitX(d.caster),EGG_DISTANCE,GetUnitFacing(d.caster))
local real y=PolarProjectionY(GetUnitY(d.caster),EGG_DISTANCE,GetUnitFacing(d.caster))
local rect r
local integer i=0
if (GetUnitAbilityLevel(d.caster,BUFF_ID)>0) then
set d.fx.x=x
set d.fx.y=y
set d.fx.z=EGG_HEIGHT+GetUnitFlyHeight(d.caster)
call SetUnitState(d.caster,UNIT_STATE_LIFE,GetUnitState(d.caster,UNIT_STATE_LIFE)-d.hpcost*.035)
call TimerStart(t,XE_ANIMATION_PERIOD,false,function Check)
elseif (d.patrol==false) then
call d.fx.destroy()
set d.patrol=true
loop
exitwhen i>NUM-1
set d.Beetle[i]=CreateUnit(GetOwningPlayer(d.caster),BEETLE,GetUnitX(d.caster),GetUnitY(d.caster),0)
call UnitApplyTimedLife(d.Beetle[i],'BTLF',d.counter)
//--add ability--
if (d.lvl==2) then
call UnitAddAbility(d.Beetle[i],B_ABILITY[1])
elseif (d.lvl==3) then
call UnitAddAbility(d.Beetle[i],B_ABILITY[1])
call UnitAddAbility(d.Beetle[i],B_ABILITY[2])
endif
//--------------
set i=i+1
endloop
call TimerStart(t,XE_ANIMATION_PERIOD,false,function Check)
elseif (d.patrol==true and GetWidgetLife(d.caster)>0.405) then
set r = Rect(GetUnitX(d.caster)-PATROL_RADIUS, GetUnitY(d.caster)-PATROL_RADIUS, GetUnitX(d.caster)+PATROL_RADIUS, GetUnitY(d.caster)+PATROL_RADIUS)
set i=0
loop
exitwhen i>NUM-1
set x=GetRandomReal(GetRectMinX(r), GetRectMaxX(r))
set y=GetRandomReal(GetRectMinY(r), GetRectMaxY(r))
call IssuePointOrder(d.Beetle[i],"patrol",x,y)
set i=i+1
endloop
if (d.counter>0.) then
call TimerStart(t,1.0,false,function Check)
set d.counter=d.counter-1.0
else
call d.destroy()
endif
call RemoveRect(r)
elseif (GetWidgetLife(d.caster)<=0.405) then
call d.destroy()
endif
set r=null
endfunction
private function Actions takes nothing returns nothing
local timer t
local unit u
local data d
if GetSpellAbilityId() == SPELL_ID then
set t=NewTimer()
set u=GetSpellAbilityUnit()
set d=data.create(u,t)
call SetTimerData(t,integer(d))
call TimerStart(t,XE_ANIMATION_PERIOD,false,function Check)
endif
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddAction(t, function Actions )
call InitBSpells()
endfunction
endlibrary