- Joined
- Apr 24, 2012
- Messages
- 5,111
I was creating an Arena and thought of Slashes and Bouncing abilities
Example code of a simple Sleight of Fist:
Changelogs:
- 2.0
Major rework:
- rewrote the whole library
- no longer picks destructables and items
- CONSISTENT type removed
- Chains can now have target priorities
- No longer has Event (and events)
- No longer runs on a timer
- 1.1
- Improved Random selection
- Fixed Random stopping suddenly
- Improved Pick algorithm
- added new method : getNextTarget
- Fixed parts of the code (Thanks Bannar)
PLEASE GO TO THE FILTERING SYSTEM FIRST BEFORE USING!
http://www.hiveworkshop.com/forums/jass-resources-412/filtering-system-240887/
JASS:
library Chain/* 2.0
************************************************************************************
*
* Used for building chain/bouncing spells like Omnislash
* and Custom Chain Lightning
*
* Also allows you to do target priorities.
*
* The system's target prioty requires you to use FilteringSystem
*
************************************************************************************
*
* */ requires /*
*
* */Table /*
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
* */GetClosestWidget /*
* hiveworkshop.com/forums/jass-resources-412/filtering-system-240887/
* */FilteringSystem /*
* hiveworkshop.com/forums/jass-resources-412/snippet-getclosestwidget-204217/
*
************************************************************************************
*
* API
*
* struct ChainType
* [See the comments in the struct
*
* struct ChainPriority
* readonly integer priorities
* - the current priority size
* static method create takes nothing returns thistype
*
* method operator next= takes integer filter returns nothing
* - enqueues the priority filter
* - pushed values must follow the FilteringSystem values
*
* method operator [] takes integer index returns integer
* - get the priority filter
*
* method destroy takes nothing returns nothing
* - mostly not recommended. Let ChainPriority act as a constant and/or
* a basis of some of your chains
*
* struct Chain
* readonly integer cType
* - The chain type
*
* player ownPlayer
* - the owner of the chain
* - used for filtering system; default is Player(14)(see DEFAULT_OWNER)
*
* real x
* real y
* real radius
* - the coordinates and the radius of the chain
*
* boolean locked
* - if true, the chain moves to the owner's position whenever
* it is requested to get the next target (getNext())
* unit owner
* - the unit owner of the chain
*
* boolean limited
* integer count
* - the number of counts/picks the chain can do.
* - if limited is false, the chain can pick infinitely
*
* boolean waitTargets
* - if true, the chain can pick another unit for the next cycle
* if it happens to find none on the previous cycle
* - if false, the chain gets destroyed(regardless if limited is false)
* Take note that you don't need to manually destroy the chain
* if you set this to false
*
*
* static method create takes integer chain, real x, real y, real range,
* integer bounces, boolean limited, boolean waitTargets,
* ChainPriority priority returns thistype
*
* static method createEx takes integer chain, unit o, real range, integer bounces,
* boolean limited, boolean waitTargets, ChainPriority priority
* returns thistype
* - "chain" : the ChainType
* - "o" : the unit owner
*
* method getNext takes nothing returns unit
* - requests for the next target
*
* method destroy takes nothing returns nothing
*
*
************************************************************************************
*
* Credits
*
* Bribe
* TheWrecker
* Bannar
*
************************************************************************************/
globals
/*
* The default owner of all chains.
*/
private constant player DEFAULT_OWNER = Player(14)
/*
* Destroy the chain if it has empty counts? (if it is limited)
*/
private constant boolean DESTROY_EMPTY_CHAIN = true
endglobals
/*
* Chain Types
*/
struct ChainType extends array
/*
* Type 1 : Picks unit randomly in range
*/
readonly static constant integer RANDOM = 1
/*
* Type 2 : The previous picked unit won't be picked on the next cycle
* Picking method is random
*/
readonly static constant integer BOUNCE_RANDOM = 2
/*
* Type 3 : The previous picked unit won't be picked on the next cycle.
* Picking method is the closest unit.
*/
readonly static constant integer BOUNCE_CLOSEST = 3
/*
* Type 4: All units picked before won't be picked on future cycles.
* Picking method is random
*/
readonly static constant integer UNIQUE_RANDOM = 4
/*
* Type 5: All units picked before won't be picked on future cycles.
* Picking method is the closest unit
*/
readonly static constant integer UNIQUE_CLOSEST = 5
endstruct
/*
* Chain Priority
* To make chains prioritize unit types.
* Priorities are in queue, meaning that it checks from the first
* priority to the last.
*
* Values that are pushed in the ChainPriority must follow
* The Filtering System method (See FilteringSystem library)
*/
struct ChainPriority
readonly integer priorities
private Table t
static method create takes nothing returns thistype
local thistype this = allocate()
set t = Table.create()
set priorities = 0
return this
endmethod
/*
* add the priority filter to the unit
*/
method operator next= takes integer filter returns nothing
set priorities = priorities + 1
set t[priorities] = filter
endmethod
/*
* get the priority filter
*/
method operator [] takes integer index returns integer
if priorities >= index and index > 0 then
return t[priorities]
endif
return 0
endmethod
method destroy takes nothing returns nothing
set priorities = 0
call t.destroy()
endmethod
endstruct
struct Chain
readonly integer cType
/*
* The owner of the chain
*/
player ownPlayer
/*
* The coordinates
*/
real x
real y
real radius
/*
* the owner (unit)
* if it is locked, the Chain always pick the
* target from the owning unit's position
*/
boolean locked
unit owner
/*
* pick counter
* limited = if false, Chain can still pick even if it exceeds the
* counter
*/
boolean limited
integer count
/*
* used to check if the chain can wait for targets(when it can't pick any)
* if false, the chain is destroyed once it fails to find a unit
*/
boolean waitTargets
private ChainPriority prio
/*
* Used for BOUNCE_RANDOM and BOUNCE_CLOSEST
*/
private unit prevTarget
/*
* Used for UNIQUE_RANDOM and UNIQUE_CLOSEST
*/
private Table hitUnits
static method create takes integer chain, real x, real y, real range, integer bounces, boolean limited, boolean waitTargets, ChainPriority priority returns thistype
local thistype this = allocate()
set cType = chain
set this.ownPlayer = DEFAULT_OWNER
set this.x = x
set this.y = y
set this.radius = range
set this.count = bounces
set this.limited = limited
set this.prio = priority
set this.waitTargets = waitTargets
if chain == ChainType.UNIQUE_RANDOM or chain == ChainType.UNIQUE_CLOSEST then
set hitUnits = Table.create()
endif
return this
endmethod
static method createEx takes integer chain, unit o, real range, integer bounces, boolean limited, boolean waitTargets, ChainPriority priority returns thistype
local thistype this = create(chain, GetUnitX(o), GetUnitY(o), range, bounces, waitTargets, limited, priority)
set ownPlayer = GetOwningPlayer(o)
set owner = o
return this
endmethod
method destroy takes nothing returns nothing
set x = 0
set y = 0
set radius = 0
set count = 0
set limited = false
set waitTargets = false
if cType == ChainType.UNIQUE_RANDOM or cType == ChainType.UNIQUE_CLOSEST then
call hitUnits.destroy()
endif
set cType = 0
set ownPlayer = null
set owner = null
endmethod
private static group g = CreateGroup()
private static unit array picked
private static integer pickCount = 0
private static thistype temp
private static integer currentPrio
private static unit fUnit
/*
* filter for the bounce types
*/
private static method bFilter takes nothing returns boolean
set fUnit = GetFilterUnit()
return temp.prevTarget != fUnit and UnitFilter(fUnit, temp.ownPlayer, temp.prio[currentPrio])
endmethod
/*
* filter for the unique types
*/
private static method uFilter takes nothing returns boolean
set fUnit = GetFilterUnit()
return UnitFilter(fUnit, temp.ownPlayer, temp.prio[currentPrio]) and not temp.hitUnits.boolean[GetHandleId(fUnit)]
endmethod
/*
* to prevent null handle leaks
*/
private static unit tempU
/*
* the picking method
*/
method getNext takes nothing returns unit
local unit enumU
local integer pr = 0
if this != 0 then
/*
* Check if the count has reached 0
*/
if count == 0 then
/*
* if the chain is limited, return null
*/
if limited then
if DESTROY_EMPTY_CHAIN then
call destroy()
endif
return null
endif
/*
* Otherwise, don't let it drop below 0
*/
set count = 1
endif
/*
* if chain is locked, move the chain to the unit's position
*/
if locked then
set x = GetUnitX(owner)
set y = GetUnitY(owner)
endif
/*
* ITERATE ALL PRIORITY FILTERS
*/
set pickCount = 0
loop
set pr = pr + 1
/*
* Stubborn users :V
*/
if prio[pr] == 0 then
return null
endif
if cType == ChainType.RANDOM then
/*
* Get all the units that match the filter
*/
call GroupEnumUnitsInRange(g, x, y, radius, null)
loop
set enumU = FirstOfGroup(g)
exitwhen enumU == null
if UnitFilter(enumU, ownPlayer, prio[pr]) then
set pickCount = pickCount + 1
set picked[pickCount] = enumU
endif
call GroupRemoveUnit(g, enumU)
endloop
/*
* check if it has picked a unit
*/
if pickCount > 0 then
/*
* if true, decrement counter then return a random unit from the array
*/
set count = count - 1
return picked[GetRandomInt(1, pickCount)]
endif
/*
* Otherwise, continue to the next priority
*/
set pickCount = 0
elseif cType == ChainType.BOUNCE_RANDOM then
/*
* Get all the units
*/
call GroupEnumUnitsInRange(g, x, y, radius, null)
loop
set enumU = FirstOfGroup(g)
exitwhen enumU == null
if UnitFilter(enumU, ownPlayer, prio[pr]) and enumU != prevTarget then
set pickCount = pickCount + 1
set picked[pickCount] = enumU
endif
call GroupRemoveUnit(g, enumU)
endloop
/*
* if it has picked any
*/
if pickCount > 0 then
/*
* if true, protect the picked target for the next cycle
*/
set prevTarget = picked[GetRandomInt(1, pickCount)]
set count = count - 1
return prevTarget
endif
/*
* continue to the next priority
*/
set pickCount = 0
elseif cType == ChainType.BOUNCE_CLOSEST then
/*
* get the closest unit
*/
set temp = this
set currentPrio = pr
set tempU = GetClosestUnitInRange(x, y, radius, Filter(function thistype.bFilter))
/*
* Check if the pickin succeeded
*/
if tempU != null then
/*
* if true, decrement count then protect unit for the next cycle
*/
set count = count - 1
set prevTarget = tempU
return tempU
endif
set tempU = null
elseif cType == ChainType.UNIQUE_RANDOM then
/*
* Get the units that are not yet picked by the previous cycles
*/
call GroupEnumUnitsInRange(g, x, y, radius, null)
loop
set enumU = FirstOfGroup(g)
exitwhen enumU == null
if UnitFilter(enumU, ownPlayer, prio[pr]) and not hitUnits.boolean[GetHandleId(enumU)] then
set pickCount = pickCount + 1
set picked[pickCount] = enumU
endif
call GroupRemoveUnit(g, enumU)
endloop
/*
* Check if it has picked any unit
*/
if pickCount > 0 then
/*
* Get the random unit, then protect the unit for the future cycles
*/
set tempU = picked[GetRandomInt(1, pickCount)]
set hitUnits.boolean[GetHandleId(tempU)] = true
set count = count - 1
return tempU
endif
/*
* continue to next priority
*/
set pickCount = 0
elseif cType == ChainType.UNIQUE_CLOSEST then
/*
* Get the closest unit
*/
set temp = this
set currentPrio = pr
set tempU = GetClosestUnitInRange(x, y, radius, Filter(function thistype.uFilter))
/*
* Check if the picking succeeded
*/
if tempU != null then
/*
* if true, protect the unit then return
*/
set count = count - 1
set hitUnits.boolean[GetHandleId(tempU)] = true
return tempU
endif
/*
* continue to next priority
*/
set tempU = null
endif
exitwhen pr >= prio.priorities
endloop
/*
* The cycle failed, check if it can wait for targets.
* if not, destroy
*/
if not waitTargets then
call destroy()
endif
return null
endif
return null
endmethod
endstruct
endlibrary
Example code of a simple Sleight of Fist:
JASS:
library SleightOfFist requires Chain
globals
private constant integer ABIL_ID = 'fist'
private constant real TIMEOUT = 0.03125
private constant real RADIUS = 600
private constant string SWORD_SFX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
endglobals
struct SleightOfFist
private unit u
private real x
private real y
private Chain c
private static ChainPriority cp
private static thistype array ind
private static thistype count = 0
private effect sword
private static constant timer t = CreateTimer()
private static method P takes nothing returns nothing
local thistype this = count
local unit target
loop
exitwhen this == 0
set target = c.getNext()
if target != null then
call SetUnitX(u, GetUnitX(target))
call SetUnitY(u, GetUnitY(target))
else
set u = count.u
set x = count.x
set y = count.y
set c = count.c
call DestroyEffect(sword)
set sword = count.sword
set count = count - 1
if count == 0 then
call PauseTimer(t)
endif
endif
set target = null
set this = this - 1
endloop
endmethod
private static method onCast takes nothing returns boolean
local thistype this
if GetSpellAbilityId() == ABIL_ID then
set this = count + 1
set count = this
set u = GetTriggerUnit()
set x = GetSpellTargetX()
set y = GetSpellTargetY()
//set c = Chain.create(ChainType.RANDOM, x, y, RADIUS, 10, true, false, cp)
//set c = Chain.create(ChainType.BOUNCE_RANDOM, x, y, RADIUS, 10, true, false, cp)
//set c = Chain.create(ChainType.BOUNCE_CLOSEST, x, y, RADIUS, 10, true, false, cp)
set c = Chain.create(ChainType.UNIQUE_RANDOM, x, y, RADIUS, 10, true, false, cp)
//set c = Chain.create(ChainType.UNIQUE_CLOSEST, x, y, RADIUS, 10, true, false, cp)
set sword = AddSpecialEffectTarget(SWORD_SFX, u, "weapon")
if count == 1 then
call TimerStart(t, TIMEOUT, true, function thistype.P)
endif
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.onCast))
set t = null
set cp = ChainPriority.create()
/*
* Sample Target Priority
* Heroes are picked first, if the chain can't find any hero,
* it tries to pick non-heroes
*/
set cp.next = FS_UNIT_ALIVE + FS_UNIT_ENEMY + FS_UNIT_HERO
set cp.next = FS_UNIT_ALIVE + FS_UNIT_ENEMY + FS_UNIT_NON_HERO
endmethod
endstruct
endlibrary
Changelogs:
- 2.0
Major rework:
- rewrote the whole library
- no longer picks destructables and items
- CONSISTENT type removed
- Chains can now have target priorities
- No longer has Event (and events)
- No longer runs on a timer
- 1.1
- Improved Random selection
- Fixed Random stopping suddenly
- Improved Pick algorithm
- added new method : getNextTarget
- Fixed parts of the code (Thanks Bannar)
Attachments
Last edited: