Name | Type | is_array | initial_value |
AttackSystem__DamageAmount | real | No | |
AttackSystem__Event | real | No | |
AttackSystem__IsAttackMain | boolean | No | |
AttackSystem__IsAttackMelee | boolean | No | |
AttackSystem__TargetDest | destructable | No | |
AttackSystem__TargetItem | item | No | |
AttackSystem__TargetPoint | location | No | |
AttackSystem__TargetUnit | unit | No | |
AttackSystem__TriggerAttack | integer | No | |
AttackSystem__TriggerUnit | unit | No | |
AttackSystem_Acceleration | real | No | |
AttackSystem_AcquireRange | real | No | |
AttackSystem_Arc | real | No | |
AttackSystem_Attack | integer | No | |
AttackSystem_AttackType | attacktype | No | |
AttackSystem_BounceRange | real | No | |
AttackSystem_CombatSound | weapontype | No | |
AttackSystem_Cooldown | real | No | |
AttackSystem_DamagePoint | real | No | |
AttackSystem_DamageType | damagetype | No | |
AttackSystem_DESTROY_ATTACK | integer | No | |
AttackSystem_DISABLE_UNIT | integer | No | |
AttackSystem_ENABLE_UNIT | integer | No | |
AttackSystem_ImpactZ | real | No | |
AttackSystem_LaunchX | real | No | |
AttackSystem_LaunchY | real | No | |
AttackSystem_LaunchZ | real | No | |
AttackSystem_MaxBounces | integer | No | |
AttackSystem_MaxDamage | real | No | |
AttackSystem_MaxSpeed | real | No | |
AttackSystem_MaxTargets | integer | No | |
AttackSystem_Melee | boolean | No | |
AttackSystem_MinDamage | real | No | |
AttackSystem_MinSpeed | real | No | |
AttackSystem_Model | string | No | |
AttackSystem_Operation | integer | No | |
AttackSystem_OperationTrigger | trigger | No | |
AttackSystem_Range | real | No | |
AttackSystem_REGISTER_UNIT | integer | No | |
AttackSystem_Scale | real | No | |
AttackSystem_ShowShadow | boolean | No | |
AttackSystem_Speed | real | No | |
AttackSystem_Unit | unit | No | |
AttackSystem_UNREGISTER_UNIT | integer | No | |
AttackSystem_WantDestroyAttack | boolean | No |
library AttackSystem /* v1.1
*/uses /*
*/OrderIds /* https://www.hiveworkshop.com/threads/290882/
*/Table /* https://www.hiveworkshop.com/threads/188084/
*/UnitDex /* https://www.hiveworkshop.com/threads/248209/
*/GetClosestWidget /* https://www.hiveworkshop.com/threads/204217/
*/ImageStruct /* https://www.hiveworkshop.com/threads/271099/
*/optional RegisterPlayerUnitEvent /* https://www.hiveworkshop.com/threads/250266/
*/optional WorldBounds /* https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j/
*/optional ErrorMessage /* https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage/main.j/
[Resource Thread] : https://www.hiveworkshop.com/threads/307417/
*///! novjass
|-------------|
| Description |
|-------------|
/*
A fully functional alternative attack system made to closely mimic the behavior of the
default Warcraft 3 attack system. Below are some pros and cons of this system compared
to the game's default attack system:
Pros:
- All fields can be changed dynamically (range, acquisition range, projectile speed, etc.)
- Full control over the lauched attacks' stats (damage, attacktype, damagetype, Missile,
etc., which is OP :D)
- Events for the various phases in the attacking process
- Can retrieve the 'raw', unaltered damage of an attack
- Attacks can have negative damage amount, in which case, the target will be healed
- Projectiles of ranged attacks can have > 1 special effects attached
Cons:
- The synchronization of the unit's attack animation and the attack release is not as
flawless as the default attack system
- Retrieving the current order of an actively attacking unit does not return "attack"
nor "channel" (which is the order of the dummy attack ability used)
- You need to manually set the units' attack cooldown
Known Issues:
- Ethereal units, by the time they lose their ethereal status, will only auto-attack/
auto-acquire targets after the next order given to them is finished
Common practical issues that can easily be solved using this system:
- Custom Evasion
- Custom Critical Strike / Bash (without redundantly triggering a damage event)
- Easily configuring a split shot and bounce (like Moon Glaive) attacks
- Combination of split shot and bounce attacks (Ex: Three projectiles, each bouncing twice
per attack)
- Differentiation between melee and ranged attacks
*/
|---------|
| Credits |
|---------|
/*
AGD - Author
Wareditor - Special effects 3D facing algorithm
MyPad - Allocation Algorithm
*/
|-----------|
| Importing |
|-----------|
/*
1. Install all the required libraries listed above
2. Copy all the necessary object data for this system
- Attack (AttackSystem) - Ability
3. Paste this library into your map
4. Read the API documentation and configure the system settings below (Important!)
*/
|-----|
| API |
|-----|
/*
Attack Event Types
*/constant integer EVENT_ATTACK_START/*
- Fires when a unit begins its attack animation
*/constant integer EVENT_ATTACK_FINISH/*
- Fires when a unit lands its attack on the target (for melee) or when a unit releases its main projectile (for ranged)
*/constant integer EVENT_ATTACK_MISSILE_LAUNCH/*
- Fires when a ranged unit releases its projectile (the event EVENT_ATTACK_FINISH fires before this one)
*/constant integer EVENT_ATTACK_MISSILE_IMPACT/*
- Fires when a projectile impacts the target (Including those bounce targets)
*/constant integer EVENT_ATTACK_MISSILE_BOUNCE/*
- Fires when a projectile bounces off a unit (the event EVENT_ATTACK_MISSILE_IMPACT fires before this one)
*/constant integer EVENT_ATTACK_DESTROY/*
- Fires when an attack instance is destroyed, whether explicitly or implicitly
*/struct CustomAttack extends array/*
*/readonly static CustomAttack triggerInstance/* Event response
*/readonly static Attack triggerAttack /* Event response
*/readonly unit unit /* The unit corresponding to this CustomAttack instance
*/readonly widget target /* The current target widget
*/readonly unit targetUnit /* The current target unit
*/readonly item targetItem /* The current target item
*/readonly destructable targetDestructable /* The current target destructable
*/readonly real targetX /* The x-coordinate of the current target point
*/readonly real targetY /* The y-coordinate of the current target point
*/boolean enabled /* Determines if the unit's attack is enabled
*/boolean melee /* Determines if the unit's attacks are melee
*/boolean showShadow /* Determines if the unit's attacks' missiles' shadow is visible
*/integer maxTargets /* The maximum number of targets when attacking (for split-shot)
*/integer maxBounces /* The maximum number of bounces the unit's attacks
*/real acquisitionRange /* The target acquisition range
*/real range /* The attack range
*/real bounceRange /* The projectile bounce range
*/real cooldown /* The attack cooldown in seconds (cooldown countdown starts after EVENT_ATTACK_FINISH fires)
*/real damagePoint /* The attack damage point in seconds
*/real launchX /* The x-offset of the projectile launch location on the unit's rotating coordinate system
*/real launchY /* The y-offset of the projectile launch location on the unit's rotating coordinate system
*/real launchZ /* The z-offset of the projectile launch location from the unit's origin
*/real impactZ /* The z-offset of the projectile impact location from the target's origin
*/real arc /* The launch angle of a projectile from the ground (in radians)
*/real speed /* The speed of the projectile
*/real minSpeed /* The minimum speed of a projectile
*/real maxSpeed /* The maximum speed of a projectile
*/real acceleration /* The acceleration of a projectile
*/real scale /* The scale of the projectile
*/real minDamage /* The low-bound of the attack damage
*/real maxDamage /* The high-bound of the attack damage
*/attacktype attacktype /* The unit's attacktype
*/damagetype damagetype /* The unit's damagetype
*/weapontype weapontype /* The unit's weapontype
*/string model /* The model of the projectile
*/static method operator [] takes unit whichUnit returns CustomAttack/*
- Returns the CustomAttack instance of a unit
*/static method isUnitRegistered takes unit whichUnit returns boolean/*
*/static method register takes unit whichUnit returns nothing/*
*/static method unregister takes unit whichUnit returns nothing/*
- Registers/Unregisters a unit to/from the system
*/static method registerEventHandler takes integer whichEvent, code handlerFunc returns nothing/*
*/static method unregisterEventHandler takes integer whichEvent, code handlerFunc returns nothing/*
*/static method clearEventHandlers takes integer whichEvent returns nothing/*
- Event handler methods
*/method addAttackSfx takes string model returns this/*
*/method removeAttackSfx takes string model returns this/*
*/method clearAttackSfx takes nothing returns this/*
- Allows you to dynamically add as many number of special effects as you like to the projectile of a ranged attacker
*/struct Attack extends array/*
*/readonly boolean flag /* Returns false if this Attack is destroyed/inactive
*/readonly boolean main /* Determines if this is an attack to the main target (Returns false for the Attacks for other split-shot targets)
*/readonly boolean landed /* Determines if the Attack's target successfully passes the filter condition at the time the Attack impacts the target
*/readonly boolean melee /* Determines if this is a melee Attack
*/readonly boolean ranged /* Determines if this is a ranged Attack
*/readonly integer currentBounces /* The current number of bounces of the projectile
*/readonly real x /* The current x-coordinate
*/readonly real y /* The current y-coordinate
*/readonly real z /* The current z-coordinate (absolute)
*/readonly unit source /* The source unit of this Attack
*/widget target /* The target widget of this Attack
*/unit targetUnit /* The target unit of this Attack
*/item targetItem /* The target item of this Attack
*/destructable targetDestructable /* The target destructable of this Attack
*/real targetX /* The target x-coordinate (You can only edit this value if target == null)
*/real targetY /* The target y-coordinate (You can only edit this value if target == null)
*/real targetZ /* The target z-coordinate (absolute) (You can only edit this value if target == null)
*/real damage /* The raw damage amount of this Attack
*/integer maxBounces /* The maximum number of projectile bounces
*/real bounceRange /* The projectile bounce range
*/real speed /* The projectile speed of a ranged Attack
*/real minSpeed /* The minimum possible speed
*/real maxSpeed /* The maximum possible speed
*/real acceleration /* The acceleration of a ranged Attack
*/real scale /* The scale value of a ranged Attack
*/real arc /* The arcing movement of a ranged Attack
*/attacktype attacktype /* The attacktype
*/damagetype damagetype /* The damagetype
*/weapontype weapontype /* The weapontype
*/boolean showShadow /* Determines if an Attack's shadow is visible
*/static method create takes unit source, widget target, boolean isMainAttack, boolean fireEvent returns Attack/*
- Manually allocate an Attack without the need to check for the source unit's attack cooldown and range limitations
- Also works for units' whose custom attacks are disabled
- Does not play the source unit's attack animation nor interrupts the unit's current order
*/method destroy takes nothing returns nothing/*
- Force destroys an Attack
*/method addSfx takes string model returns this/*
*/method removeSfx takes string model returns this/*
*/method clearSfx takes nothing returns this/*
- Adds/Removes a model to/from a ranged Attack
Wrapper Functions
*/function GetEventAttackDamage takes nothing returns real/*
*/function GetEventAttackSource takes nothing returns unit/*
*/function GetEventAttackTarget takes nothing returns widget/*
*/function GetEventAttackMaxBounces takes nothing returns integer/*
*/function GetEventAttackCurrentBounces takes nothing returns integer/*
*/function GetEventAttackTargetUnit takes nothing returns unit/*
*/function GetEventAttackTargetItem takes nothing returns item/*
*/function GetEventAttackTargetDestructable takes nothing returns destructable/*
*/function GetEventAttackTargetX takes nothing returns real/*
*/function GetEventAttackTargetY takes nothing returns real/*
*/function GetEventAttackTargetZ takes nothing returns real/*
*/function GetEventAttackMissileSpeed takes nothing returns real/*
*/function GetEventAttackMissileAcceleration takes nothing returns real/*
*/function GetEventAttackMissileScale takes nothing returns real/*
*/function GetEventAttackMissileArc takes nothing returns real/*
*/function GetEventAttackType takes nothing returns attacktype/*
*/function GetEventDamageType takes nothing returns damagetype/*
*/function GetEventWeaponType takes nothing returns weapontype/*
*/function IsEventAttackMelee takes nothing returns boolean/*
*/function IsEventAttackRanged takes nothing returns boolean/*
*/function IsEventAttackMain takes nothing returns boolean/*
*/function DestroyEventAttack takes nothing returns nothing/*
*/function SetEventAttackDamage takes real damage returns nothing/*
*/function SetEventAttackTarget takes widget target returns nothing/*
*/function SetEventAttackTargetUnit takes unit target returns nothing/*
*/function SetEventAttackTargetItem takes item target returns nothing/*
*/function SetEventAttackTargetDestructable takes destructable target returns nothing/*
*/function SetEventAttackTargetX takes real targetX returns nothing/*
*/function SetEventAttackTargetY takes real targetY returns nothing/*
*/function SetEventAttackTargetZ takes real targetZ returns nothing/*
*/function SetEventAttackMissileSpeed takes real speed returns nothing/*
*/function SetEventAttackMissileMinSpeed takes real speed returns nothing/*
*/function SetEventAttackMissileMaxSpeed takes real speed returns nothing/*
*/function SetEventAttackMissileAcceleration takes real acceleration returns nothing/*
*/function SetEventAttackMissileScale takes real scale returns nothing/*
*/function SetEventAttackMissileArc takes real arc returns nothing/*
*/function SetEventAttackType takes attacktype whichAttackType returns nothing/*
*/function SetEventDamageType takes damagetype whichDamageType returns nothing/*
*/function SetEventWeaponType takes weapontype whichWeaponType returns nothing/*
*/function GetUnitAttackDamageMin takes unit whichUnit returns real/*
*/function GetUnitAttackDamageMax takes unit whichUnit returns real/*
*/function GetUnitAttackType takes unit whichUnit returns attacktype/*
*/function GetUnitDamageType takes unit whichUnit returns damagetype/*
*/function GetUnitWeaponType takes unit whichUnit returns weapontype/*
*/function GetUnitAttackModel takes unit whichUnit returns string/*
*/function GetUnitAttackDamagePoint takes unit whichUnit returns real/*
*/function GetUnitAttackCooldown takes unit whichUnit returns real/*
*/function GetUnitAttackRange takes unit whichUnit returns real/*
*/function GetUnitAttackAcquireRange takes unit whichUnit returns real/*
*/function GetUnitAttackMissileSpeed takes unit whichUnit returns real/*
*/function GetUnitAttackMissileMinSpeed takes unit whichUnit returns real/*
*/function GetUnitAttackMissileMaxSpeed takes unit whichUnit returns real/*
*/function GetUnitAttackLaunchX takes unit whichUnit returns real/*
*/function GetUnitAttackLaunchY takes unit whichUnit returns real/*
*/function GetUnitAttackLaunchZ takes unit whichUnit returns real/*
*/function GetUnitAttackImpactZ takes unit whichUnit returns real/*
*/function GetUnitAttackMaxTargets takes unit whichUnit returns integer/*
*/function GetUnitAttackMaxBounces takes unit whichUnit returns integer/*
*/function GetUnitAttackTarget takes unit whichUnit returns widget/*
*/function GetUnitAttackTargetUnit takes unit whichUnit returns unit/*
*/function GetUnitAttackTargetItem takes unit whichUnit returns item/*
*/function GetUnitAttackTargetDestructable takes unit whichUnit returns destructable/*
*/function IsUnitAttackShadowVisible takes unit whichUnit returns boolean/*
*/function IsUnitAttackMelee takes unit whichUnit returns boolean/*
*/function IsUnitAttackRanged takes unit whichUnit returns boolean/*
*/function SetUnitAttackDamageMin takes unit whichUnit, real amount returns nothing/*
*/function SetUnitAttackDamageMax takes unit whichUnit, real amount returns nothing/*
*/function SetUnitAttackType takes unit whichUnit, attacktype whichAttackType returns nothing/*
*/function SetUnitDamageType takes unit whichUnit, damagetype whichDamageType returns nothing/*
*/function SetUnitWeaponType takes unit whichUnit, weapontype whichWeaponType returns nothing/*
*/function SetUnitAttackModel takes unit whichUnit, string modelPath returns nothing/*
*/function SetUnitAttackDamagePoint takes unit whichUnit, real damagePoint returns nothing/*
*/function SetUnitAttackCooldown takes unit whichUnit, real cooldown returns nothing/*
*/function SetUnitAttackRange takes unit whichUnit, real range returns nothing/*
*/function SetUnitAttackAcquireRange takes unit whichUnit, real acquireRange returns nothing/*
*/function SetUnitAttackMissileSpeed takes unit whichUnit, real missileSpeed returns nothing/*
*/function SetUnitAttackMissileMinSpeed takes unit whichUnit, real missileSpeed returns nothing/*
*/function SetUnitAttackMissileMaxSpeed takes unit whichUnit, real missileSpeed returns nothing/*
*/function SetUnitAttackLaunchX takes unit whichUnit, real launchX returns nothing/*
*/function SetUnitAttackLaunchY takes unit whichUnit, real launchY returns nothing/*
*/function SetUnitAttackLaunchZ takes unit whichUnit, real launchZ returns nothing/*
*/function SetUnitAttackImpactZ takes unit whichUnit, real impactZ returns nothing/*
*/function SetUnitAttackMaxTargets takes unit whichUnit, integer maxTargets returns nothing/*
*/function SetUnitAttackMaxBounces takes unit whichUnit, integer maxBounces returns nothing/*
*/function SetUnitAttackShadowVisible takes unit whichUnit, boolean visible returns nothing/*
*/function SetUnitAttackMelee takes unit whichUnit returns nothing/*
*/function SetUnitAttackRanged takes unit whichUnit returns nothing/*
*/function UnitAddAttackEffect takes unit whichUnit, string model returns nothing/*
*/function UnitRemoveAttackEffect takes unit whichUnit, string model returns nothing/*
*/function UnitClearAttackEffect takes unit whichUnit returns nothing/*
*/function UnitCustomAttackEnable takes unit whichUnit returns nothing/*
*/function UnitCustomAttackDisable takes unit whichUnit returns nothing/*
*/function IsUnitCustomAttackEnabled takes unit whichUnit returns boolean/*
*/function RegisterAttackEventHandler takes integer whichEvent, code handler returns nothing/*
*/function UnregisterAttackEventHandler takes integer whichEvent, code handler returns nothing/*
*/function UnitCustomAttackRegister takes unit whichUnit returns nothing/*
*/function UnitCustomAttackUnregister takes unit whichUnit returns nothing/*
*/function IsUnitCustomAttackRegistered takes unit whichUnit returns boolean/*
*///! endnovjass
/*==============================================================================*/
/* SYSTEM CONFIGURATION */
/*==============================================================================*/
private module SystemConstants
/*
* Rawcode of the attack ability
*/
static constant integer ATTACK_ABIL_ID = 'Atk1'
/*
* Order id of the attack ability
*/
static constant integer ATTACK_ORDER_ID = ORDER_channel
/*
* The maximum deviation of the attacker's facing where it is still able to
* attack the target
*/
static constant real ATTACK_ANGLE_TOLERANCE = 10.00
/*
* The periodic timer timeout
*/
static constant real PERIOD = 1.00/32.00
/*
* The image path for the attack missiles' shadow
*/
static constant string MISSILE_SHADOW = "ReplaceableTextures\\Shadows\\Shadow.blp"
/*
* The diameter of the attack missiles' shadow
*/
static constant real MISSILE_SHADOW_SIZE = 70.00
/*
* The attack missiles' alpha value
*/
static constant integer MISSILE_SHADOW_ALPHA = 0x60
/*
* The attack missiles' red value
*/
static constant integer MISSILE_SHADOW_RED = 0xFF
/*
* The attack missiles' green value
*/
static constant integer MISSILE_SHADOW_GREEN = 0xFF
/*
* The attack missiles' blue value
*/
static constant integer MISSILE_SHADOW_BLUE = 0xFF
endmodule
/*
* This set of values are automatically applied when a unit is registered to the system
* This module is optional and you can delete it if you don't need it
*/
private module SystemDefaults
set this.melee = true
set this.showShadow = false
set this.maxTargets = 1
set this.maxBounces = 0
set this.acquisitionRange = 1000.00
set this.range = 600.00
set this.bounceRange = 500.00
set this.cooldown = 1.00
set this.damagePoint = 0.35
set this.launchX = 0.00
set this.launchY = 20.00
set this.launchZ = 60.00
set this.impactZ = 60.00
set this.arc = 0.00
set this.speed = 1000000.00
set this.minSpeed = 0.00
set this.maxSpeed = 1000000.00
set this.acceleration = 0.00
set this.scale = 1.00
set this.model = ""
set this.attacktype = ATTACK_TYPE_HERO
set this.damagetype = DAMAGE_TYPE_NORMAL
set this.weapontype = WEAPON_TYPE_WHOKNOWS
set this.minDamage = 0.00
set this.maxDamage = 0.00
endmodule
/*
* This value is used to set the animation scale of an attacking unit.
* Ideally, this value should vary for different unit types so you might
* want to store those values into a hashtable/Table and make this function
* work this those values.
*
* Example: return animationTable[GetUnitTypeId(this.unit)]/Pow(this.damagePoint, 1.00/1.18)
*/
private function GetAttackAnimationScale takes CustomAttack this returns real
return 0.35/Pow(this.damagePoint, 1.00/1.18) // Be careful when changing this
endfunction
/*==============================================================================*/
/* END OF SYSTEM CONFIGURATION */
/*==============================================================================*/
/*================================ System Code =================================*/
native UnitAlive takes unit u returns boolean
globals
constant integer EVENT_ATTACK_START = 0xABC + 0xAB*0
constant integer EVENT_ATTACK_FINISH = 0xABC + 0xAB*1
constant integer EVENT_ATTACK_MISSILE_LAUNCH = 0xABC + 0xAB*2
constant integer EVENT_ATTACK_MISSILE_IMPACT = 0xABC + 0xAB*3
constant integer EVENT_ATTACK_MISSILE_BOUNCE = 0xABC + 0xAB*4
constant integer EVENT_ATTACK_DESTROY = 0xABC + 0xAB*5
private constant real LOOKAT_OFFSET = 100.00
private constant location loc = Location(0.00, 0.00)
private timer missileTimer = CreateTimer()
private CustomAttack eventInstance = 0
private Attack eventAttack = 0
private TableArray sfxTable
private key KEY
private timer tempTimer
private code onPeriodCode
private unit tempUnit
private item tempItem
private destructable tempDest
private integer array sfxCount
private boolean array doFireEvent
private unit dummyUnit
endglobals
private keyword Initializer
private keyword Node
static if DEBUG_MODE then
private function DebugError takes boolean condition, string libraryName, string methodName, string objectName, integer object, string message returns nothing
static if LIBRARY_ErrorMessage then
call ThrowError(condition, libraryName, methodName, objectName, object, message)
else
if condition then
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[" + libraryName + "] ERROR: " + message)
if 1/0 == 0 then
endif
endif
endif
endfunction
private function DebugWarning takes boolean condition, string libraryName, string methodName, string objectName, integer object, string message returns nothing
static if LIBRARY_ErrorMessage then
call ThrowWarning(condition, libraryName, methodName, objectName, object, message)
else
if condition then
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[" + libraryName + "] WARNING: " + message)
endif
endif
endfunction
endif
private function GetAngle takes real dA, real dB, real dz returns real
local real iB
if dA == 0.00 then
return 0.00
endif
set iB = RAbsBJ(dB)
return Atan(dz/dA)*(1-(iB/(RAbsBJ(dA)+iB)))
endfunction
private function SetEffectFacingXYZ takes effect e, real targetX, real targetY, real targetZ returns nothing
local real dx = targetX - BlzGetLocalSpecialEffectX(e)
local real dy = targetY - BlzGetLocalSpecialEffectY(e)
local real dz = targetZ - BlzGetLocalSpecialEffectZ(e)
call BlzSetSpecialEffectOrientation(e, GetAngle(dy, dx, dz), -GetAngle(dx, dy, dz), Atan2(dy,dx))
endfunction
private function GetSurfaceZ takes real x, real y returns real
call MoveLocation(loc, x, y)
return GetLocationZ(loc)
endfunction
private function GetWidgetCollision takes widget w returns real
local integer index
set Table(KEY).widget[0] = w
set index = GetUnitId(Table(KEY).unit[0])
call Table(KEY).handle.remove(0)
return BlzGetUnitCollisionSize(GetUnitById(index))
endfunction
private function IsWidgetRemoved takes widget w returns boolean
set Table(KEY).widget[0] = w
set tempUnit = Table(KEY).unit[0]
set tempItem = Table(KEY).item[0]
set tempDest = Table(KEY).destructable[0]
call Table(KEY).handle.remove(0)
if tempUnit != null then
return GetUnitTypeId(tempUnit) == 0
elseif tempItem != null then
return GetItemTypeId(tempItem) == 0
elseif tempDest != null then
return GetDestructableTypeId(tempDest) == 0
endif
return true
endfunction
private function TargetUnitFilter takes unit attacker, unit target returns boolean
return attacker != target and/*
*/ UnitAlive(target) and/*
*/ GetUnitAbilityLevel(target, 'Aloc') == 0 and/*
*/ (IsUnitVisible(target, GetOwningPlayer(attacker)) or GetUnitTypeId(attacker) == 0) and/*
*/ not IsUnitType(target, UNIT_TYPE_ETHEREAL) and/*
*/ not BlzIsUnitInvulnerable(target)
endfunction
private function TargetItemFilter takes unit attacker, item target returns boolean
return IsItemVisible(target) and not IsItemOwned(target) and not IsItemInvulnerable(target) and GetWidgetLife(target) >= 0.405
endfunction
private function TargetDestructableFilter takes unit attacker, destructable target returns boolean
return not IsDestructableInvulnerable(target) and GetDestructableLife(target) >= 0.405
endfunction
private function DealDamage takes Attack attack, widget target, boolean ranged returns nothing
local unit u = attack.source
local boolean sourceRemoved = GetUnitTypeId(u) == 0
local player prevOwner
/*
* If the original source unit was removed, set the source to another valid unit. Otherwise, no damage will be dealt.
*/
if sourceRemoved then
set u = dummyUnit
set prevOwner = GetOwningPlayer(u)
call SetUnitOwner(u, Player(Node(attack).ownerId), false)
endif
if attack.damage < 0.00 then
call SetWidgetLife(target, GetWidgetLife(target) - attack.damage + 0.001)
/*
* If the damage is negative, it's still needed to deal damage to the target to maintain the desired
* behavior of an attacked target (ex: units with 'build' ability should retreat from the attacker)
*/
call UnitDamageTarget(u, target, 0.001, true, ranged, attack.attacktype, attack.damagetype, attack.weapontype)
else
call UnitDamageTarget(u, target, attack.damage, true, ranged, attack.attacktype, attack.damagetype, attack.weapontype)
endif
if sourceRemoved then
call SetUnitOwner(u, prevOwner, false)
set prevOwner = null
endif
set u = null
endfunction
private function GetTimer takes integer data returns timer
set tempTimer = CreateTimer()
set Table(KEY)[GetHandleId(tempTimer)] = data
return tempTimer
endfunction
private function DeleteTimer takes timer t returns integer
local integer id = GetHandleId(t)
local integer data = Table(KEY)[id]
call PauseTimer(t)
call DestroyTimer(t)
call Table(KEY).remove(id)
return data
endfunction
private function GetBoundedValue takes real value, real min, real max returns real
if value < min then
return min
elseif value > max then
return max
endif
return value
endfunction
static if not LIBRARY_WorldBounds then
private struct MapBounds extends array
static real minX = 0.00
static real minY = 0.00
static real maxX = 0.00
static real maxY = 0.00
static method init takes nothing returns nothing
local rect bounds = GetWorldBounds()
set minX = GetRectMinX(bounds)
set minY = GetRectMinY(bounds)
set maxX = GetRectMaxX(bounds)
set maxY = GetRectMaxY(bounds)
call RemoveRect(bounds)
set bounds = null
endmethod
endstruct
endif
/*
* Event triggers are only created when there are handlers registered to the event.
*/
private struct EventHandler extends array
private static TableArray table
private integer handlerCount
private trigger handlerTrigger
debug private string eventName
static method operator [] takes thistype this returns trigger
return thistype((this - 0xABC)/0xAB).handlerTrigger
endmethod
method register takes code c returns nothing
local boolexpr expr = Filter(c)
set this = (this - 0xABC)/0xAB
debug call DebugError((this) < 0 or (this) > 5, "AttackSystem", "registerEventHandler()", "null", this, "Invalid input event type")
debug call DebugError(c == null, "AttackSystem", "registerEventHandler()", this.eventName, 0, "Attempted to register a null code")
debug call DebugError(table[this].handle.has(GetHandleId(expr)), "AttackSystem", "registerEventHandler()", this.eventName, 0, "Attemped to register a code twice")
if this.handlerCount == 0 then
set this.handlerTrigger = CreateTrigger()
endif
set this.handlerCount = this.handlerCount + 1
set table[this].triggercondition[GetHandleId(expr)] = TriggerAddCondition(this.handlerTrigger, expr)
set expr = null
endmethod
method unregister takes code c returns nothing
local boolexpr expr = Filter(c)
local integer exprId
set this = (this - 0xABC)/0xAB
debug call DebugError((this) < 0 or (this) > 5, "AttackSystem", "unregisterEventHandler()", "null", this, "Invalid input event type")
debug call DebugError(c == null, "AttackSystem", "unregisterEventHandler()", this.eventName, 0, "Attempted to unregister a null code")
debug call DebugError(not table[this].handle.has(GetHandleId(expr)), "AttackSystem", "unregisterEventHandler()", this.eventName, 0, "Attemped to unregister a code twice")
set this.handlerCount = this.handlerCount - 1
if this.handlerCount == 0 then
call table[this].handle.remove(GetHandleId(expr))
call DestroyTrigger(this.handlerTrigger)
set this.handlerTrigger = null
else
set exprId = GetHandleId(expr)
call TriggerRemoveCondition(this.handlerTrigger, table[this].triggercondition[exprId])
call table[this].handle.remove(exprId)
endif
set expr = null
endmethod
method clear takes nothing returns nothing
set this = (this - 0xABC)/0xAB
debug call DebugError((this) < 0 or (this) > 5, "AttackSystem", "clearEventHandlers()", "null", this, "Invalid input event type")
call DestroyTrigger(this.handlerTrigger)
call table[this].flush()
set this.handlerTrigger = null
set this.handlerCount = 0
endmethod
static method init takes nothing returns nothing
set table = TableArray[6]
debug set thistype(0).eventName = "EVENT_ATTACK_START"
debug set thistype(1).eventName = "EVENT_ATTACK_FINISH"
debug set thistype(2).eventName = "EVENT_ATTACK_MISSILE_LAUNCH"
debug set thistype(3).eventName = "EVENT_ATTACK_MISSILE_IMPACT"
debug set thistype(4).eventName = "EVENT_ATTACK_MISSILE_BOUNCE"
debug set thistype(5).eventName = "EVENT_ATTACK_DESTROY"
endmethod
endstruct
private function FireHandler takes integer eventType, CustomAttack instance, Attack attack returns nothing
local Attack prevAttack = eventAttack
local CustomAttack prevInstance = eventInstance
set eventInstance = instance
set eventAttack = attack
call TriggerEvaluate(EventHandler[eventType])
set eventAttack = prevAttack
set eventInstance = prevInstance
endfunction
private struct Node extends array
private static integer array recycler
static TableArray table
thistype prev
thistype next
boolean flag
boolean main
boolean landed
boolean melee
unit source
unit targetUnit
item targetItem
destructable targetDestructable
integer currentBounces
real arc
real x
real y
real z
real oX
real oY
real oZ
real targetX
real targetY
real targetZ
real lookAtX
real lookAtY
real lookAtZ
real travelled
real scale
real speed
real minSpeed
real maxSpeed
real acceleration
real timeScale
integer alpha
integer red
integer green
integer blue
integer ownerId
Image shadow
boolean shadowFlag
method operator target takes nothing returns widget
if this.targetUnit != null then
return this.targetUnit
elseif this.targetItem != null then
return this.targetItem
elseif this.targetDestructable != null then
return this.targetDestructable
endif
return null
endmethod
method operator target= takes widget whichWidget returns nothing
if whichWidget == null then
set this.targetUnit = null
set this.targetItem = null
set this.targetDestructable = null
else
set Table(KEY).widget[0] = whichWidget
set this.targetUnit = Table(KEY).unit[0]
set this.targetItem = Table(KEY).item[0]
set this.targetDestructable = Table(KEY).destructable[0]
call Table(KEY).handle.remove(0)
endif
endmethod
method getParabolaZ takes nothing returns real
local real dx = this.targetX - this.x
local real dy = this.targetY - this.y
local real dz = this.targetZ - this.z
local real d = SquareRoot(dx*dx + dy*dy) + this.travelled
return (d*Tan(this.arc))*this.travelled*(d - this.travelled)/(d*d) + this.travelled*(this.targetZ - this.oZ)/d + this.oZ
endmethod
method move takes real x, real y, real z returns nothing
local integer index = table[this][0]
local effect sfx
static if LIBRARY_WorldBounds then
set x = GetBoundedValue(x, WorldBounds.minX, WorldBounds.maxX)
set y = GetBoundedValue(y, WorldBounds.minY, WorldBounds.maxY)
else
set x = GetBoundedValue(x, MapBounds.minX, MapBounds.maxX)
set y = GetBoundedValue(y, MapBounds.minY, MapBounds.maxY)
endif
loop
exitwhen index == 0
set sfx = table[this].effect[index]
call BlzSetSpecialEffectPosition(sfx, x, y, z)
call SetEffectFacingXYZ(sfx, this.lookAtX, this.lookAtY, this.lookAtZ)
set sfx = null
set index = index - 1
endloop
if this.shadow != 0 then
call this.shadow.setPositionEx(x, y, 0.00)
endif
set this.x = x
set this.y = y
set this.z = z
endmethod
method addSfx takes string model returns nothing
local integer key = StringHash(model)
local integer index
local effect sfx
if not table[this].has(key) then
set index = table[this][0] + 1
set table[this][0] = index
set sfx = AddSpecialEffect(model, this.x, this.y)
call BlzSetSpecialEffectZ(sfx, this.z)
call SetEffectFacingXYZ(sfx, this.lookAtX, this.lookAtY, this.lookAtZ)
call BlzSetSpecialEffectScale(sfx, this.scale)
call BlzSetSpecialEffectTimeScale(sfx, this.timeScale)
call BlzSetSpecialEffectAlpha(sfx, this.alpha)
call BlzSetSpecialEffectColor(sfx, this.red, this.green, this.blue)
if this.shadow == 0 and this.shadowFlag then
set this.shadow = Image.create(CustomAttack.MISSILE_SHADOW, CustomAttack.MISSILE_SHADOW_SIZE, CustomAttack.MISSILE_SHADOW_SIZE, this.x - CustomAttack.MISSILE_SHADOW_SIZE*0.50, this.y - CustomAttack.MISSILE_SHADOW_SIZE*0.50, 0.00, IMAGE_TYPE_INDICATOR, true)
call this.shadow.wrap(true)
call SetImageColor(this.shadow.img, CustomAttack.MISSILE_SHADOW_RED, CustomAttack.MISSILE_SHADOW_GREEN, CustomAttack.MISSILE_SHADOW_BLUE, CustomAttack.MISSILE_SHADOW_ALPHA)
endif
set table[this].effect[index] = sfx
set table[this][key] = index
set sfx = null
endif
endmethod
method removeSfx takes string model returns nothing
local integer key = StringHash(model)
local integer index = table[this][key]
local integer count
if index != 0 then
set count = table[this][0]
set table[this][0] = count - 1
call DestroyEffect(table[this].effect[index])
set table[this].effect[index] = table[this].effect[count]
call table[this].handle.remove(count)
call table[this].remove(key)
if count == 1 then
if this.shadow != 0 then
call this.shadow.destroy()
set this.shadow = 0
endif
endif
endif
endmethod
method clearSfx takes nothing returns nothing
local integer index = table[this][0]
loop
exitwhen index == 0
call DestroyEffect(table[this].effect[index])
set index = index - 1
endloop
call table[this].flush()
endmethod
static method create takes boolean ranged, real x, real y, real z, real scale, real arc, widget target returns thistype
local thistype this = recycler[0]
local real angle
local real xy
debug call DebugError(this == JASS_MAX_ARRAY_SIZE - 2, "AttackSystem", "create()", "Attack", 0, "Overflow")
if recycler[this] == 0 then
set this = this + 1
set recycler[0] = this
else
set recycler[0] = recycler[this]
set recycler[this] = 0
endif
set this.target = target
set this.flag = true
set this.melee = not ranged
if ranged then
if thistype(0).next == 0 then
call TimerStart(missileTimer, CustomAttack.PERIOD, true, onPeriodCode)
endif
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
set this.alpha = 0xFF
set this.red = 0xFF
set this.green = 0xFF
set this.blue = 0xFF
set this.timeScale = 1.00
set this.oX = x
set this.oY = y
set this.oZ = z
set this.x = x
set this.y = y
set this.z = z
set this.scale = scale
set this.arc = arc
set angle = Atan2(GetWidgetY(target) - y, GetWidgetX(target) - x)
set xy = LOOKAT_OFFSET*Cos(arc)
set this.lookAtX = x + xy*Cos(angle)
set this.lookAtY = y + xy*Sin(angle)
set this.lookAtZ = z + LOOKAT_OFFSET*Sin(arc)
endif
return this
endmethod
method destroy takes nothing returns nothing
call FireHandler(EVENT_ATTACK_DESTROY, GetUnitId(this.source), this)
if not this.melee then
call this.clearSfx()
if this.shadow != 0 then
call this.shadow.destroy()
set this.shadow = 0
endif
set this.shadowFlag = false
set this.travelled = 0.00
set this.next.prev = this.prev
set this.prev.next = this.next
if thistype(0).next == 0 then
call PauseTimer(missileTimer)
endif
endif
set this.source = null
set this.targetUnit = null
set this.targetItem = null
set this.targetDestructable = null
set this.landed = false
set this.flag = false
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
static method init takes nothing returns nothing
set table = TableArray[JASS_MAX_ARRAY_SIZE - 1]
endmethod
endstruct
/*
* An Attack's data is set by the time it is launched. By that time, it becomes
* independent from the stats of its source unit.
*/
struct Attack extends array
real damage
real bounceRange
integer maxBounces
attacktype attacktype
damagetype damagetype
weapontype weapontype
method operator flag takes nothing returns boolean
return Node(this).flag
endmethod
method operator main takes nothing returns boolean
return Node(this).main
endmethod
method operator landed takes nothing returns boolean
return Node(this).landed
endmethod
method operator melee takes nothing returns boolean
return Node(this).melee
endmethod
method operator ranged takes nothing returns boolean
return not Node(this).melee
endmethod
method operator x takes nothing returns real
return Node(this).x
endmethod
method operator y takes nothing returns real
return Node(this).y
endmethod
method operator z takes nothing returns real
return Node(this).z
endmethod
method operator targetX= takes real x returns nothing
if Node(this).target == null then
set Node(this).targetX = x
endif
endmethod
method operator targetX takes nothing returns real
return Node(this).targetX
endmethod
method operator targetY= takes real y returns nothing
if Node(this).target == null then
set Node(this).targetY = y
endif
endmethod
method operator targetY takes nothing returns real
return Node(this).targetY
endmethod
method operator targetZ= takes real z returns nothing
if Node(this).target == null then
set Node(this).targetZ = z
endif
endmethod
method operator targetZ takes nothing returns real
return Node(this).targetZ
endmethod
method operator source takes nothing returns unit
return Node(this).source
endmethod
method operator targetUnit= takes unit whichUnit returns nothing
set Node(this).targetUnit = whichUnit
endmethod
method operator targetUnit takes nothing returns unit
return Node(this).targetUnit
endmethod
method operator targetItem= takes item whichItem returns nothing
set Node(this).targetItem = whichItem
endmethod
method operator targetItem takes nothing returns item
return Node(this).targetItem
endmethod
method operator targetDestructable= takes destructable whichDestructable returns nothing
set Node(this).targetDestructable = whichDestructable
endmethod
method operator targetDestructable takes nothing returns destructable
return Node(this).targetDestructable
endmethod
method operator target= takes widget whichWidget returns nothing
set Node(this).target = whichWidget
endmethod
method operator target takes nothing returns widget
return Node(this).target
endmethod
method operator speed= takes real value returns nothing
set Node(this).speed = value
endmethod
method operator speed takes nothing returns real
return Node(this).speed
endmethod
method operator minSpeed= takes real value returns nothing
set Node(this).minSpeed = value
endmethod
method operator minSpeed takes nothing returns real
return Node(this).minSpeed
endmethod
method operator maxSpeed= takes real value returns nothing
set Node(this).maxSpeed = value
endmethod
method operator maxSpeed takes nothing returns real
return Node(this).maxSpeed
endmethod
method operator acceleration= takes real value returns nothing
set Node(this).acceleration = value
endmethod
method operator acceleration takes nothing returns real
return Node(this).acceleration
endmethod
method operator scale= takes real scale returns nothing
local integer index = Node.table[this][0]
loop
exitwhen index == 0
call BlzSetSpecialEffectScale(Node.table[this].effect[index], scale)
set index = index - 1
endloop
set Node(this).scale = scale
endmethod
method operator scale takes nothing returns real
return Node(this).scale
endmethod
method operator arc= takes real value returns nothing
set Node(this).arc = value
endmethod
method operator arc takes nothing returns real
return Node(this).arc
endmethod
method operator currentBounces takes nothing returns integer
return Node(this).currentBounces
endmethod
method operator showShadow= takes boolean show returns nothing
if show then
if not Node(this).shadowFlag and Node.table[this][0] > 0 then
set Node(this).shadow = Image.create(CustomAttack.MISSILE_SHADOW, CustomAttack.MISSILE_SHADOW_SIZE, CustomAttack.MISSILE_SHADOW_SIZE, this.x - CustomAttack.MISSILE_SHADOW_SIZE*0.50, this.y - CustomAttack.MISSILE_SHADOW_SIZE*0.50, 0.00, IMAGE_TYPE_INDICATOR, true)
call Node(this).shadow.wrap(true)
call SetImageColor(Node(this).shadow.img, CustomAttack.MISSILE_SHADOW_RED, CustomAttack.MISSILE_SHADOW_GREEN, CustomAttack.MISSILE_SHADOW_BLUE, CustomAttack.MISSILE_SHADOW_ALPHA)
endif
elseif Node(this).shadowFlag and Node(this).shadow != 0 then
call Node(this).shadow.destroy()
set Node(this).shadow = 0
endif
set Node(this).shadowFlag = show
endmethod
method operator showShadow takes nothing returns boolean
return Node(this).shadowFlag
endmethod
method setColor takes integer alpha, integer red, integer green, integer blue returns thistype
local integer index = Node.table[this][0]
local effect sfx
loop
exitwhen index == 0
set sfx = Node.table[this].effect[index]
call BlzSetSpecialEffectAlpha(sfx, alpha)
call BlzSetSpecialEffectColor(sfx, red, green, blue)
set sfx = null
set index = index - 1
endloop
set Node(this).alpha = alpha
set Node(this).red = red
set Node(this).green = green
set Node(this).blue = blue
return this
endmethod
method setTimeScale takes real value returns thistype
local integer index = Node.table[this][0]
loop
exitwhen index == 0
call BlzSetSpecialEffectTimeScale(Node.table[this].effect[index], value)
set index = index - 1
endloop
set Node(this).timeScale = value
return this
endmethod
method addSfx takes string model returns thistype
call Node(this).addSfx(model)
return this
endmethod
method removeSfx takes string model returns thistype
call Node(this).removeSfx(model)
return this
endmethod
method clearSfx takes nothing returns thistype
call Node(this).clearSfx()
return this
endmethod
private static method onMeleeAttackLand takes nothing returns nothing
local Node attack = DeleteTimer(GetExpiredTimer())
local Attack prevAttack
local CustomAttack prevInstance
if attack.targetUnit != null then
set attack.landed = TargetUnitFilter(attack.source, attack.targetUnit)
elseif attack.targetItem != null then
set attack.landed = TargetItemFilter(attack.source, attack.targetItem)
elseif attack.targetDestructable != null then
set attack.landed = TargetDestructableFilter(attack.source, attack.targetDestructable)
endif
set prevInstance = eventInstance
set prevAttack = eventAttack
set eventInstance = GetUnitId(attack.source)
set eventAttack = attack
if doFireEvent[attack] then
call TriggerEvaluate(EventHandler[EVENT_ATTACK_FINISH])
endif
if attack.flag then
if attack.landed and attack.target != null then
call DealDamage(attack, attack.target, false)
endif
call attack.destroy()
endif
set eventAttack = prevAttack
set eventInstance = prevInstance
set doFireEvent[attack] = false
endmethod
static method create takes unit source, widget target, boolean mainAttack, boolean fireEvent returns thistype
local CustomAttack instance = GetUnitId(source)
local real deltaAngle = bj_PI/2.00 - GetUnitFacing(source)*bj_DEGTORAD
local integer count = 0
local boolean success = true
local Node attack
set Table(KEY).widget[0] = target
if instance.melee then
set attack = Node.create(false, 0.00, 0.00, 0.00, 0.00, 0.00, null)
set attack.main = true
set attack.ownerId = GetPlayerId(GetOwningPlayer(source))
set attack.source = source
set attack.targetUnit = Table(KEY).unit[0]
set attack.targetItem = Table(KEY).item[0]
set attack.targetDestructable = Table(KEY).destructable[0]
set Attack(attack).damage = GetRandomReal(instance.minDamage, instance.maxDamage)
set Attack(attack).attacktype = instance.attacktype
set Attack(attack).damagetype = instance.damagetype
set Attack(attack).weapontype = instance.weapontype
set doFireEvent[attack] = fireEvent
call TimerStart(GetTimer(attack), 0.00, false, function thistype.onMeleeAttackLand)
else
set tempUnit = Table(KEY).unit[0]
set tempItem = Table(KEY).item[0]
set tempDest = Table(KEY).destructable[0]
if not mainAttack or TargetUnitFilter(source, tempUnit) then
set attack = Node.create(true, GetUnitX(source) + instance.launchX*Cos(deltaAngle) + instance.launchY*Sin(deltaAngle), GetUnitY(source) + instance.launchY*Cos(deltaAngle) - instance.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(source) + instance.launchZ, instance.scale, instance.arc, tempUnit)
set attack.targetUnit = tempUnit
elseif TargetItemFilter(source, tempItem) then
set attack = Node.create(true, GetUnitX(source) + instance.launchX*Cos(deltaAngle) + instance.launchY*Sin(deltaAngle), GetUnitY(source) + instance.launchY*Cos(deltaAngle) - instance.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(source) + instance.launchZ, instance.scale, instance.arc, tempItem)
set attack.targetItem = tempItem
elseif TargetDestructableFilter(source, tempDest) then
set attack = Node.create(true, GetUnitX(source) + instance.launchX*Cos(deltaAngle) + instance.launchY*Sin(deltaAngle), GetUnitY(source) + instance.launchY*Cos(deltaAngle) - instance.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(source) + instance.launchZ, instance.scale, instance.arc, tempDest)
set attack.targetDestructable = tempDest
else
set success = false
endif
if success then
set attack.main = mainAttack
set Attack(attack).showShadow = instance.showShadow
set attack.ownerId = GetPlayerId(GetOwningPlayer(source))
set attack.source = source
set attack.currentBounces = 0
set Attack(attack).damage = GetRandomReal(instance.minDamage, instance.maxDamage)
set Attack(attack).bounceRange = instance.bounceRange
set Attack(attack).maxBounces = instance.maxBounces
set Attack(attack).attacktype = instance.attacktype
set Attack(attack).damagetype = instance.damagetype
set Attack(attack).weapontype = instance.weapontype
set attack.speed = instance.speed
set attack.minSpeed = instance.minSpeed
set attack.maxSpeed = instance.maxSpeed
set attack.acceleration = instance.acceleration
call attack.addSfx(instance.model)
set count = sfxCount[instance]
loop
exitwhen count == 0
call attack.addSfx(sfxTable[instance].string[count])
set count = count - 1
endloop
if fireEvent then
if mainAttack then
call FireHandler(EVENT_ATTACK_FINISH, instance, attack)
endif
call FireHandler(EVENT_ATTACK_MISSILE_LAUNCH, instance, attack)
endif
endif
endif
return attack
endmethod
method destroy takes nothing returns nothing
debug call DebugError(Node(this).flag, "AttackSystem", "destroy()", "thistype", 0, "Double-free")
call Node(this).destroy()
endmethod
endstruct
/*
* Main system struct
*/
struct CustomAttack extends array
implement SystemConstants
private static boolean orderHandlerEnabled = true
private static boolean updateAttackOrderType = true
private static boolean internalSmartOrder = false
private static unit filterUnit = null
private static group ENUM_GROUP = CreateGroup()
private static timer staticTimer = CreateTimer()
private static real maxCollision = 0.00
private thistype prev
private thistype next
private timer timer
private timer chaseTimer
private timer cooldownTimer
private timer reattackTimer
private integer attackOrderType
private real startPosX
private real startPosY
private boolean registered
readonly real targetX
readonly real targetY
readonly unit targetUnit
readonly item targetItem
readonly destructable targetDestructable
boolean melee
boolean showShadow
integer maxTargets
integer maxBounces
real bounceRange
real acquisitionRange
real range
real cooldown
real damagePoint
real launchX
real launchY
real launchZ
real impactZ
real arc
real speed
real minSpeed
real maxSpeed
real acceleration
real scale
real minDamage
real maxDamage
attacktype attacktype
damagetype damagetype
weapontype weapontype
string model
private method targetWidgetFilter takes nothing returns boolean
if this.targetUnit != null then
return TargetUnitFilter(GetUnitById(this), this.targetUnit)
elseif this.targetItem != null then
return TargetItemFilter(GetUnitById(this), this.targetItem)
elseif this.targetDestructable != null then
return TargetDestructableFilter(GetUnitById(this), this.targetDestructable)
endif
return false
endmethod
static method operator triggerInstance takes nothing returns thistype
return eventInstance
endmethod
static method operator triggerAttack takes nothing returns Attack
return eventAttack
endmethod
static method operator [] takes unit u returns thistype
return GetUnitId(u)
endmethod
static method isUnitRegistered takes unit u returns boolean
return thistype[u].registered
endmethod
method operator unit takes nothing returns unit
return GetUnitById(this)
endmethod
method operator target takes nothing returns widget
if this.targetUnit != null then
return this.targetUnit
elseif this.targetItem != null then
return this.targetItem
elseif this.targetDestructable != null then
return this.targetDestructable
endif
return null
endmethod
private static method stopUnit takes nothing returns nothing
local thistype this = DeleteTimer(GetExpiredTimer())
local unit attacker = GetUnitById(this)
local unit target = this.targetUnit
set this.targetUnit = null
set orderHandlerEnabled = false
call IssueImmediateOrderById(attacker, ORDER_stop)
set orderHandlerEnabled = true
call SetUnitFacing(attacker, Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
set this.targetUnit = target
set attacker = null
set target = null
endmethod
private static method faceTarget takes nothing returns nothing
local thistype this = DeleteTimer(GetExpiredTimer())
local unit attacker = GetUnitById(this)
local widget target = this.target
set orderHandlerEnabled = false
call IssueImmediateOrderById(GetUnitById(this), ORDER_holdposition)
set orderHandlerEnabled = true
call SetUnitFacing(attacker, Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
set attacker = null
set target = null
endmethod
private static method animateAttack takes nothing returns nothing
local thistype this = DeleteTimer(GetExpiredTimer())
local unit attacker = GetUnitById(this)
local widget target = this.target
set orderHandlerEnabled = false
call IssueImmediateOrderById(attacker, ORDER_holdposition)
set orderHandlerEnabled = true
call SetUnitFacing(attacker, Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
call SetUnitAnimation(attacker, "attack")
call SetUnitTimeScale(attacker, GetAttackAnimationScale(this))
set attacker = null
set target = null
endmethod
private static method resetAttackAnimationScale takes nothing returns nothing
call SetUnitTimeScale(GetUnitById(DeleteTimer(GetExpiredTimer())), 1.00)
endmethod
private method cancelCurrentAttack takes nothing returns nothing
call SetUnitTimeScale(this.unit, 1.00)
set this.targetUnit = null
set this.targetItem = null
set this.targetDestructable = null
set this.targetX = 0.00
set this.targetY = 0.00
call TimerStart(this.timer, 0.00, false, null)
call TimerStart(this.reattackTimer, 0.00, false, null)
call TimerStart(this.chaseTimer, 0.00, false, null)
endmethod
private method attackOrderTypeUpdate takes integer newType returns nothing
if updateAttackOrderType then
set this.attackOrderType = newType
endif
endmethod
private method getClosestTargetInRange takes real x, real y, real radius returns unit
local unit closest
local unit attacker = GetUnitById(this)
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, radius + maxCollision, null)
set radius = radius + BlzGetUnitCollisionSize(attacker)*0.50
loop
set closest = GetClosestUnitInGroup(x, y, ENUM_GROUP)
exitwhen closest == null
call GroupRemoveUnit(ENUM_GROUP, closest)
if closest != attacker and closest != filterUnit and IsUnitInRangeXY(closest, x, y, radius) and IsUnitEnemy(closest, GetOwningPlayer(attacker)) and TargetUnitFilter(attacker, closest) then
set this = GetUnitId(closest)
call GroupClear(ENUM_GROUP)
set closest = null
set attacker = null
return GetUnitById(this)
endif
endloop
set attacker = null
return null
endmethod
private method attackClosestInRange takes real radius returns nothing
local unit attacker = GetUnitById(this)
set this.targetUnit = this.getClosestTargetInRange(GetUnitX(attacker), GetUnitY(attacker), radius)
if this.targetUnit != null then
set internalSmartOrder = true
set updateAttackOrderType = false
call IssueTargetOrderById(attacker, ATTACK_ORDER_ID, this.targetUnit)
set updateAttackOrderType = true
set internalSmartOrder = false
endif
set attacker = null
endmethod
private static method onCooldown takes nothing returns nothing
local timer expired = GetExpiredTimer()
local thistype this = Table(KEY)[GetHandleId(expired)]
call PauseTimer(expired)
if this.attackOrderType == 1 then
if this.targetWidgetFilter() then
set internalSmartOrder = true
call IssueTargetOrderById(GetUnitById(this), ATTACK_ORDER_ID, this.target)
set internalSmartOrder = false
else
call IssueImmediateOrderById(GetUnitById(this), ORDER_stop)
endif
elseif this.attackOrderType == 2 then
call this.attackClosestInRange(this.range)
elseif this.attackOrderType == 3 then
call this.attackClosestInRange(this.acquisitionRange)
elseif this.attackOrderType == 4 and this.targetWidgetFilter() then
set updateAttackOrderType = false
set internalSmartOrder = true
call IssueTargetOrderById(GetUnitById(this), ATTACK_ORDER_ID, this.targetUnit)
set internalSmartOrder = false
set updateAttackOrderType = true
endif
set expired = null
endmethod
private static method onAttackLand takes Node attack returns boolean
local Attack prevAttack = triggerAttack
local thistype prevInstance = triggerInstance
local widget target
if attack.targetUnit != null then
set attack.landed = TargetUnitFilter(attack.source, attack.targetUnit)
set target = attack.targetUnit
elseif attack.targetItem != null then
set attack.landed = TargetItemFilter(attack.source, attack.targetItem)
set target = attack.targetItem
elseif attack.targetDestructable != null then
set attack.landed = TargetDestructableFilter(attack.source, attack.targetDestructable)
set target = attack.targetDestructable
else
set attack.landed = false
set target = null
endif
set eventInstance = thistype[attack.source]
set eventAttack = attack
call TriggerEvaluate(EventHandler[EVENT_ATTACK_MISSILE_IMPACT])
if attack.landed and attack.target != null then
call DealDamage(attack, target, true)
endif
set target = null
if attack.targetUnit != null and attack.landed and attack.currentBounces < Attack(attack).maxBounces then
set attack.currentBounces = attack.currentBounces + 1
set filterUnit = attack.targetUnit
set attack.targetUnit = thistype[attack.source].getClosestTargetInRange(attack.x, attack.y, Attack(attack).bounceRange)
set filterUnit = null
if attack.targetUnit != null then
set attack.travelled = 0.00
set attack.oX = attack.x
set attack.oY = attack.y
set attack.oZ = attack.z
set attack.targetX = GetUnitX(attack.targetUnit)
set attack.targetY = GetUnitY(attack.targetUnit)
set attack.targetZ = BlzGetLocalUnitZ(attack.targetUnit)
call TriggerEvaluate(EventHandler[EVENT_ATTACK_MISSILE_BOUNCE])
set eventAttack = prevAttack
set eventInstance = prevInstance
return false
endif
endif
set eventAttack = prevAttack
set eventInstance = prevInstance
return true
endmethod
/*
* This periodic method handles all missile movement
*/
private static method onPeriod takes nothing returns nothing
local Node node = Node(0).next
local widget target
local real prevX
local real prevY
local real prevZ
local real dx
local real dy
local real dz
local real xy
local real angle
local real zAngle
local real offset
local real speed
local real cos
local real sin
loop
exitwhen node == 0
set target = node.target
set prevX = node.x
set prevY = node.y
set prevZ = node.z
if target != null and not IsWidgetRemoved(target) then
set node.targetX = GetWidgetX(target)
set node.targetY = GetWidgetY(target)
endif
set dx = node.targetX - prevX
set dy = node.targetY - prevY
set angle = Atan2(dy, dx)
if target != null then
set offset = GetWidgetCollision(target)*0.50
set node.targetX = node.targetX - offset*Cos(angle)
set node.targetY = node.targetY - offset*Sin(angle)
if node.targetUnit == null then
set node.targetZ = BlzGetLocalUnitZ(node.targetUnit) + thistype[node.source].impactZ
else
set node.targetZ = GetSurfaceZ(node.targetX, node.targetY) + thistype[node.source].impactZ
endif
else
set node.targetZ = GetSurfaceZ(node.targetX, node.targetY)
endif
set dz = node.targetZ - prevZ
set node.speed = GetBoundedValue(node.speed + node.acceleration*PERIOD, node.minSpeed, node.maxSpeed)
set speed = node.speed*PERIOD
if dx*dx + dy*dy < speed*speed then
call node.move(node.targetX, node.targetY, node.targetZ)
set node.travelled = node.travelled + speed
if onAttackLand(node) then
call node.destroy()
endif
else
set cos = Cos(angle)
set sin = Sin(angle)
set node.x = prevX + speed*cos
set node.y = prevY + speed*sin
set node.z = node.getParabolaZ()
set node.travelled = node.travelled + speed
set zAngle = Atan2(node.z - prevZ, speed)
set xy = LOOKAT_OFFSET*Cos(zAngle)
set node.lookAtX = node.x + xy*cos
set node.lookAtY = node.y + xy*sin
set node.lookAtZ = node.z + LOOKAT_OFFSET*Sin(zAngle)
call node.move(node.x, node.y, node.z)
endif
set target = null
set node = node.next
endloop
endmethod
private method launchProjectile takes unit targetUnit, item targetItem, destructable targetDestructable, boolean isMainAttack returns boolean
local unit attacker = GetUnitById(this)
local real deltaAngle = bj_PI/2.00 - GetUnitFacing(attacker)*bj_DEGTORAD
local boolean success = true
local integer count
local real x
local real y
local Node attack
if not isMainAttack or TargetUnitFilter(attacker, targetUnit) then
set attack = Node.create(true, GetUnitX(attacker) + this.launchX*Cos(deltaAngle) + this.launchY*Sin(deltaAngle), GetUnitY(attacker) + this.launchY*Cos(deltaAngle) - this.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(attacker) + this.launchZ, this.scale, this.arc, targetUnit)
set attack.targetUnit = targetUnit
elseif TargetItemFilter(attacker, targetItem) then
set attack = Node.create(true, GetUnitX(attacker) + this.launchX*Cos(deltaAngle) + this.launchY*Sin(deltaAngle), GetUnitY(attacker) + this.launchY*Cos(deltaAngle) - this.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(attacker) + this.launchZ, this.scale, this.arc, targetItem)
set attack.targetItem = targetItem
elseif TargetDestructableFilter(attacker, targetDestructable) then
set attack = Node.create(true, GetUnitX(attacker) + this.launchX*Cos(deltaAngle) + this.launchY*Sin(deltaAngle), GetUnitY(attacker) + this.launchY*Cos(deltaAngle) - this.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(attacker) + this.launchZ, this.scale, this.arc, targetDestructable)
set attack.targetDestructable = targetDestructable
else
set success = false
endif
if success then
set attack.main = isMainAttack
set Attack(attack).showShadow = this.showShadow
set attack.source = attacker
set attack.currentBounces = 0
set Attack(attack).damage = GetRandomReal(this.minDamage, this.maxDamage)
set Attack(attack).bounceRange = this.bounceRange
set Attack(attack).maxBounces = this.maxBounces
set Attack(attack).attacktype = this.attacktype
set Attack(attack).damagetype = this.damagetype
set Attack(attack).weapontype = this.weapontype
set attack.speed = this.speed
set attack.minSpeed = this.minSpeed
set attack.maxSpeed = this.maxSpeed
set attack.acceleration = this.acceleration
call attack.addSfx(this.model)
set count = sfxCount[this]
loop
exitwhen count == 0
call attack.addSfx(sfxTable[this].string[count])
set count = count - 1
endloop
if isMainAttack then
call FireHandler(EVENT_ATTACK_FINISH, this, attack)
endif
call FireHandler(EVENT_ATTACK_MISSILE_LAUNCH, this, attack)
endif
set attacker = null
return success
endmethod
private static method onAttackLaunch takes nothing returns nothing
local timer expired = GetExpiredTimer()
local thistype this = Table(KEY)[GetHandleId(expired)]
local unit attacker = GetUnitById(this)
local real cooldown = RMaxBJ(this.cooldown - this.damagePoint, 0.00)
local unit picked
local integer count
local real damage
local real x
local real y
local thistype prevInstance
local Node prevAttack
local Node attack
if this.melee then
set attack = Node.create(false, 0.00, 0.00, 0.00, 0.00, 0.00, null)
set attack.main = true
set attack.source = attacker
set attack.targetUnit = this.targetUnit
set attack.targetItem = this.targetItem
set attack.targetDestructable = this.targetDestructable
set Attack(attack).damage = GetRandomReal(this.minDamage, this.maxDamage)
set Attack(attack).attacktype = this.attacktype
set Attack(attack).damagetype = this.damagetype
set Attack(attack).weapontype = this.weapontype
if attack.targetUnit != null then
set attack.landed = TargetUnitFilter(attacker, attack.targetUnit)
elseif attack.targetItem != null then
set attack.landed = TargetItemFilter(attacker, attack.targetItem)
elseif attack.targetDestructable != null then
set attack.landed = TargetDestructableFilter(attacker, attack.targetDestructable)
endif
set prevInstance = eventInstance
set prevAttack = eventAttack
set eventInstance = this
set eventAttack = attack
call TriggerEvaluate(EventHandler[EVENT_ATTACK_FINISH])
if attack.flag then
if attack.landed and attack.target != null then
call DealDamage(attack, attack.target, false)
endif
call attack.destroy()
endif
set eventAttack = prevAttack
set eventInstance = prevInstance
else
set count = this.maxTargets
if count > 0 then
if this.launchProjectile(this.targetUnit, this.targetItem, this.targetDestructable, true) then
set count = count - 1
if count > 0 and this.targetUnit != null then
set x = GetUnitX(attacker)
set y = GetUnitY(attacker)
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, this.range + maxCollision, null)
loop
set picked = GetClosestUnitInGroup(x, y, ENUM_GROUP)
exitwhen picked == null or count == 0
call GroupRemoveUnit(ENUM_GROUP, picked)
if picked != attacker and picked != this.targetUnit and IsUnitInRange(attacker, picked, this.range) and IsUnitEnemy(picked, GetOwningPlayer(attacker)) and TargetUnitFilter(attacker, picked) then
set count = count - 1
call this.launchProjectile(picked, null, null, false)
endif
endloop
if picked != null then
call GroupClear(ENUM_GROUP)
set picked = null
endif
endif
else
call this.cancelCurrentAttack()
endif
endif
endif
call TimerStart(GetTimer(this), RMinBJ(this.damagePoint, cooldown), false, function thistype.resetAttackAnimationScale)
call TimerStart(this.reattackTimer, cooldown, false, function thistype.onCooldown)
call TimerStart(this.cooldownTimer, cooldown, false, null)
call PauseTimer(expired)
set expired = null
set attacker = null
endmethod
private static method onTurnPeriodic takes nothing returns nothing
local timer expired = GetExpiredTimer()
local thistype this = Table(KEY)[GetHandleId(expired)]
local unit attacker = GetUnitById(this)
local widget target = this.target
local real facing = GetUnitFacing(attacker)
local real deltaAngle = RAbsBJ(GetUnitFacing(attacker) - Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
if deltaAngle > 180.00 then
set deltaAngle = 360.00 - deltaAngle
endif
if deltaAngle <= ATTACK_ANGLE_TOLERANCE then
call FireHandler(EVENT_ATTACK_START, this, 0)
set orderHandlerEnabled = false
call IssueImmediateOrderById(attacker, ORDER_holdposition)
set orderHandlerEnabled = true
call SetUnitAnimation(attacker, "attack")
call SetUnitTimeScale(attacker, GetAttackAnimationScale(this))
call TimerStart(expired, this.damagePoint, false, function thistype.onAttackLaunch)
else
call SetUnitFacing(attacker, Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
endif
set attacker = null
set target = null
set expired = null
endmethod
private static method onChasePeriodic takes nothing returns nothing
local thistype this = Table(KEY)[GetHandleId(GetExpiredTimer())]
local unit attacker = GetUnitById(this)
local unit closest
local widget targetWidget
local real dx
local real dy
local real range
if GetUnitCurrentOrder(attacker) == ORDER_dropitem then
call this.cancelCurrentAttack()
call this.attackOrderTypeUpdate(0)
endif
if this.attackOrderType == 2 then
if GetUnitCurrentOrder(attacker) == 0 then
set closest = this.getClosestTargetInRange(GetUnitX(attacker), GetUnitY(attacker), this.range)
if closest != null and closest != this.targetUnit then
call IssueImmediateOrderById(attacker, ORDER_holdposition)
set closest = null
endif
endif
elseif this.attackOrderType == 3 then
if GetUnitCurrentOrder(attacker) == 0 then
set closest = this.getClosestTargetInRange(GetUnitX(attacker), GetUnitY(attacker), this.acquisitionRange)
if closest != null and closest != this.targetUnit then
call IssueImmediateOrderById(attacker, ORDER_stop)
set closest = null
endif
endif
endif
if this.targetWidgetFilter() then
set targetWidget = this.target
else
set targetWidget = null
endif
if targetWidget != null then
set dx = GetWidgetX(targetWidget) - GetUnitX(attacker)
set dy = GetWidgetY(targetWidget) - GetUnitY(attacker)
set range = this.range + BlzGetUnitCollisionSize(attacker)/2.00 + GetWidgetCollision(targetWidget)/2.00
if dx*dx + dy*dy <= range*range then
if TimerGetRemaining(this.cooldownTimer) == 0.00 then
set orderHandlerEnabled = false
call IssueImmediateOrderById(attacker, ORDER_holdposition)
set orderHandlerEnabled = true
call SetUnitFacing(attacker, Atan2(dy, dx)*bj_RADTODEG)
call TimerStart(this.timer, PERIOD, true, function thistype.onTurnPeriodic)
call PauseTimer(GetExpiredTimer())
elseif GetUnitCurrentOrder(attacker) == ATTACK_ORDER_ID then
set orderHandlerEnabled = false
call IssueImmediateOrderById(attacker, ORDER_stop)
set orderHandlerEnabled = true
endif
elseif GetUnitCurrentOrder(attacker) == ORDER_stop then
set internalSmartOrder = true
set updateAttackOrderType = false
call IssueTargetOrderById(attacker, ATTACK_ORDER_ID, targetWidget)
set updateAttackOrderType = true
set internalSmartOrder = false
endif
set targetWidget = null
endif
set attacker = null
endmethod
private static method onPointTarget takes nothing returns nothing
local timer expired = GetExpiredTimer()
local thistype this = Table(KEY)[GetHandleId(expired)]
local unit attacker = GetUnitById(this)
local unit target
if (GetUnitX(attacker) == this.targetX and GetUnitY(attacker) == this.targetY) or GetUnitCurrentOrder(attacker) == 0 then
set this.targetUnit = null
set this.targetItem = null
set this.targetDestructable = null
set this.attackOrderType = 3
call PauseTimer(expired)
else
set target = this.getClosestTargetInRange(GetUnitX(attacker), GetUnitY(attacker), this.acquisitionRange)
if target != null then
call PauseTimer(expired)
set internalSmartOrder = true
set updateAttackOrderType = false
call IssueTargetOrderById(attacker, ATTACK_ORDER_ID, target)
set updateAttackOrderType = true
set internalSmartOrder = false
set target = null
endif
endif
set expired = null
set attacker = null
endmethod
private method targetPoint takes real x, real y returns nothing
set this.attackOrderType = 4
set this.targetX = x
set this.targetY = y
set this.targetUnit = null
set this.targetItem = null
set this.targetDestructable = null
set orderHandlerEnabled = false
call IssuePointOrderById(GetUnitById(this), ORDER_smart, x, y)
set orderHandlerEnabled = true
call TimerStart(this.chaseTimer, PERIOD, true, function thistype.onPointTarget)
endmethod
private static method autoAcquireTargets takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen this == 0
if GetUnitCurrentOrder(GetUnitById(this)) == ORDER_dropitem and this.attackOrderType != 0 then
call this.cancelCurrentAttack()
call this.attackOrderTypeUpdate(0)
endif
if this.target == null then
if this.attackOrderType == 2 then
call this.attackClosestInRange(this.range)
elseif GetUnitCurrentOrder(GetUnitById(this)) == 0 and (this.attackOrderType == 3 or this.attackOrderType == 0) then
if this.attackOrderType == 0 then
set this.attackOrderType = 3
endif
set this.startPosX = GetUnitX(GetUnitById(this))
set this.startPosY = GetUnitY(GetUnitById(this))
call this.attackClosestInRange(this.acquisitionRange)
endif
elseif this.targetUnit != null and not TargetUnitFilter(GetUnitById(this), this.targetUnit) then
if this.attackOrderType == 1 then
call IssueImmediateOrderById(GetUnitById(this), ORDER_stop)
elseif this.attackOrderType == 3 then
call this.targetPoint(this.startPosX, this.startPosY)
elseif this.attackOrderType == 4 then
call this.targetPoint(this.targetX, this.targetY)
endif
endif
set this = this.next
endloop
endmethod
method addAttackSfx takes string model returns thistype
local integer key = StringHash(model)
if not sfxTable[this].has(key) then
set sfxCount[this] = sfxCount[this] + 1
set sfxTable[this].string[sfxCount[this]] = model
set sfxTable[this][key] = sfxCount[this]
debug else
debug call DebugWarning(true, "AttackSystem", "addAttackSfx()", "thistype", this, "The specified model string was already added [" + model + "]")
endif
return this
endmethod
method removeAttackSfx takes string model returns thistype
local integer key = StringHash(model)
local integer index = sfxTable[this][key]
if index > 0 then
set sfxTable[this].string[index] = sfxTable[this].string[sfxCount[this]]
call sfxTable[this].string.remove(sfxCount[this])
call sfxTable[this].remove(key)
set sfxCount[this] = sfxCount[this] - 1
debug else
debug call DebugWarning(true, "AttackSystem", "removeAttackSfx()", "thistype", this, "The specified model string was not yet added [" + model + "]")
endif
return this
endmethod
method clearAttackSfx takes nothing returns thistype
set sfxCount[this] = 0
call sfxTable[this].flush()
return this
endmethod
method operator enabled takes nothing returns boolean
return (this.prev.next) == (this)
endmethod
method operator enabled= takes boolean flag returns nothing
if this.registered then
if flag then
if not this.enabled then
if thistype(0).next == 0 then
call TimerStart(staticTimer, PERIOD, true, function thistype.autoAcquireTargets)
endif
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
call BlzUnitDisableAbility(this.unit, ATTACK_ABIL_ID, false, false)
endif
elseif this.enabled then
call BlzUnitDisableAbility(this.unit, ATTACK_ABIL_ID, true, false)
set this.next.prev = this.prev
set this.prev.next = this.next
if thistype(0).next == 0 then
call PauseTimer(staticTimer)
endif
endif
endif
endmethod
static method register takes unit u returns boolean
local thistype this = GetUnitId(u)
if not this.registered then
call BlzUnitDisableAbility(u, 'Aatk', true, true)
call UnitAddAbility(u, ATTACK_ABIL_ID)
call BlzUnitDisableAbility(u, ATTACK_ABIL_ID, true, false)
set this.registered = true
set this.enabled = true
set this.timer = GetTimer(this)
set this.chaseTimer = GetTimer(this)
set this.cooldownTimer = GetTimer(this)
set this.reattackTimer = GetTimer(this)
implement optional SystemDefaults
return true
endif
return false
endmethod
static method unregister takes unit u returns boolean
local thistype this = GetUnitId(u)
if this.registered then
set this.registered = false
if this.enabled then
call UnitRemoveAbility(u, ATTACK_ABIL_ID)
set this.next.prev = this.prev
set this.prev.next = this.next
if thistype(0).next == 0 then
call PauseTimer(staticTimer)
endif
endif
call BlzUnitDisableAbility(u, 'Aatk', false, false)
call this.clearAttackSfx()
call DeleteTimer(this.timer)
call DeleteTimer(this.chaseTimer)
call DeleteTimer(this.cooldownTimer)
call DeleteTimer(this.reattackTimer)
set this.targetUnit = null
set this.targetItem = null
set this.targetDestructable = null
set this.melee = false
set this.maxTargets = 0
set this.maxBounces = 0
set this.acquisitionRange = 0.00
set this.range = 0.00
set this.bounceRange = 0.00
set this.cooldown = 0.00
set this.damagePoint = 0.00
set this.launchX = 0.00
set this.launchY = 0.00
set this.launchZ = 0.00
set this.impactZ = 0.00
set this.arc = 0.00
set this.speed = 0.00
set this.minSpeed = 0.00
set this.maxSpeed = 0.00
set this.acceleration = 0.00
set this.scale = 0.00
set this.minDamage = 0.00
set this.maxDamage = 0.00
set this.attacktype = null
set this.damagetype = null
set this.weapontype = null
set this.model = ""
return true
endif
return false
endmethod
static method registerEventHandler takes EventHandler eventHandler, code handlerFunc returns nothing
call eventHandler.register(handlerFunc)
endmethod
static method unregisterEventHandler takes EventHandler eventHandler, code handlerFunc returns nothing
call eventHandler.unregister(handlerFunc)
endmethod
static method clearEventHandlers takes EventHandler eventHandler returns nothing
call eventHandler.clear()
endmethod
private static method onAttackStart takes unit attacker, widget target returns boolean
local thistype this = GetUnitId(attacker)
local real dx = GetWidgetX(target) - GetUnitX(attacker)
local real dy = GetWidgetY(target) - GetUnitY(attacker)
local real range = this.range + BlzGetUnitCollisionSize(attacker)/2.00 + GetWidgetCollision(target)/2.00
local real deltaAngle
if dx*dx + dy*dy <= range*range then
set deltaAngle = RAbsBJ(GetUnitFacing(attacker) - Atan2(dy, dx)*bj_RADTODEG)
if deltaAngle > 180.00 then
set deltaAngle = 360.00 - deltaAngle
endif
if deltaAngle > ATTACK_ANGLE_TOLERANCE then
call TimerStart(GetTimer(this), 0.00, false, function thistype.faceTarget)
call TimerStart(this.timer, PERIOD, true, function thistype.onTurnPeriodic)
else
call FireHandler(EVENT_ATTACK_START, this, 0)
call TimerStart(GetTimer(this), 0.00, false, function thistype.animateAttack)
call TimerStart(this.timer, this.damagePoint, false, function thistype.onAttackLaunch)
endif
return true
endif
return false
endmethod
private static method onOrder takes nothing returns nothing
local unit u
local unit targetUnit
local widget target
local widget tempTarget
local integer id
local thistype this
local real dx
local real dy
local real range
if orderHandlerEnabled then
set u = GetOrderedUnit()
if GetUnitAbilityLevel(u, ATTACK_ABIL_ID) > 0 then
set tempTarget = GetOrderTarget()
set id = GetIssuedOrderId()
set this = GetUnitId(u)
if id == ORDER_smart and tempTarget != null then
set targetUnit = GetOrderTargetUnit()
if targetUnit != null and IsUnitEnemy(targetUnit, GetOwningPlayer(u)) then
call IssueTargetOrderById(u, ATTACK_ORDER_ID, tempTarget)
else
call this.cancelCurrentAttack()
call this.attackOrderTypeUpdate(0)
endif
set targetUnit = null
elseif id == ORDER_holdposition then
call this.cancelCurrentAttack()
call this.attackOrderTypeUpdate(2)
elseif id == ORDER_stop or id == 0 then
call this.cancelCurrentAttack()
call this.attackOrderTypeUpdate(3)
elseif id == ATTACK_ORDER_ID then
if TimerGetRemaining(this.timer) > 0.00 and tempTarget != this.target then
call this.cancelCurrentAttack()
endif
if internalSmartOrder then
set this.targetUnit = GetOrderTargetUnit()
if this.targetWidgetFilter() then
set target = this.target
else
set target = null
endif
else// Manual attack order
if GetOrderTargetUnit() != null then
set this.targetUnit = GetOrderTargetUnit()
set this.targetItem = null
set this.targetDestructable = null
set target = this.targetUnit
elseif GetOrderTargetItem() != null then
set this.targetUnit = null
set this.targetItem = GetOrderTargetItem()
set this.targetDestructable = null
set target = this.targetItem
elseif GetOrderTargetDestructable() != null then
set this.targetUnit = null
set this.targetItem = null
set this.targetDestructable = GetOrderTargetDestructable()
set target = this.targetDestructable
else
set target = null
endif
endif
if target == null then
call this.targetPoint(GetOrderPointX(), GetOrderPointY())
else
if internalSmartOrder then
call this.attackOrderTypeUpdate(1)
else
set this.attackOrderType = 1
set this.targetX = 0.00
set this.targetY = 0.00
endif
if TimerGetRemaining(this.cooldownTimer) == 0.00 then
if not onAttackStart(u, target) then
call TimerStart(this.chaseTimer, PERIOD, true, function thistype.onChasePeriodic)
endif
else
set dx = GetWidgetX(target) - GetUnitX(u)
set dy = GetWidgetY(target) - GetUnitY(u)
set range = this.range + BlzGetUnitCollisionSize(u)/2.00 + GetWidgetCollision(target)/2.00
if dx*dx + dy*dy <= range*range then
call TimerStart(GetTimer(this), 0.00, false, function thistype.faceTarget)
endif
call TimerStart(this.chaseTimer, PERIOD, true, function thistype.onChasePeriodic)
endif
set target = null
endif
elseif (id < ORDER_moveslot1 or id > ORDER_useslot6) then
call this.cancelCurrentAttack()
call this.attackOrderTypeUpdate(0)
endif
set tempTarget = null
endif
set u = null
endif
endmethod
private static method onIndex takes nothing returns nothing
set maxCollision = RMaxBJ(BlzGetUnitCollisionSize(GetIndexedUnit()), maxCollision)
endmethod
private static method onDeindex takes nothing returns nothing
call unregister(GetIndexedUnit())
endmethod
private static method registerEvent takes playerunitevent whichEvent, code handler returns nothing
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterAnyPlayerUnitEvent(whichEvent, handler)
else
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, whichEvent)
call TriggerAddCondition(t, Filter(handler))
set t = null
endif
endmethod
private static method init takes nothing returns nothing
local code onIndex = function thistype.onIndex
local code onDeindex = function thistype.onDeindex
call OnUnitIndex(onIndex)
call OnUnitDeindex(onDeindex)
set onPeriodCode = function thistype.onPeriod
call registerEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onOrder)
call registerEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
call registerEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
set dummyUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'ncop', 0.00, 0.00, 0.00)
call SetUnitVertexColor(dummyUnit, 0, 0, 0, 0)
call UnitRemoveAbility(dummyUnit, 'Amov')
call UnitRemoveAbility(dummyUnit, 'Aatk')
call UnitAddAbility(dummyUnit, 'Aloc')
endmethod
implement Initializer
endstruct
private module Initializer
private static method onInit takes nothing returns nothing
set sfxTable = TableArray[JASS_MAX_ARRAY_SIZE - 1]
static if not LIBRARY_WorldBounds then
call MapBounds.init()
endif
call EventHandler.init()
call Node.init()
call CustomAttack.init()
endmethod
endmodule
/*
* Wrapper Functions
*/
function GetEventAttackDamage takes nothing returns real
return CustomAttack.triggerAttack.damage
endfunction
function GetEventAttackSource takes nothing returns unit
return CustomAttack.triggerAttack.source
endfunction
function GetEventAttackTarget takes nothing returns widget
return CustomAttack.triggerAttack.target
endfunction
function GetEventAttackMaxBounces takes nothing returns integer
return CustomAttack.triggerAttack.maxBounces
endfunction
function GetEventAttackCurrentBounces takes nothing returns integer
return CustomAttack.triggerAttack.currentBounces
endfunction
function GetEventAttackTargetUnit takes nothing returns unit
return CustomAttack.triggerAttack.targetUnit
endfunction
function GetEventAttackTargetItem takes nothing returns item
return CustomAttack.triggerAttack.targetItem
endfunction
function GetEventAttackTargetDestructable takes nothing returns destructable
return CustomAttack.triggerAttack.targetDestructable
endfunction
function GetEventAttackTargetX takes nothing returns real
return CustomAttack.triggerAttack.targetX
endfunction
function GetEventAttackTargetY takes nothing returns real
return CustomAttack.triggerAttack.targetY
endfunction
function GetEventAttackTargetZ takes nothing returns real
return CustomAttack.triggerAttack.targetZ
endfunction
function GetEventAttackMissileSpeed takes nothing returns real
return CustomAttack.triggerAttack.speed
endfunction
function GetEventAttackMissileMinSpeed takes nothing returns real
return CustomAttack.triggerAttack.minSpeed
endfunction
function GetEventAttackMissileMaxSpeed takes nothing returns real
return CustomAttack.triggerAttack.maxSpeed
endfunction
function GetEventAttackMissileAcceleration takes nothing returns real
return CustomAttack.triggerAttack.acceleration
endfunction
function GetEventAttackMissileScale takes nothing returns real
return CustomAttack.triggerAttack.scale
endfunction
function GetEventAttackMissileArc takes nothing returns real
return CustomAttack.triggerAttack.arc
endfunction
function GetEventAttackType takes nothing returns attacktype
return CustomAttack.triggerAttack.attacktype
endfunction
function GetEventDamageType takes nothing returns damagetype
return CustomAttack.triggerAttack.damagetype
endfunction
function GetEventWeaponType takes nothing returns weapontype
return CustomAttack.triggerAttack.weapontype
endfunction
function IsEventAttackMelee takes nothing returns boolean
return CustomAttack.triggerAttack.melee
endfunction
function IsEventAttackRanged takes nothing returns boolean
return CustomAttack.triggerAttack.ranged
endfunction
function IsEventAttackMain takes nothing returns boolean
return CustomAttack.triggerAttack.main
endfunction
function DestroyEventAttack takes nothing returns nothing
call CustomAttack.triggerAttack.destroy()
endfunction
function SetEventAttackDamage takes real damage returns nothing
set CustomAttack.triggerAttack.damage = damage
endfunction
function SetEventAttackTarget takes widget target returns nothing
set CustomAttack.triggerAttack.target = target
endfunction
function SetEventAttackTargetUnit takes unit target returns nothing
set CustomAttack.triggerAttack.targetUnit = target
endfunction
function SetEventAttackTargetItem takes item target returns nothing
set CustomAttack.triggerAttack.targetItem = target
endfunction
function SetEventAttackTargetDestructable takes destructable target returns nothing
set CustomAttack.triggerAttack.targetDestructable = target
endfunction
function SetEventAttackTargetX takes real x returns nothing
set CustomAttack.triggerAttack.targetX = x
endfunction
function SetEventAttackTargetY takes real y returns nothing
set CustomAttack.triggerAttack.targetY = y
endfunction
function SetEventAttackTargetZ takes real z returns nothing
set CustomAttack.triggerAttack.targetZ = z
endfunction
function SetEventAttackMissileSpeed takes real speed returns nothing
set CustomAttack.triggerAttack.speed = speed
endfunction
function SetEventAttackMissileMinSpeed takes real speed returns nothing
set CustomAttack.triggerAttack.minSpeed = speed
endfunction
function SetEventAttackMissileMaxSpeed takes real speed returns nothing
set CustomAttack.triggerAttack.maxSpeed = speed
endfunction
function SetEventAttackMissileAcceleration takes real acceleration returns nothing
set CustomAttack.triggerAttack.acceleration = acceleration
endfunction
function SetEventAttackMissileScale takes real scale returns nothing
set CustomAttack.triggerAttack.scale = scale
endfunction
function SetEventAttackMissileArc takes real arc returns nothing
set CustomAttack.triggerAttack.arc = arc
endfunction
function SetEventAttackType takes attacktype whichAttackType returns nothing
set CustomAttack.triggerAttack.attacktype = whichAttackType
endfunction
function SetEventDamageType takes damagetype whichDamageType returns nothing
set CustomAttack.triggerAttack.damagetype = whichDamageType
endfunction
function SetEventWeaponType takes weapontype whichWeaponType returns nothing
set CustomAttack.triggerAttack.weapontype = whichWeaponType
endfunction
function GetUnitAttackDamage takes unit whichUnit returns real
local CustomAttack unitAttack = CustomAttack[whichUnit]
return (unitAttack.minDamage + unitAttack.maxDamage)*0.50
endfunction
function GetUnitAttackType takes unit whichUnit returns attacktype
return CustomAttack[whichUnit].attacktype
endfunction
function GetUnitDamageType takes unit whichUnit returns damagetype
return CustomAttack[whichUnit].damagetype
endfunction
function GetUnitWeaponType takes unit whichUnit returns weapontype
return CustomAttack[whichUnit].weapontype
endfunction
function GetUnitAttackModel takes unit whichUnit returns string
return CustomAttack[whichUnit].model
endfunction
function GetUnitAttackDamagePoint takes unit whichUnit returns real
return CustomAttack[whichUnit].damagePoint
endfunction
function GetUnitAttackCooldown takes unit whichUnit returns real
return CustomAttack[whichUnit].cooldown
endfunction
function GetUnitAttackRange takes unit whichUnit returns real
return CustomAttack[whichUnit].range
endfunction
function GetUnitAttackAcquireRange takes unit whichUnit returns real
return CustomAttack[whichUnit].acquisitionRange
endfunction
function GetUnitAttackMissileSpeed takes unit whichUnit returns real
return CustomAttack[whichUnit].speed
endfunction
function GetUnitAttackMissileMinSpeed takes unit whichUnit returns real
return CustomAttack[whichUnit].minSpeed
endfunction
function GetUnitAttackMissileMaxSpeed takes unit whichUnit returns real
return CustomAttack[whichUnit].maxSpeed
endfunction
function GetUnitAttackLaunchX takes unit whichUnit returns real
return CustomAttack[whichUnit].launchX
endfunction
function GetUnitAttackLaunchY takes unit whichUnit returns real
return CustomAttack[whichUnit].launchY
endfunction
function GetUnitAttackLaunchZ takes unit whichUnit returns real
return CustomAttack[whichUnit].launchZ
endfunction
function GetUnitAttackImpactZ takes unit whichUnit returns real
return CustomAttack[whichUnit].impactZ
endfunction
function GetUnitAttackMaxTargets takes unit whichUnit returns integer
return CustomAttack[whichUnit].maxTargets
endfunction
function GetUnitAttackMaxBounces takes unit whichUnit returns integer
return CustomAttack[whichUnit].maxBounces
endfunction
function GetUnitAttackTarget takes unit whichUnit returns widget
return CustomAttack[whichUnit].target
endfunction
function GetUnitAttackTargetUnit takes unit whichUnit returns unit
return CustomAttack[whichUnit].targetUnit
endfunction
function GetUnitAttackTargetItem takes unit whichUnit returns item
return CustomAttack[whichUnit].targetItem
endfunction
function GetUnitAttackTargetDestructable takes unit whichUnit returns destructable
return CustomAttack[whichUnit].targetDestructable
endfunction
function IsUnitAttackShadowVisible takes unit whichUnit returns boolean
return CustomAttack[whichUnit].showShadow
endfunction
function IsUnitAttackMelee takes unit whichUnit returns boolean
return CustomAttack[whichUnit].melee
endfunction
function IsUnitAttackRanged takes unit whichUnit returns boolean
return not CustomAttack[whichUnit].melee
endfunction
function SetUnitAttackDamageMin takes unit whichUnit, real amount returns nothing
set CustomAttack[whichUnit].minDamage = amount
endfunction
function SetUnitAttackDamageMax takes unit whichUnit, real amount returns nothing
set CustomAttack[whichUnit].maxDamage = amount
endfunction
function SetUnitAttackType takes unit whichUnit, attacktype whichAttackType returns nothing
set CustomAttack[whichUnit].attacktype = whichAttackType
endfunction
function SetUnitDamageType takes unit whichUnit, damagetype whichDamageType returns nothing
set CustomAttack[whichUnit].damagetype = whichDamageType
endfunction
function SetUnitWeaponType takes unit whichUnit, weapontype whichWeaponType returns nothing
set CustomAttack[whichUnit].weapontype = whichWeaponType
endfunction
function SetUnitAttackModel takes unit whichUnit, string modelPath returns nothing
set CustomAttack[whichUnit].model = modelPath
endfunction
function SetUnitAttackDamagePoint takes unit whichUnit, real damagePoint returns nothing
set CustomAttack[whichUnit].damagePoint = damagePoint
endfunction
function SetUnitAttackCooldown takes unit whichUnit, real cooldown returns nothing
set CustomAttack[whichUnit].cooldown = cooldown
endfunction
function SetUnitAttackRange takes unit whichUnit, real range returns nothing
set CustomAttack[whichUnit].range = range
endfunction
function SetUnitAttackAcquireRange takes unit whichUnit, real acquireRange returns nothing
set CustomAttack[whichUnit].acquisitionRange = acquireRange
endfunction
function SetUnitAttackMissileSpeed takes unit whichUnit, real missileSpeed returns nothing
set CustomAttack[whichUnit].speed = missileSpeed
endfunction
function SetUnitAttackMissileMinSpeed takes unit whichUnit, real missileSpeed returns nothing
set CustomAttack[whichUnit].minSpeed = missileSpeed
endfunction
function SetUnitAttackMissileMaxSpeed takes unit whichUnit, real missileSpeed returns nothing
set CustomAttack[whichUnit].maxSpeed = missileSpeed
endfunction
function SetUnitAttackLaunchX takes unit whichUnit, real launchX returns nothing
set CustomAttack[whichUnit].launchX = launchX
endfunction
function SetUnitAttackLaunchY takes unit whichUnit, real launchY returns nothing
set CustomAttack[whichUnit].launchY = launchY
endfunction
function SetUnitAttackLaunchZ takes unit whichUnit, real launchZ returns nothing
set CustomAttack[whichUnit].launchZ = launchZ
endfunction
function SetUnitAttackImpactZ takes unit whichUnit, real impactZ returns nothing
set CustomAttack[whichUnit].impactZ = impactZ
endfunction
function SetUnitAttackMaxTargets takes unit whichUnit, integer maxTargets returns nothing
set CustomAttack[whichUnit].maxTargets = maxTargets
endfunction
function SetUnitAttackMaxBounces takes unit whichUnit, integer maxBounces returns nothing
set CustomAttack[whichUnit].maxBounces = maxBounces
endfunction
function SetUnitAttackShadowVisible takes unit whichUnit, boolean visible returns nothing
set CustomAttack[whichUnit].showShadow = visible
endfunction
function SetUnitAttackMelee takes unit whichUnit returns nothing
set CustomAttack[whichUnit].melee = true
endfunction
function SetUnitAttackRanged takes unit whichUnit returns nothing
set CustomAttack[whichUnit].melee = false
endfunction
function UnitAddAttackEffect takes unit whichUnit, string model returns nothing
call CustomAttack[whichUnit].addAttackSfx(model)
endfunction
function UnitRemoveAttackEffect takes unit whichUnit, string model returns nothing
call CustomAttack[whichUnit].removeAttackSfx(model)
endfunction
function UnitClearAttackEffect takes unit whichUnit returns nothing
call CustomAttack[whichUnit].clearAttackSfx()
endfunction
function RegisterAttackEventHandler takes integer whichEvent, code handler returns nothing
call CustomAttack.registerEventHandler(whichEvent, handler)
endfunction
function UnregisterAttackEventHandler takes integer whichEvent, code handler returns nothing
call CustomAttack.unregisterEventHandler(whichEvent, handler)
endfunction
function UnitCustomAttackEnable takes unit whichUnit returns nothing
set CustomAttack[whichUnit].enabled = true
endfunction
function UnitCustomAttackDisable takes unit whichUnit returns nothing
set CustomAttack[whichUnit].enabled = false
endfunction
function IsUnitCustomAttackEnabled takes unit whichUnit returns boolean
return CustomAttack[whichUnit].enabled
endfunction
function UnitCustomAttackRegister takes unit whichUnit returns nothing
call CustomAttack.register(whichUnit)
endfunction
function UnitCustomAttackUnregister takes unit whichUnit returns nothing
call CustomAttack.unregister(whichUnit)
endfunction
function IsUnitCustomAttackRegistered takes unit whichUnit returns boolean
return CustomAttack.isUnitRegistered(whichUnit)
endfunction
endlibrary
library AttackSystemGUI initializer Init uses AttackSystem
/*
* After registering a unit using the operation trigger, the variables will be reset
* to these values. You can remove this textmacro is the resets are not needed.
*/
//! textmacro ATTACK_SYSTEM_GUI_VARS_RESET
set udg_AttackSystem_Melee = true
set udg_AttackSystem_ShowShadow = false
set udg_AttackSystem_MaxTargets = 1
set udg_AttackSystem_MaxBounces = 0
set udg_AttackSystem_AcquireRange = 1000.00
set udg_AttackSystem_Range = 600.00
set udg_AttackSystem_BounceRange = 500.00
set udg_AttackSystem_Cooldown = 1.00
set udg_AttackSystem_DamagePoint = 0.35
set udg_AttackSystem_LaunchX = 0.00
set udg_AttackSystem_LaunchY = 20.00
set udg_AttackSystem_LaunchZ = 60.00
set udg_AttackSystem_ImpactZ = 60.00
set udg_AttackSystem_Arc = 0.00
set udg_AttackSystem_Speed = 1000000.00
set udg_AttackSystem_MinSpeed = 0.00
set udg_AttackSystem_MaxSpeed = 1000000.00
set udg_AttackSystem_Acceleration = 0.00
set udg_AttackSystem_Scale = 1.00
set udg_AttackSystem_MinDamage = 0.00
set udg_AttackSystem_MaxDamage = 0.00
set udg_AttackSystem_AttackType = ATTACK_TYPE_HERO
set udg_AttackSystem_DamageType = DAMAGE_TYPE_NORMAL
set udg_AttackSystem_CombatSound = WEAPON_TYPE_WHOKNOWS
set udg_AttackSystem_Model = ""
//! endtextmacro
/*======================================================================================*/
globals
private constant location loc = Location(0.00, 0.00)
endglobals
private function FireEvent takes real whichEvent returns nothing
local real prevAmount = udg_AttackSystem__DamageAmount
local Attack prevAttack = udg_AttackSystem__TriggerAttack
local unit prevSource = udg_AttackSystem__TriggerUnit
local unit prevTargetUnit = udg_AttackSystem__TargetUnit
local item prevTargetItem = udg_AttackSystem__TargetItem
local destructable prevTargetDest = udg_AttackSystem__TargetDest
local location prevTargetPoint = udg_AttackSystem__TargetPoint
local boolean prevMeleeFlag = udg_AttackSystem__IsAttackMelee
local boolean prevMainFlag = udg_AttackSystem__IsAttackMain
local boolean wantDestroy = udg_AttackSystem_WantDestroyAttack
call MoveLocation(loc, GetEventAttackTargetX(), GetEventAttackTargetY())
set udg_AttackSystem__DamageAmount = GetEventAttackDamage()
set udg_AttackSystem__TriggerAttack = CustomAttack.triggerAttack
set udg_AttackSystem__TriggerUnit = GetEventAttackSource()
set udg_AttackSystem__TargetUnit = GetEventAttackTargetUnit()
set udg_AttackSystem__TargetItem = GetEventAttackTargetItem()
set udg_AttackSystem__TargetDest = GetEventAttackTargetDestructable()
set udg_AttackSystem__TargetPoint = loc
set udg_AttackSystem__IsAttackMelee = IsEventAttackMelee()
set udg_AttackSystem__IsAttackMain = IsEventAttackMain()
set udg_AttackSystem__Event = 0.00
set udg_AttackSystem__Event = whichEvent
set udg_AttackSystem__Event = 0.00
set udg_AttackSystem_WantDestroyAttack = false
if wantDestroy then
call DestroyEventAttack()
else
call SetEventAttackDamage(udg_AttackSystem__DamageAmount)
call SetEventAttackTargetUnit(udg_AttackSystem__TargetUnit)
call SetEventAttackTargetItem(udg_AttackSystem__TargetItem)
call SetEventAttackTargetDestructable(udg_AttackSystem__TargetDest)
if udg_AttackSystem__TargetPoint != loc then
call SetEventAttackTargetX(GetLocationX(udg_AttackSystem__TargetPoint))
call SetEventAttackTargetY(GetLocationY(udg_AttackSystem__TargetPoint))
call RemoveLocation(udg_AttackSystem__TargetPoint)
endif
endif
set udg_AttackSystem__DamageAmount = prevAmount
set udg_AttackSystem__TriggerAttack = prevAttack
set udg_AttackSystem__TriggerUnit = prevSource
set udg_AttackSystem__TargetUnit = prevTargetUnit
set udg_AttackSystem__TargetItem = prevTargetItem
set udg_AttackSystem__TargetDest = prevTargetDest
set udg_AttackSystem__TargetPoint = prevTargetPoint
set udg_AttackSystem__IsAttackMelee = prevMeleeFlag
set udg_AttackSystem__IsAttackMain = prevMainFlag
set udg_AttackSystem_WantDestroyAttack = wantDestroy
set prevSource = null
set prevTargetUnit = null
set prevTargetItem = null
set prevTargetDest = null
set prevTargetPoint = null
endfunction
private function OnAttackStart takes nothing returns nothing
call FireEvent(1.00)
endfunction
private function OnAttackFinish takes nothing returns nothing
call FireEvent(2.00)
endfunction
private function OnMissileLaunch takes nothing returns nothing
call FireEvent(3.00)
endfunction
private function OnMissileImpact takes nothing returns nothing
call FireEvent(4.00)
endfunction
private function OnMissileBounce takes nothing returns nothing
call FireEvent(5.00)
endfunction
private function OnAttackDestroy takes nothing returns nothing
call FireEvent(6.00)
endfunction
private function OnOperate takes nothing returns nothing
local CustomAttack this
if udg_AttackSystem_Operation == udg_AttackSystem_REGISTER_UNIT then
if udg_AttackSystem_Unit != null and not IsUnitCustomAttackRegistered(udg_AttackSystem_Unit) then
call CustomAttack.register(udg_AttackSystem_Unit)
set this = CustomAttack[udg_AttackSystem_Unit]
set this.melee = udg_AttackSystem_Melee
set this.showShadow = udg_AttackSystem_ShowShadow
set this.maxTargets = udg_AttackSystem_MaxTargets
set this.maxBounces = udg_AttackSystem_MaxBounces
set this.acquisitionRange = udg_AttackSystem_AcquireRange
set this.range = udg_AttackSystem_Range
set this.cooldown = udg_AttackSystem_Cooldown
set this.damagePoint = udg_AttackSystem_DamagePoint
set this.launchX = udg_AttackSystem_LaunchX
set this.launchY = udg_AttackSystem_LaunchY
set this.launchZ = udg_AttackSystem_LaunchZ
set this.impactZ = udg_AttackSystem_ImpactZ
set this.arc = udg_AttackSystem_Arc*bj_DEGTORAD
set this.speed = udg_AttackSystem_Speed
set this.minSpeed = udg_AttackSystem_MinSpeed
set this.maxSpeed = udg_AttackSystem_MaxSpeed
set this.acceleration = udg_AttackSystem_Acceleration
set this.scale = udg_AttackSystem_Scale
set this.minDamage = udg_AttackSystem_MinDamage
set this.maxDamage = udg_AttackSystem_MaxDamage
set this.attacktype = udg_AttackSystem_AttackType
set this.damagetype = udg_AttackSystem_DamageType
set this.weapontype = udg_AttackSystem_CombatSound
set this.model = udg_AttackSystem_Model
//! runtextmacro optional ATTACK_SYSTEM_GUI_VARS_RESET()
endif
elseif udg_AttackSystem_Operation == udg_AttackSystem_UNREGISTER_UNIT then
if udg_AttackSystem_Unit != null and IsUnitCustomAttackRegistered(udg_AttackSystem_Unit) then
call UnitCustomAttackUnregister(udg_AttackSystem_Unit)
endif
elseif udg_AttackSystem_Operation == udg_AttackSystem_ENABLE_UNIT then
if udg_AttackSystem_Unit != null then
call UnitCustomAttackEnable(udg_AttackSystem_Unit)
endif
elseif udg_AttackSystem_Operation == udg_AttackSystem_DISABLE_UNIT then
if udg_AttackSystem_Unit != null then
call UnitCustomAttackDisable(udg_AttackSystem_Unit)
endif
elseif udg_AttackSystem_Operation == udg_AttackSystem_DESTROY_ATTACK then
if udg_AttackSystem_Attack > 0 and Attack(udg_AttackSystem_Attack).flag then
call Attack(udg_AttackSystem_Attack).destroy()
endif
endif
endfunction
private function Init takes nothing returns nothing
set udg_AttackSystem_OperationTrigger = CreateTrigger()
call TriggerAddAction(udg_AttackSystem_OperationTrigger, function OnOperate)
call RegisterAttackEventHandler(EVENT_ATTACK_START, function OnAttackStart)
call RegisterAttackEventHandler(EVENT_ATTACK_FINISH, function OnAttackFinish)
call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_LAUNCH, function OnMissileLaunch)
call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_IMPACT, function OnMissileImpact)
call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_BOUNCE, function OnMissileBounce)
call RegisterAttackEventHandler(EVENT_ATTACK_DESTROY, function OnAttackDestroy)
endfunction
endlibrary
library EvasionSystem /*
*/uses /*
*/AttackSystem /* https://www.hiveworkshop.com/threads/307417/
*/UnitDex /* https://www.hiveworkshop.com/threads/248209/
*/Table /* https://www.hiveworkshop.com/threads/188084/
*/ErrorMessage /* https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage/main.j/
*///! novjass
|-------------|
| Description |
|-------------|
/*
This is a simple library used to simulate the default evasion behavior.
This snippet supports evasion stacking. Evasions stack multiplicatively.
*/
|-----|
| API |
|-----|
/*
Event Responses
*/constant function GetEventMissedAttack takes nothing returns Attack/*
*/constant function GetEventMissedSource takes nothing returns unit/*
*/constant function GetEventMissedTargetUnit takes nothing returns unit/*
A value of 1.00 is equal to 100%
*/function SetUnitEvasion takes unit whichUnit, real evasion returns nothing/*
*/function SetUnitAccuracy takes unit whichUnit, real accuracy returns nothing/*
*/function SetAttackAccuracy takes Attack whichAttack, real accuracy returns nothing/*
*/function GetUnitEvasion takes unit whichUnit returns real/*
*/function GetUnitAccuracy takes unit whichUnit returns real/*
*/function GetAttackAccuracy takes Attack whichAttack returns real/*
*/function RegisterAttackEvasionHandler takes code handler returns nothing/*
*/function UnregisterAttackEvasionHandler takes code handler returns nothing/*
*///! endnovjass
globals
/*=======================================================================*/
/* SYSTEM CONFIGURATION */
/*=======================================================================*/
/*
* The maximum distance the projectile can deflect from the position
* of the target unit when an evasion occurs
*/
private constant real MIN_DISTANCE_OFFSET_PERCENT = 0.15
/*
* The minimum distance the projectile can deflect from the position
* of the target unit when an evasion occurs
*/
private constant real MAX_DISTANCE_OFFSET_PERCENT = 0.25
/*
* The maximum angle (in radians) of the projectile's deflection location
* from the angle of the line from the target to the attacker's location
*/
private constant real MIN_ANGLE_OFFSET = 0.00
/*
* The minimum angle (in radians) of the projectile's deflection location
* from the angle of the line from the target to the attacker's location
*/
private constant real MAX_ANGLE_OFFSET = bj_PI
/*
* Evasion texttag text
*/
private constant string EVASION_TEXT_MESSAGE = "Miss"
/*
* Evasion texttag color
*/
private constant integer EVASION_TEXT_ALPHA = 0xCC
private constant integer EVASION_TEXT_RED = 0xFF
private constant integer EVASION_TEXT_GREEN = 0x00
private constant integer EVASION_TEXT_BLUE = 0x00
/*=======================================================================*/
/* END OF CONFIGURATION */
/*=======================================================================*/
endglobals
/*=======================================================================*/
globals
private boolean array isDummy
private real array evasion
private real array accuracy
private real array marksmanship
private Table table
private integer handlerCount = 0
private trigger handlerTrigger = null
private unit eventSource = null
private unit eventTarget = null
private Attack eventAttack = 0
endglobals
constant function GetEventMissedAttack takes nothing returns Attack
return eventAttack
endfunction
constant function GetEventMissedSource takes nothing returns unit
return eventSource
endfunction
constant function GetEventMissedTargetUnit takes nothing returns unit
return eventTarget
endfunction
function SetUnitEvasion takes unit u, real value returns nothing
set evasion[GetUnitId(u)] = value
endfunction
function AddUnitEvasion takes unit u, real value returns nothing
local integer i = GetUnitId(u)
set evasion[i] = evasion[i] + (1.00 - evasion[i])*value
endfunction
function SetUnitAccuracy takes unit u, real value returns nothing
set marksmanship[GetUnitId(u)] = value
endfunction
function AddUnitAccuracy takes unit u, real value returns nothing
local integer i = GetUnitId(u)
set marksmanship[i] = marksmanship[i] + (1.00 - marksmanship[i])*value
endfunction
function SetAttackAccuracy takes Attack attack, real value returns nothing
set accuracy[attack] = value
endfunction
function AddAttackAccuracy takes Attack attack, real value returns nothing
set accuracy[attack] = accuracy[attack] + (1.00 - accuracy[attack])*value
endfunction
function GetUnitEvasion takes unit u returns real
return evasion[GetUnitId(u)]
endfunction
function GetUnitAccuracy takes unit u returns real
return marksmanship[GetUnitId(u)]
endfunction
function GetAttackAccuracy takes Attack attack returns real
return accuracy[attack]
endfunction
function RegisterAttackEvasionHandler takes code handler returns nothing
local boolexpr expr = Filter(handler)
debug call ThrowError(table.handle.has(GetHandleId(expr)), "EvasionSystem", "RegisterAttackEvasionHandler()", "", 0, "Attempted to register an already registered handler")
if handlerCount == 0 then
set handlerTrigger = CreateTrigger()
endif
set handlerCount = handlerCount + 1
set table.triggercondition[GetHandleId(expr)] = TriggerAddCondition(handlerTrigger, expr)
set expr = null
endfunction
function UnregisterAttackEvasionHandler takes code handler returns nothing
local integer exprId = GetHandleId(Filter(handler))
debug call ThrowError(not table.handle.has(exprId), "EvasionSystem", "UnregisterAttackEvasionHandler()", "", 0, "Attempted to unregister an unregistered handler")
call TriggerRemoveCondition(handlerTrigger, table.triggercondition[exprId])
call table.handle.remove(exprId)
set handlerCount = handlerCount - 1
if handlerCount == 0 then
call DestroyTrigger(handlerTrigger)
set handlerTrigger = null
endif
endfunction
private function FireHandlers takes Attack attack, unit source, unit missedTarget returns nothing
local Attack prevAttack = eventAttack
local unit prevSource = eventSource
local unit prevTarget = eventTarget
set eventAttack = attack
set eventSource = source
set eventTarget = missedTarget
call TriggerEvaluate(handlerTrigger)
set eventAttack = prevAttack
set eventSource = prevSource
set eventTarget = prevTarget
set prevSource = null
set prevTarget = null
endfunction
private function DisplayEvasionText takes real x, real y returns nothing
local texttag text = CreateTextTag()
call SetTextTagPermanent(text, false)
call SetTextTagText(text, EVASION_TEXT_MESSAGE, 0.023)
call SetTextTagColor(text, EVASION_TEXT_RED, EVASION_TEXT_GREEN, EVASION_TEXT_BLUE, EVASION_TEXT_ALPHA)
call SetTextTagPos(text, x - 30.00, y, 70.00)
call SetTextTagVelocity(text, 0.00, 0.03)
call SetTextTagFadepoint(text, 1.20)
call SetTextTagLifespan(text, 2.00)
set text = null
endfunction
private function DivertAttackFromTarget takes Attack attack returns nothing
local unit attacker = attack.source
local unit target = attack.targetUnit
local real sourceX = GetUnitX(attacker)
local real sourceY = GetUnitY(attacker)
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real dx = sourceX - targetX
local real dy = sourceY - targetY
local real angle
local real offset
call DisplayEvasionText(sourceX, sourceY)
if GetRandomInt(0, 1) == 0 then
set angle = Atan2(dy, dx) + GetRandomReal(MIN_ANGLE_OFFSET, MAX_ANGLE_OFFSET)
else
set angle = Atan2(dy, dx) - GetRandomReal(MIN_ANGLE_OFFSET, MAX_ANGLE_OFFSET)
endif
set angle = angle - I2R(R2I(angle/(2.00*bj_PI)))*(2.00*bj_PI)
set offset = GetRandomReal(MIN_DISTANCE_OFFSET_PERCENT, MAX_DISTANCE_OFFSET_PERCENT)*SquareRoot(dx*dx + dy*dy)
set attack.target = null
set attack.targetX = targetX + offset*Cos(angle)
set attack.targetY = targetY + offset*Sin(angle)
set attacker = null
set target = null
endfunction
private function OnExpire takes nothing returns nothing
local timer expired = GetExpiredTimer()
local integer id = GetHandleId(expired)
local Attack attack = table[id]
local real evasion = 0.00
local unit missedTarget = attack.targetUnit
if attack.targetUnit != null then
set evasion = GetUnitEvasion(attack.targetUnit)
endif
if GetRandomReal(0.00, 1.00) <= evasion + (1.00 - evasion)*(1.00 - GetAttackAccuracy(attack)) then
call DivertAttackFromTarget(attack)
call FireHandlers(attack, attack.source, missedTarget)
endif
call table.remove(id)
set missedTarget = null
set expired = null
endfunction
private function OnAttackFinish takes nothing returns nothing
local real evasion = 0.00
local unit missedTarget
if CustomAttack.triggerAttack.melee then
set missedTarget = CustomAttack.triggerAttack.targetUnit
if missedTarget != null then
set evasion = GetUnitEvasion(missedTarget)
endif
if GetRandomReal(0.00, 1.00) <= evasion + (1.00 - evasion)*(1.00 - GetUnitAccuracy(CustomAttack.triggerAttack.source)) then
set CustomAttack.triggerAttack.target = null
call DisplayEvasionText(GetUnitX(CustomAttack.triggerAttack.source), GetUnitY(CustomAttack.triggerAttack.source))
call FireHandlers(CustomAttack.triggerAttack, CustomAttack.triggerAttack.source, missedTarget)
endif
set missedTarget = null
endif
endfunction
private function OnProjectileLaunch takes nothing returns nothing
local timer t = CreateTimer()
call SetAttackAccuracy(CustomAttack.triggerAttack, GetUnitAccuracy(CustomAttack.triggerAttack.source))
set table[GetHandleId(t)] = CustomAttack.triggerAttack
call TimerStart(t, 0.00, false, function OnExpire)
set t = null
endfunction
private function OnIndex takes nothing returns nothing
call SetUnitAccuracy(GetIndexedUnit(), 1.00)
endfunction
private module InitModule
private static method onInit takes nothing returns nothing
local code onIndex = function OnIndex
set table = Table.create()
call OnUnitIndex(onIndex)
call RegisterAttackEventHandler(EVENT_ATTACK_FINISH, function OnAttackFinish)
call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_LAUNCH, function OnProjectileLaunch)
endmethod
endmodule
private struct Init extends array
implement InitModule
endstruct
endlibrary
library AttackModifier /*
*/uses /*
*/AttackSystem /* https://www.hiveworkshop.com/threads/307417/
*/UnitDex /* https://www.hiveworkshop.com/threads/248209/
*/Table /* https://www.hiveworkshop.com/threads/188084/
*/NxList /* https://github.com/nestharus/JASS/blob/master/jass/Data%20Structures/NxList/script.j/
*/ErrorMessage /* https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage/main.j/
*///! novjass
|-------------|
| Description |
|-------------|
/*
The purpose of this library is to solve the problem that commonly arises from many coded attacker-modifier
systems such as orb-effects, life-steal, damage return, cleave, bash, critical-strike, and so on.
An attack modifier should IDEALLY take effect only when an attack that lands was launched by the time
the attack modifier is present on the attacker - a condition not taken into account by any attack-
modifying systems so far. For example, a ranged attack is launched by a unit who has a life-steal buff
but before the attack lands on the target, the life-steal buff on the attacker expires. At which case,
those system will not trigger the life-steal effect when that attack lands since they only check for the
conditions at the time the damage is detected. Such bug can be solved using this system (And at the cost
of using an alternative attack system =P).
*/
|-----|
| API |
|-----|
/*
*/struct AttackModifier/*
*/string missileArt /* The model of the special effect attached to the modified attacks
*/static method create takes nothing returns AttackModifier/*
*/method destroy takes nothing returns nothing/*
- Constructor/Destructor
*/method registerUnit takes unit whichUnit returns this/*
*/method unregisterUnit takes unit whichUnit returns this/*
*/method clearUnits takes unit whichUnit returns this/*
- Adds/Removes an attack modifier to/from a unit
*/method registerHandler takes code handler returns this/*
*/method unregisterHandler takes code handler returns this/*
*/method clearHandlers takes nothing returns this/*
- Registers/Unregisters a handler code that will run when a modified Attack lands on the target
- You can use the event responses of the AttackSystem library inside these registered handler codes
*/method updateAttackerArt takes string model, string attachPoint returns this/*
- Updates the special effect attached to all units with this attack modifier
*///! endnovjass
/*==================================== SYSTEM CODE ====================================*/
private module Init
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
private struct List1 extends array
AttackModifier data
implement NxList
endstruct
private struct List2 extends array
AttackModifier data
implement NxList
endstruct
private struct UnitList extends array
integer data
effect art
implement NxList
endstruct
struct AttackModifier
string missileArt
private trigger trigger
private integer handlerCount
private effect attackerEffect
private string attackerEffectModel
private static TableArray table
private static TableArray handlerTable
method updateAttackerArt takes string model, string attachPoint returns thistype
local UnitList node
if model != null and attachPoint != null then
set node = UnitList(this).first
loop
exitwhen node == 0
call DestroyEffect(node.art)
set node.art = AddSpecialEffectTarget(model, GetUnitById(node.data), attachPoint)
set node = node.next
endloop
endif
return this
endmethod
method registerUnit takes unit u returns thistype
local integer unitId = GetUnitId(u)
local List1 node = List1(unitId).enqueue()
debug call ThrowError(table[this].has(unitId), "AttackModifier", "registerUnit()", "thistype", this, "Attempted to register an already registered unit")
set node.data = this
set table[this][unitId] = node
set node = UnitList(this).enqueue()
set UnitList(node).data = unitId
set table[this][-unitId] = node
return this
endmethod
method unregisterUnit takes unit u returns thistype
local integer unitId = GetUnitId(u)
local UnitList node = table[this][-unitId]
debug call ThrowError(not table[this].has(unitId), "AttackModifier", "unregisterUnit()", "thistype", this, "Attempted to unregister an unregistered unit")
call DestroyEffect(node.art)
set node.art = null
call node.remove()
call List1(table[this][unitId]).remove()
call table[this].remove(-unitId)
call table[this].remove(unitId)
return this
endmethod
method clearUnits takes nothing returns thistype
local UnitList node = UnitList(this).first
local UnitList nextNode
loop
exitwhen node == 0
set nextNode = node.next
call this.unregisterUnit(GetUnitById(node.data))
set node = nextNode
endloop
return this
endmethod
method registerHandler takes code handler returns thistype
local boolexpr expr = Filter(handler)
debug call ThrowError(handlerTable[this].has(GetHandleId(expr)), "AttackModifier", "registerHandler()", "thistype", this, "Attempted to register an already registered handler")
if this.handlerCount == 0 then
set this.trigger = CreateTrigger()
endif
set this.handlerCount = this.handlerCount + 1
set handlerTable[this].triggercondition[GetHandleId(expr)] = TriggerAddCondition(this.trigger, expr)
return this
endmethod
method unregisterHandler takes code handler returns thistype
local integer exprId = GetHandleId(Filter(handler))
debug call ThrowError(not handlerTable[this].has(exprId), "AttackModifier", "unregisterHandler()", "thistype", this, "Attempted to unregister an unregistered handler")
call TriggerRemoveCondition(this.trigger, handlerTable[this].triggercondition[exprId])
call handlerTable[this].handle.remove(exprId)
set this.handlerCount = this.handlerCount - 1
if this.handlerCount == 0 then
call DestroyTrigger(this.trigger)
set this.trigger = null
endif
return this
endmethod
method clearHandlers takes nothing returns thistype
if this.handlerCount > 0 then
call handlerTable[this].flush()
call DestroyTrigger(this.trigger)
set this.trigger = null
set this.handlerCount = 0
endif
return this
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
call UnitList(this).clear()
debug set table[0].boolean[this] = true
return this
endmethod
method destroy takes nothing returns nothing
debug call ThrowError(not table[0].boolean[this], "AttackModifier", "destroy()", "thistype", this, "Double-free Attempted")
debug set table[0].boolean[this] = false
call this.clearUnits()
call this.clearHandlers()
call UnitList(this).destroy()
call this.deallocate()
endmethod
private static method onMeleeAttackLaunch takes nothing returns nothing
local List1 node
if IsEventAttackMelee() then
set node = List1(GetUnitId(GetEventAttackSource())).first
loop
exitwhen node == 0
call TriggerEvaluate(node.data.trigger)
set node = node.next
endloop
endif
endmethod
private static method onAttackMissileLaunch takes nothing returns nothing
local List1 node = List1(GetUnitId(GetEventAttackSource())).first
call List2(CustomAttack.triggerAttack).clear()
loop
exitwhen node == 0
set List2(CustomAttack.triggerAttack).enqueue().data = node.data
call CustomAttack.triggerAttack.addSfx(node.data.missileArt)
set node = node.next
endloop
endmethod
private static method onAttackMissileImpact takes nothing returns nothing
local List2 node = List2(CustomAttack.triggerAttack).first
loop
exitwhen node == 0
call TriggerEvaluate(node.data.trigger)
set node = node.next
endloop
endmethod
private static method onAttackMissileDestroy takes nothing returns nothing
if not IsEventAttackMelee() then
call List2(CustomAttack.triggerAttack).destroy()
endif
endmethod
private static method onIndex takes nothing returns nothing
call List1(GetIndexedUnitId()).clear()
endmethod
private static method onDeindex takes nothing returns nothing
local List1 node = List1(GetIndexedUnitId()).first
local List1 nextNode
loop
exitwhen node == 0
set nextNode = node.next
call node.data.unregisterUnit(GetIndexedUnit())
set node = nextNode
endloop
call List1(GetIndexedUnitId()).destroy()
endmethod
private static method init takes nothing returns nothing
local code onIndex = function thistype.onIndex
local code onDeindex = function thistype.onDeindex
call OnUnitIndex(onIndex)
call OnUnitDeindex(onDeindex)
call RegisterAttackEventHandler(EVENT_ATTACK_FINISH, function thistype.onMeleeAttackLaunch)
call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_LAUNCH, function thistype.onAttackMissileLaunch)
call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_IMPACT, function thistype.onAttackMissileImpact)
call RegisterAttackEventHandler(EVENT_ATTACK_DESTROY, function thistype.onAttackMissileDestroy)
set table = TableArray[JASS_MAX_ARRAY_SIZE - 1]
set handlerTable = TableArray[JASS_MAX_ARRAY_SIZE - 1]
endmethod
implement Init
endstruct
endlibrary
library AttackMissileUtils initializer Init /*
*/uses /*
*/AttackSystem /* https://www.hiveworkshop.com/threads/307417/
*/UnitDex /* https://www.hiveworkshop.com/threads/248209/
*///! novjass
|-------------|
| Description |
|-------------|
/*
This library provides utility functions for Attack missile enumerations.
*/
|-----|
| API |
|-----|
/*
*/Attack array ENUMED_ATTACK_MISSILES/*
- This array variable stores all the enumerated Attack missiles after calling one of the functions below
- The stored values are automatically cleared before the enumeration when calling the enumerator functions
- Acts as a circular singly-linked list
- How to traverse the list:
local Attack node
call EnumAttackMissilesInRange(0.00, 0.00, 100.00)
set node = ENUMED_ATTACK_MISSILES[0] //refers to the first element in the list
loop
exitwhen node == 0
...
set node = ENUMED_ATTACK_MISSILES[node] //returns 0 if 'node' is the last element
endloop
*/function EnumAttackMissilesAll takes nothing returns nothing/*
*/function EnumAttackMissilesInRange takes real x, real y, real radius returns nothing/*
*/function EnumAttackMissilesInSphere takes real x, real y, real z, real radius returns nothing/*
*/function EnumAttackMissilesInRectangle takes real minX, real minY, real maxX, real maxY returns nothing/*
*/function EnumAttackMissilesInCube takes real minX, real minY, real minZ, real maxX, real maxY, real maxZ returns nothing/*
*/function EnumAttackMissilesInCylinder takes real x, real y, real radius, real minZ, real maxZ returns nothing/*
*/function EnumAttackMissilesOfUnitAll takes unit source returns nothing/*
*/function EnumAttackMissilesOfUnitInRange takes unit source, real x, real y, real radius returns nothing/*
*/function EnumAttackMissilesOfUnitInSphere takes unit source, real x, real y, real z, real radius returns nothing/*
*/function EnumAttackMissilesOfUnitInRectangle takes unit source, real minX, real minY, real maxX, real maxY returns nothing/*
*/function EnumAttackMissilesOfUnitInCube takes unit source, real minX, real minY, real minZ, real maxX, real maxY, real maxZ returns nothing/*
*/function EnumAttackMissilesOfUnitInCylinder takes unit source, real x, real y, real radius, real minZ, real maxZ returns nothing/*
*///! endnovjass
globals
Attack array ENUMED_ATTACK_MISSILES
private integer array prev
private integer array next
private integer array unitPrev
private integer array unitNext
private integer array unitFirst
private integer array unitLast
endglobals
private function ClearPrevEnumed takes nothing returns nothing
local integer node = ENUMED_ATTACK_MISSILES[0]
local integer temp
loop
exitwhen node == 0
set temp = ENUMED_ATTACK_MISSILES[node]
set ENUMED_ATTACK_MISSILES[node] = 0
set node = temp
endloop
set ENUMED_ATTACK_MISSILES[0] = 0
endfunction
private function EnumAttackMissiles takes real x, real y, real z, real radius, boolean spherical returns nothing
local Attack node = next[0]
local integer temp = 0
local real dx
local real dy
local real dz
call ClearPrevEnumed()
loop
exitwhen node == 0
set dx = node.x - x
set dy = node.y - y
if spherical then
set dz = node.z - z
else
set dz = 0.00
endif
if dx*dx + dy*dy + dz*dz <= radius*radius then
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
endif
set node = next[node]
endloop
endfunction
function EnumAttackMissilesInRange takes real x, real y, real radius returns nothing
call EnumAttackMissiles(x, y, 0.00, radius, false)
endfunction
function EnumAttackMissilesInSphere takes real x, real y, real z, real radius returns nothing
call EnumAttackMissiles(x, y, z, radius, true)
endfunction
function EnumAttackMissilesInRectangle takes real minX, real minY, real maxX, real maxY returns nothing
local Attack node = next[0]
local integer temp = 0
call ClearPrevEnumed()
loop
exitwhen node == 0
if node.x >= minX and node.x <= maxX and node.y >= minY and node.y <= maxY then
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
endif
set node = next[node]
endloop
endfunction
function EnumAttackMissilesInCube takes real minX, real minY, real minZ, real maxX, real maxY, real maxZ returns nothing
local Attack node = next[0]
local integer temp = 0
call ClearPrevEnumed()
loop
exitwhen node == 0
if node.x >= minX and node.x <= maxX and node.y >= minY and node.y <= maxY and node.z >= minZ and node.z <= maxZ then
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
endif
set node = next[node]
endloop
endfunction
function EnumAttackMissilesInCylinder takes real x, real y, real radius, real minZ, real maxZ returns nothing
local Attack node = next[0]
local integer temp = 0
local real dx
local real dy
call ClearPrevEnumed()
loop
exitwhen node == 0
if node.z >= minZ and node.z <= maxZ then
set dx = node.x - x
set dy = node.y - y
if dx*dx + dy*dy <= radius*radius then
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
endif
endif
set node = next[node]
endloop
endfunction
function EnumAttackMissilesAll takes nothing returns nothing
local Attack node = next[0]
local integer temp = 0
call ClearPrevEnumed()
loop
exitwhen node == 0
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
set node = next[node]
endloop
endfunction
private function EnumAttackMissilesOfUnit takes unit source, real x, real y, real z, real radius, boolean spherical returns nothing
local Attack node = unitFirst[GetUnitId(source)]
local integer temp = 0
local real dx
local real dy
local real dz
call ClearPrevEnumed()
loop
exitwhen node == 0
set dx = node.x - x
set dy = node.y - y
if spherical then
set dz = node.z - z
else
set dz = 0.00
endif
if dx*dx + dy*dy + dz*dz <= radius*radius then
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
endif
set node = unitNext[node]
endloop
endfunction
function EnumAttackMissilesOfUnitInRange takes unit source, real x, real y, real radius returns nothing
call EnumAttackMissilesOfUnit(source, x, y, 0.00, radius, false)
endfunction
function EnumAttackMissilesOfUnitInSphere takes unit source, real x, real y, real z, real radius returns nothing
call EnumAttackMissilesOfUnit(source, x, y, z, radius, true)
endfunction
function EnumAttackMissilesOfUnitInRectangle takes unit source, real minX, real minY, real maxX, real maxY returns nothing
local Attack node = unitFirst[GetUnitId(source)]
local integer temp = 0
call ClearPrevEnumed()
loop
exitwhen node == 0
if node.x >= minX and node.x <= maxX and node.y >= minY and node.y <= maxY then
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
endif
set node = unitNext[node]
endloop
endfunction
function EnumAttackMissilesOfUnitInCube takes unit source, real minX, real minY, real minZ, real maxX, real maxY, real maxZ returns nothing
local Attack node = unitFirst[GetUnitId(source)]
local integer temp = 0
call ClearPrevEnumed()
loop
exitwhen node == 0
if node.x >= minX and node.x <= maxX and node.y >= minY and node.y <= maxY and node.z >= minZ and node.z <= maxZ then
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
endif
set node = unitNext[node]
endloop
endfunction
function EnumAttackMissilesOfUnitInCylinder takes unit source, real x, real y, real radius, real minZ, real maxZ returns nothing
local Attack node = unitFirst[GetUnitId(source)]
local integer temp = 0
local real dx
local real dy
call ClearPrevEnumed()
loop
exitwhen node == 0
if node.z >= minZ and node.z <= maxZ then
set dx = node.x - x
set dy = node.y - y
if dx*dx + dy*dy <= radius*radius then
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
endif
endif
set node = unitNext[node]
endloop
endfunction
function EnumAttackMissilesOfUnitAll takes unit source returns nothing
local Attack node = unitFirst[GetUnitId(source)]
local integer temp = 0
call ClearPrevEnumed()
loop
exitwhen node == 0
set ENUMED_ATTACK_MISSILES[temp] = node
set temp = node
set node = unitNext[node]
endloop
endfunction
//======================================================================================
private function UnitRemoveNode takes integer unitId, integer node returns nothing
if unitPrev[node] == 0 then
set unitFirst[unitId] = unitNext[node]
else
set unitNext[unitPrev[unitId]] = unitNext[node]
endif
if unitNext[node] == 0 then
set unitLast[unitId] = unitPrev[node]
else
set unitPrev[unitNext[unitId]] = unitPrev[node]
endif
endfunction
private function OnMissileLaunch takes nothing returns nothing
local integer node = CustomAttack.triggerAttack
local integer unitId = GetUnitId(GetEventAttackSource())
// Enqueue into the global list
set next[node] = 0
set prev[node] = prev[0]
set next[prev[0]] = node
set prev[0] = node
// Enqueue into the unit-specific list
if unitFirst[unitId] == 0 then
set unitFirst[unitId] = node
else
set unitNext[unitLast[unitId]] = node
endif
set unitNext[node] = 0
set unitPrev[node] = unitLast[unitId]
set unitLast[unitId] = node
endfunction
private function OnMissileDestroy takes nothing returns nothing
local integer node = CustomAttack.triggerAttack
local integer unitId
if not IsEventAttackMelee() then
set unitId = GetUnitId(GetEventAttackSource())
// Remove from the global list
set next[prev[node]] = next[node]
set prev[next[node]] = prev[node]
// Remove from the unit-specific list
call UnitRemoveNode(GetUnitId(GetEventAttackSource()), node)
endif
endfunction
private function OnDeindex takes nothing returns nothing
local integer node = unitFirst[GetIndexedUnitId()]
loop
exitwhen node == 0
// Remove from the unit-specific list
call UnitRemoveNode(GetIndexedUnitId(), node)
set node = unitNext[node]
endloop
endfunction
private function Init takes nothing returns nothing
local code onDeindex = function OnDeindex
call OnUnitDeindex(onDeindex)
call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_LAUNCH, function OnMissileLaunch)
call RegisterAttackEventHandler(EVENT_ATTACK_DESTROY, function OnMissileDestroy)
endfunction
endlibrary
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
* v1.2.1, by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex assigns every unit an unique integer. It attempts to make that number within the
* maximum array limit so you can associate it with one.
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the script to your map, save it, then restart the editor and comment out the line below.
*/
// external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/* ________________________________________________________________________
* 2. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
private module UnitDexConfig
// The raw code of the leave detection ability.
static constant integer DETECT_LEAVE_ABILITY = 'uDex'
// Allow debug messages (debug mode must also be on)
static constant boolean ALLOW_DEBUGGING = true
// Uncomment the lines below to define a filter for units being indexed
/*static method onFilter takes unit u returns boolean
return true
endmethod*/
endmodule
/* _________________________________________________________________________
* 3. Function API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Every function inlines except for UnitDexRemove
*
* function GetUnitId takes unit whichUnit returns integer
* function GetUnitById takes integer index returns unit
*
* function UnitDexEnable takes boolean flag returns nothing
* function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
* function IsUnitIndexed takes unit u returns boolean
* function IsIndexingEnabled takes nothing returns boolean
*
* function GetIndexedUnit takes nothing returns unit
* function GetIndexedUnitId takes nothing returns integer
*
* function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
* function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
* function OnUnitIndex takes code func returns triggercondition
* function OnUnitDeidex takes code func returns triggercondition
* _________________________________________________________________________
* 4. Struct API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex.Enabled = false // toggle the indexer
* UnitDex.Initialized // returns true if the preload timer has finished
* UnitDex.Count // returns the amount of units indexed
* UnitDex.Unit[0] // access the UnitDex array directly
* UnitDex.Group // a unit group containing every indexed unit (for enumeration)
* UnitDex.LastIndex // returns the last indexed unit's id
* _________________________________________________________________________
* 5. Public Variables
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* These are to be used with the "eventtype" argument of the event API:
*
* constant integer EVENT_UNIT_INDEX = 0
* constant integer EVENT_UNIT_DEINDEX = 1
* _________________________________________________________________________
* 6. Examples
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Associate a unit with a variable
*
* set UnitFlag[GetUnitId(yourUnit)] = true
*
* 2. Allocate a struct instance using a units assigned ID
*
* local somestruct data = GetUnitId(yourUnit)
*
* 3. Detect when a unit leaves the map
*
* function Exit takes nothing returns nothing
* call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
* endfunction
*
* call OnUnitDeindex(function Exit)
* // or
* call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
* _________________________________________________________________________
* 7. How it works
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Enumerate all preplaced units
* 2. TriggerRegisterEnterRegion native to detect when a unit enters the map
* 3. Assign each unit that enters the map a unique integer
* 4. Give every unit an ability based off of defend. There is no native to accurately
* detect when a unit leaves the map but when a unit dies or is removed from the game
* they are issued the "undefend" order
* 5. Catch the "undefend" order and remove unit from the queue
* _________________________________________________________________________
* 8. Notes
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
* - The object merger line should be commented out after saving and restarting.
* - All public functions are inlined except UnitDexRemove.
*
* -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
// Event types
constant integer EVENT_UNIT_INDEX = 0
constant integer EVENT_UNIT_DEINDEX = 1
// System variables
private trigger array IndexTrig
private integer Index = 0
private real E=-1
private boolexpr FilterEnter
endglobals
function GetUnitId takes unit whichUnit returns integer
return GetUnitUserData(whichUnit)
endfunction
function GetUnitById takes integer index returns unit
return UnitDex.Unit[index]
endfunction
function GetIndexedUnit takes nothing returns unit
return UnitDex.Unit[UnitDex.LastIndex]
endfunction
function GetIndexedUnitId takes nothing returns integer
return UnitDex.LastIndex
endfunction
function IsUnitIndexed takes unit u returns boolean
return (GetUnitById(GetUnitId(u)) != null)
endfunction
function UnitDexEnable takes boolean flag returns nothing
set UnitDex.Enabled = flag
endfunction
function IsIndexingEnabled takes nothing returns boolean
return UnitDex.Enabled
endfunction
function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
return TriggerAddCondition(IndexTrig[eventtype], func)
endfunction
function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
call TriggerRemoveCondition(IndexTrig[eventtype], c)
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
endfunction
function OnUnitIndex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
endfunction
function OnUnitDeindex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
endfunction
function UnitDexRemove takes unit u, boolean runEvents returns boolean
return UnitDex.Remove(u, runEvents)
endfunction
/****************************************************************/
private keyword UnitDexCore
struct UnitDex extends array
static boolean Enabled = true
readonly static integer LastIndex
readonly static boolean Initialized=false
readonly static group Group=CreateGroup()
readonly static unit array Unit
readonly static integer Count = 0
readonly static integer array List
implement UnitDexConfig
private static integer Counter = 0
implement UnitDexCore
endstruct
/****************************************************************/
private module UnitDexCore
static method Remove takes unit u, boolean runEvents returns boolean
local integer i
if (IsUnitIndexed(u)) then
set i = GetUnitId(u)
set UnitDex.List[i] = Index
set Index = i
call GroupRemoveUnit(UnitDex.Group, u)
call SetUnitUserData(u, 0)
if (runEvents) then
set UnitDex.LastIndex = i
set E = EVENT_UNIT_DEINDEX
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
set E = -1
endif
set UnitDex.Unit[i] = null
set UnitDex.Count = UnitDex.Count - 1
return true
endif
return false
endmethod
private static method onGameStart takes nothing returns nothing
local integer i = 0
static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
local group ENUM_GROUP = CreateGroup() // If not, create the group.
endif
// Index preplaced units
loop
call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
static if (not LIBRARY_GroupUtils) then
call DestroyGroup(ENUM_GROUP)
set ENUM_GROUP = null
endif
// run init triggers
set i = 1
loop
exitwhen i > Counter
set LastIndex = i
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
set E = EVENT_UNIT_INDEX
set E = -1
set i = i + 1
endloop
set LastIndex = Counter
set Initialized = true
set FilterEnter = null
call DestroyTimer(GetExpiredTimer())
endmethod
private static method onEnter takes nothing returns boolean
local unit u = GetFilterUnit()
local integer i = GetUnitId(u)
local integer t = Index
if (i == 0 and thistype.Enabled) then
// If a filter was defined pass the unit through it.
static if (thistype.onFilter.exists) then
if (not thistype.onFilter(u)) then
set u = null
return false // check failed
endif
endif
// Handle debugging
static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
set u = null
return false
endif
endif
// Add to group of indexed units
call GroupAddUnit(thistype.Group, u)
// Give unit the leave detection ability
call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
// Allocate index
if (Index != 0) then
set Index = List[t]
else
set Counter = Counter + 1
set t = Counter
endif
set List[t] = -1
set LastIndex = t
set thistype.Unit[t] = u
set Count = Count + 1
// Store the index
call SetUnitUserData(u, t)
if (thistype.Initialized) then
// Execute custom events registered with RegisterUnitIndexEvent
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_INDEX
// Reset so the event can occur again
set E = -1
endif
endif
set u = null
return false
endmethod
private static method onLeave takes nothing returns boolean
local unit u
local integer i
// Check if order is undefend.
if (thistype.Enabled and GetIssuedOrderId() == 852056) then
set u = GetTriggerUnit()
// If unit was killed (not removed) then don't continue
if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
set u = null
return false
endif
set i = GetUnitId(u)
// If unit has been indexed then deindex it
if (i > 0 and i <= Counter and u == GetUnitById(i)) then
// Recycle the index
set List[i] = Index
set Index = i
set LastIndex = i
// Remove to group of indexed units
call GroupRemoveUnit(thistype.Group, u)
// Execute custom events without any associated triggers
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_DEINDEX
// Remove entry
call SetUnitUserData(u, 0)
set thistype.Unit[i] = null
// Decrement unit count
set Count = Count - 1
// Reset so the event can occur again
set E = -1
endif
set u = null
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local player p
local unit u
static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
local rect world = GetWorldBounds()
endif
set FilterEnter = Filter(function thistype.onEnter)
// Begin to index units when they enter the map
static if (LIBRARY_WorldBounds) then
call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
else
call RegionAddRect(reg, world)
call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
call RemoveRect(world)
set world = null
endif
call TriggerAddCondition(t, Filter(function thistype.onLeave))
set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
loop
set p = Player(i)
// Detect "undefend"
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
// Hide the detect ability from players
call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TimerStart(CreateTimer(), 0, false, function thistype.onGameStart)
endmethod
endmodule
endlibrary
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
/*****************************************************************************
*
* GetClosestWidget v3.0.1.3
* by Bannar aka Spinnaker
*
* Allows finding closest widget with ease.
*
******************************************************************************
*
* Configurables:
*
* Choose which modules should or should not be implemented.
*
* constant boolean UNITS_MODULE
* constant boolean GROUP_MODULE
* constant boolean ITEMS_MODULE
* constant boolean DESTS_MODULE
*
* Define start and final distances for search iterations within generic GetClosest functions.
* If final value is reached, enumeration is performed on whole map.
*
* constant real START_DISTANCE
* constant real FINAL_DISTANCE
*
******************************************************************************
*
* Functions:
*
* Units:
* | function GetClosestUnit takes real x, real y, boolexpr filter returns unit
* | returns unit closest to coords(x, y)
* |
* | function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
* | returns unit closest to coords(x, y) within range radius
* |
* | function GetClosestUnitInGroup takes real x, real y, group g returns unit
* | returns unit closest to coords(x, y) within group g
*
*
* Group:
* | function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group dest, boolexpr filter returns nothing
* | adds to group dest up to N units, closest to coords(x, y) within range radius
* |
* | function GetClosestNUnitsInGroup takes real x, real y, integer n, group source, group dest returns nothing
* | adds to group dest up to N units, closest to coords(x, y) within group source
*
*
* Items:
* | function GetClosestItem takes real x, real y, boolexpr filter returns item
* | returns item closest to coords(x, y)
* |
* | function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
* | returns item closest to coords(x, y) within range radius
*
*
* Destructables:
* | function GetClosestDestructable takes real x, real y, boolexpr filter returns destructable
* | returns destructable closest to coords(x, y)
* |
* | function GetClosestDestructableInRange takes real x, real y, real radius, boolexpr filter returns destructable
* | returns destructable closest to coords(x, y) within range radius
*
*
*****************************************************************************/
library GetClosestWidget
globals
private constant boolean UNITS_MODULE = true
private constant boolean GROUP_MODULE = false
private constant boolean ITEMS_MODULE = false
private constant boolean DESTS_MODULE = false
private constant real START_DISTANCE = 800
private constant real FINAL_DISTANCE = 3200
endglobals
globals
private real distance
private real coordX
private real coordY
endglobals
private keyword GroupModule
private function calcDistance takes real x, real y returns real
local real dx = x - coordX
local real dy = y - coordY
return ( (dx*dx + dy*dy) / 10000 )
endfunction
private struct ClosestWidget extends array
static if UNITS_MODULE then
static unit unit
static group group = CreateGroup()
endif
static if GROUP_MODULE then
static if not UNITS_MODULE then
static group group = CreateGroup()
endif
static integer count = 0
static unit array sorted
static real array vector
implement GroupModule
endif
static if ITEMS_MODULE then
static item item
static rect area = Rect(0, 0, 0, 0)
endif
static if DESTS_MODULE then
static destructable destructable
static if not ITEMS_MODULE then
static rect area = Rect(0, 0, 0, 0)
endif
endif
endstruct
private function Defaults takes real x, real y returns nothing
static if UNITS_MODULE then
set ClosestWidget.unit = null
endif
static if ITEMS_MODULE then
set ClosestWidget.item = null
endif
static if DESTS_MODULE then
set ClosestWidget.destructable = null
endif
set distance = 100000
set coordX = x
set coordY = y
endfunction
static if UNITS_MODULE then
//! runtextmacro DEFINE_GCW_UNIT_MODULE()
endif
static if GROUP_MODULE then
//! runtextmacro DEFINE_GCW_GROUP_MODULE()
endif
static if ITEMS_MODULE then
//! runtextmacro DEFINE_GCW_MODULE("Item", "item")
endif
static if DESTS_MODULE then
//! runtextmacro DEFINE_GCW_MODULE("Destructable", "destructable")
endif
//! textmacro DEFINE_GCW_UNIT_MODULE
private function doEnumUnits takes unit u returns nothing
local real dist = calcDistance(GetUnitX(u), GetUnitY(u))
if ( dist < distance ) then
set ClosestWidget.unit = u
set distance = dist
endif
endfunction
private function enumUnits takes nothing returns nothing
call doEnumUnits(GetEnumUnit())
endfunction
function GetClosestUnit takes real x, real y, boolexpr filter returns unit
local real r = START_DISTANCE
local unit u
call Defaults(x, y)
loop
if ( r > FINAL_DISTANCE ) then
call GroupEnumUnitsInRect(ClosestWidget.group, GetWorldBounds(), filter)
exitwhen true
else
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, r, filter)
exitwhen FirstOfGroup(ClosestWidget.group) != null
endif
set r = 2*r
endloop
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call doEnumUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
return ClosestWidget.unit
endfunction
function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
local unit u
call Defaults(x, y)
if ( radius >= 0 ) then
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, radius, filter)
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call doEnumUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
endif
return ClosestWidget.unit
endfunction
function GetClosestUnitInGroup takes real x, real y, group g returns unit
call Defaults(x, y)
call ForGroup(g, function enumUnits)
return ClosestWidget.unit
endfunction
//! endtextmacro
//! textmacro DEFINE_GCW_GROUP_MODULE
private module GroupModule
static method doSaveUnits takes unit u returns nothing
set count = count + 1
set sorted[count] = u
set vector[count] = calcDistance(GetUnitX(u), GetUnitY(u))
endmethod
static method saveUnits takes nothing returns nothing
call doSaveUnits(GetEnumUnit())
endmethod
static method sortUnits takes integer lo, integer hi returns nothing
local integer i = lo
local integer j = hi
local real pivot = vector[(lo+hi)/2]
loop
loop
exitwhen vector[i] >= pivot
set i = i + 1
endloop
loop
exitwhen vector[j] <= pivot
set j = j - 1
endloop
exitwhen i > j
set vector[0] = vector[i]
set vector[i] = vector[j]
set vector[j] = vector[0]
set sorted[0] = sorted[i]
set sorted[i] = sorted[j]
set sorted[j] = sorted[0]
set i = i + 1
set j = j - 1
endloop
if ( lo < j ) then
call sortUnits(lo, j)
endif
if ( hi > i ) then
call sortUnits(i, hi)
endif
endmethod
static method fillGroup takes integer n, group dest returns nothing
loop
exitwhen count <= 0 or sorted[count] == null
if ( count <= n ) then
call GroupAddUnit(dest, sorted[count])
endif
set sorted[count] = null
set count = count - 1
endloop
endmethod
endmodule
function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group dest, boolexpr filter returns nothing
local unit u
call Defaults(x, y)
if ( radius >= 0 )then
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, radius, filter)
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call ClosestWidget.doSaveUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
call ClosestWidget.sortUnits(1, ClosestWidget.count)
call ClosestWidget.fillGroup(n, dest)
endif
endfunction
function GetClosestNUnitsInGroup takes real x, real y, integer n, group source, group dest returns nothing
local integer i = 0
call Defaults(x, y)
call ForGroup(source, function ClosestWidget.saveUnits)
call ClosestWidget.sortUnits(1, ClosestWidget.count)
call ClosestWidget.fillGroup(n, dest)
endfunction
//! endtextmacro
//! textmacro DEFINE_GCW_MODULE takes NAME, TYPE
private function enum$NAME$s takes nothing returns nothing
local $TYPE$ temp = GetEnum$NAME$()
local real dist = calcDistance(Get$NAME$X(temp), Get$NAME$Y(temp))
if ( dist < distance ) then
set ClosestWidget.$TYPE$ = temp
set distance = dist
endif
set temp = null
endfunction
function GetClosest$NAME$ takes real x, real y, boolexpr filter returns $TYPE$
local real r = START_DISTANCE
call Defaults(x, y)
loop
if ( r > FINAL_DISTANCE ) then
call Enum$NAME$sInRect(GetWorldBounds(), filter, function enum$NAME$s)
exitwhen true
else
call SetRect(ClosestWidget.area, x-r, y-r, x+r, y+r)
call Enum$NAME$sInRect(ClosestWidget.area, filter, function enum$NAME$s)
exitwhen ClosestWidget.$TYPE$ != null
endif
set r = 2*r
endloop
return ClosestWidget.$TYPE$
endfunction
function GetClosest$NAME$InRange takes real x, real y, real radius, boolexpr filter returns $TYPE$
call Defaults(x, y)
if ( radius > 0 ) then
call SetRect(ClosestWidget.area, x-radius, y-radius, x+radius, y+radius)
call Enum$NAME$sInRect(ClosestWidget.area, filter, function enum$NAME$s)
endif
return ClosestWidget.$TYPE$
endfunction
//! endtextmacro
endlibrary
library ErrorMessage /* v1.0.1.4
*************************************************************************************
*
* Issue Compliant Error Messages
*
************************************************************************************
*
* debug function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
* - In the event of an error the game will be permanently paused
*
* debug function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
*
************************************************************************************/
static if DEBUG_MODE then
private struct Fields extends array
static constant string COLOR_RED = "|cffff0000"
static constant string COLOR_YELLOW = "|cffffff00"
static string lastError = null
endstruct
private function Pause takes nothing returns nothing
call PauseGame(true)
endfunction
private function ThrowMessage takes string libraryName, string functionName, string objectName, integer objectInstance, string description, string errorType, string color returns nothing
local string str
local string color_braces = "|cff66FF99"
local string orange = "|cffff6600"
set str = "->\n-> " + color_braces + "{|r " + "Library" + color_braces + "(" + orange + libraryName + color_braces + ")"
if (objectName != null) then
if (objectInstance > 0) then
set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + " (|rinstance = " + orange + I2S(objectInstance) + color_braces + ") )" + "|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
else
set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + ")|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
endif
else
set str = str + "|r." + "Function" + color_braces + "(" + orange + functionName + color_braces + ")"
endif
set str = str + color_braces + " }|r " + "has thrown an exception of type " + color_braces + "(" + color + errorType + color_braces + ")|r."
set Fields.lastError = str + "\n->\n" + "-> " + color + description + "|r\n->"
endfunction
function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
if (Fields.lastError != null) then
set objectInstance = 1/0
endif
if (expression) then
call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Error", Fields.COLOR_RED)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
call TimerStart(CreateTimer(), 0, true, function Pause)
set objectInstance = 1/0
endif
endfunction
function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
if (Fields.lastError != null) then
set objectInstance = 1/0
endif
if (expression) then
call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Warning", Fields.COLOR_YELLOW)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
set Fields.lastError = null
endif
endfunction
endif
endlibrary
/*****************************************************************************
*
* RegisterNativeEvent v1.1.1.3
* by Bannar
*
* Storage of trigger handles for native events.
*
******************************************************************************
*
* Optional requirements:
*
* Table by Bribe
* hiveworkshop.com/threads/snippet-new-table.188084/
*
******************************************************************************
*
* Important:
*
* Avoid using TriggerSleepAction within functions registered.
* Destroy native event trigger on your own responsibility.
*
******************************************************************************
*
* Core:
*
* function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
* Whether index whichIndex has already been attached to event whichEvent.
*
* function RegisterNativeEventTrigger takes integer whichIndex, integer eventId returns boolean
* Registers whichIndex within whichEvent scope and assigns new trigger handle for it.
*
* function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent specific to provided index whichIndex.
*
* function GetNativeEventTrigger takes integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent.
*
*
* Custom events:
*
* function CreateNativeEvent takes nothing returns integer
* Returns unique id for new event and registers it with RegisterNativeEvent.
*
* function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
* Registers new event handler func for event whichEvent specific to index whichIndex.
*
* function RegisterNativeEvent takes integer whichEvent, code func returns nothing
* Registers new event handler func for specified event whichEvent.
*
*****************************************************************************/
library RegisterNativeEvent uses optional Table
globals
private integer eventIndex = 500 // 0-499 reserved for Blizzard native events
endglobals
private module NativeEventInit
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set table = TableArray[0x2000]
endif
endmethod
endmodule
private struct NativeEvent extends array
static if LIBRARY_Table then
static TableArray table
else
static hashtable table = InitHashtable()
endif
implement NativeEventInit
endstruct
function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger.has(whichIndex)
else
return HaveSavedHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function RegisterNativeEventTrigger takes integer whichIndex, integer whichEvent returns boolean
if not IsNativeEventRegistered(whichIndex, whichEvent) then
static if LIBRARY_Table then
set NativeEvent.table[whichEvent].trigger[whichIndex] = CreateTrigger()
else
call SaveTriggerHandle(NativeEvent.table, whichEvent, whichIndex, CreateTrigger())
endif
return true
endif
return false
endfunction
function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger[whichIndex]
else
return LoadTriggerHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function GetNativeEventTrigger takes integer whichEvent returns trigger
return GetIndexNativeEventTrigger(bj_MAX_PLAYER_SLOTS, whichEvent)
endfunction
function CreateNativeEvent takes nothing returns integer
local integer eventId = eventIndex
call RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId)
set eventIndex = eventIndex + 1
return eventId
endfunction
function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
call RegisterNativeEventTrigger(whichIndex, whichEvent)
call TriggerAddCondition(GetIndexNativeEventTrigger(whichIndex, whichEvent), Condition(func))
endfunction
function RegisterNativeEvent takes integer whichEvent, code func returns nothing
call RegisterIndexNativeEvent(bj_MAX_PLAYER_SLOTS, whichEvent, func)
endfunction
endlibrary
/*****************************************************************************
*
* RegisterPlayerUnitEvent v1.0.3.2
* by Bannar
*
* Register version of TriggerRegisterPlayerUnitEvent.
*
* Special thanks to Magtheridon96, Bribe, azlier and BBQ for the original library version.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent.
*
* function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
* Registers generic playerunitevent whichEvent adding code func as callback.
*
* function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
* Registers playerunitevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerUnitEvent requires RegisterNativeEvent
function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), whichEvent, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerUnitEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent, null)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
library OrderIds //v1.0 hiveworkshop.com/threads/orders-repo.290882/
globals
constant integer ORDER_wandillusion=852274
constant integer ORDER_absorb=852529
constant integer ORDER_acidbomb=852662
constant integer ORDER_acolyteharvest=852185
constant integer ORDER_AImove=851988
constant integer ORDER_ambush=852131
constant integer ORDER_ancestralspirit=852490
constant integer ORDER_ancestralspirittarget=852491
constant integer ORDER_animatedead=852217
constant integer ORDER_antimagicshell=852186
constant integer ORDER_attack=851983
constant integer ORDER_attackground=851984
constant integer ORDER_attackonce=851985
constant integer ORDER_attributemodskill=852576
constant integer ORDER_auraunholy=852215
constant integer ORDER_auravampiric=852216
constant integer ORDER_autodispel=852132
constant integer ORDER_autodispeloff=852134
constant integer ORDER_autodispelon=852133
constant integer ORDER_autoentangle=852505
constant integer ORDER_autoentangleinstant=852506
constant integer ORDER_autoharvestgold=852021
constant integer ORDER_autoharvestlumber=852022
constant integer ORDER_avatar=852086
constant integer ORDER_avengerform=852531
constant integer ORDER_awaken=852466
constant integer ORDER_banish=852486
constant integer ORDER_barkskin=852135
constant integer ORDER_barkskinoff=852137
constant integer ORDER_barkskinon=852136
constant integer ORDER_battleroar=852099
constant integer ORDER_battlestations=852099
constant integer ORDER_bearform=852138
constant integer ORDER_berserk=852100
constant integer ORDER_blackarrow=852577
constant integer ORDER_blackarrowoff=852579
constant integer ORDER_blackarrowon=852578
constant integer ORDER_blight=852187
constant integer ORDER_blink=852525
constant integer ORDER_blizzard=852089
constant integer ORDER_bloodlust=852101
constant integer ORDER_bloodlustoff=852103
constant integer ORDER_bloodluston=852102
constant integer ORDER_board=852043
constant integer ORDER_breathoffire=852580
constant integer ORDER_breathoffrost=852560
constant integer ORDER_build=851994
constant integer ORDER_burrow=852533
constant integer ORDER_cannibalize=852188
constant integer ORDER_carrionscarabs=852551
constant integer ORDER_carrionscarabsinstant=852554
constant integer ORDER_carrionscarabsoff=852553
constant integer ORDER_carrionscarabson=852552
constant integer ORDER_carrionswarm=852218
constant integer ORDER_chainlightning=852119
constant integer ORDER_channel=852600
constant integer ORDER_charm=852581
constant integer ORDER_chemicalrage=852663
constant integer ORDER_cloudoffog=852473
constant integer ORDER_clusterrockets=852652
constant integer ORDER_coldarrows=852244
constant integer ORDER_coldarrowstarg=852243
constant integer ORDER_controlmagic=852474
constant integer ORDER_corporealform=852493
constant integer ORDER_corrosivebreath=852140
constant integer ORDER_coupleinstant=852508
constant integer ORDER_coupletarget=852507
constant integer ORDER_creepanimatedead=852246
constant integer ORDER_creepdevour=852247
constant integer ORDER_creepheal=852248
constant integer ORDER_creephealoff=852250
constant integer ORDER_creephealon=852249
constant integer ORDER_creepthunderbolt=852252
constant integer ORDER_creepthunderclap=852253
constant integer ORDER_cripple=852189
constant integer ORDER_curse=852190
constant integer ORDER_curseoff=852192
constant integer ORDER_curseon=852191
constant integer ORDER_cyclone=852144
constant integer ORDER_darkconversion=852228
constant integer ORDER_darkportal=852229
constant integer ORDER_darkritual=852219
constant integer ORDER_darksummoning=852220
constant integer ORDER_deathanddecay=852221
constant integer ORDER_deathcoil=852222
constant integer ORDER_deathpact=852223
constant integer ORDER_decouple=852509
constant integer ORDER_defend=852055
constant integer ORDER_detectaoe=852015
constant integer ORDER_detonate=852145
constant integer ORDER_devour=852104
constant integer ORDER_devourmagic=852536
constant integer ORDER_disassociate=852240
constant integer ORDER_disenchant=852495
constant integer ORDER_dismount=852470
constant integer ORDER_dispel=852057
constant integer ORDER_divineshield=852090
constant integer ORDER_doom=852583
constant integer ORDER_drain=852487
constant integer ORDER_dreadlordinferno=852224
constant integer ORDER_dropitem=852001
constant integer ORDER_drunkenhaze=852585
constant integer ORDER_earthquake=852121
constant integer ORDER_eattree=852146
constant integer ORDER_elementalfury=852586
constant integer ORDER_ensnare=852106
constant integer ORDER_ensnareoff=852108
constant integer ORDER_ensnareon=852107
constant integer ORDER_entangle=852147
constant integer ORDER_entangleinstant=852148
constant integer ORDER_entanglingroots=852171
constant integer ORDER_etherealform=852496
constant integer ORDER_evileye=852105
constant integer ORDER_faeriefire=852149
constant integer ORDER_faeriefireoff=852151
constant integer ORDER_faeriefireon=852150
constant integer ORDER_fanofknives=852526
constant integer ORDER_farsight=852122
constant integer ORDER_fingerofdeath=852230
constant integer ORDER_firebolt=852231
constant integer ORDER_flamestrike=852488
constant integer ORDER_flamingarrows=852174
constant integer ORDER_flamingarrowstarg=852173
constant integer ORDER_flamingattack=852540
constant integer ORDER_flamingattacktarg=852539
constant integer ORDER_flare=852060
constant integer ORDER_forceboard=852044
constant integer ORDER_forceofnature=852176
constant integer ORDER_forkedlightning=852586
constant integer ORDER_freezingbreath=852195
constant integer ORDER_frenzy=852561
constant integer ORDER_frenzyoff=852563
constant integer ORDER_frenzyon=852562
constant integer ORDER_frostarmor=852225
constant integer ORDER_frostarmoroff=852459
constant integer ORDER_frostarmoron=852458
constant integer ORDER_frostnova=852226
constant integer ORDER_getitem=851981
constant integer ORDER_gold2lumber=852233
constant integer ORDER_grabtree=852511
constant integer ORDER_harvest=852018
constant integer ORDER_heal=852063
constant integer ORDER_healingspray=852664
constant integer ORDER_healingward=852109
constant integer ORDER_healingwave=852501
constant integer ORDER_healoff=852065
constant integer ORDER_healon=852064
constant integer ORDER_hex=852502
constant integer ORDER_holdposition=851993
constant integer ORDER_holybolt=852092
constant integer ORDER_howlofterror=852588
constant integer ORDER_humanbuild=851995
constant integer ORDER_immolation=852177
constant integer ORDER_impale=852555
constant integer ORDER_incineratearrow=852670
constant integer ORDER_incineratearrowoff=852672
constant integer ORDER_incineratearrowon=852671
constant integer ORDER_inferno=852232
constant integer ORDER_innerfire=852066
constant integer ORDER_innerfireoff=852068
constant integer ORDER_innerfireon=852067
constant integer ORDER_instant=852200
constant integer ORDER_invisibility=852069
constant integer ORDER_lavamonster=852667
constant integer ORDER_lightningshield=852110
constant integer ORDER_load=852046
constant integer ORDER_loadarcher = 852142
constant integer ORDER_loadcorpse=852050
constant integer ORDER_loadcorpseinstant=852053
constant integer ORDER_locustswarm=852556
constant integer ORDER_lumber2gold=852234
constant integer ORDER_magicdefense=852478
constant integer ORDER_magicleash=852480
constant integer ORDER_magicundefense=852479
constant integer ORDER_manaburn=852179
constant integer ORDER_manaflareoff=852513
constant integer ORDER_manaflareon=852512
constant integer ORDER_manashieldoff=852590
constant integer ORDER_manashieldon=852589
constant integer ORDER_massteleport=852093
constant integer ORDER_mechanicalcritter=852564
constant integer ORDER_metamorphosis=852180
constant integer ORDER_militia=852072
constant integer ORDER_militiaconvert=852071
constant integer ORDER_militiaoff=852073
constant integer ORDER_militiaunconvert=852651
constant integer ORDER_mindrot=852565
constant integer ORDER_mirrorimage=852123
constant integer ORDER_monsoon=852591
constant integer ORDER_mount=852469
constant integer ORDER_mounthippogryph=852143
constant integer ORDER_move=851986
constant integer ORDER_nagabuild=852467
constant integer ORDER_neutraldetectaoe=852023
constant integer ORDER_neutralinteract=852566
constant integer ORDER_neutralspell=852630
constant integer ORDER_nightelfbuild=851997
constant integer ORDER_orcbuild=851996
constant integer ORDER_parasite=852601
constant integer ORDER_parasiteoff=852603
constant integer ORDER_parasiteon=852602
constant integer ORDER_patrol=851990
constant integer ORDER_phaseshift=852514
constant integer ORDER_phaseshiftinstant=852517
constant integer ORDER_phaseshiftoff=852516
constant integer ORDER_phaseshifton=852515
constant integer ORDER_phoenixfire=852481
constant integer ORDER_phoenixmorph=852482
constant integer ORDER_poisonarrows=852255
constant integer ORDER_poisonarrowstarg=852254
constant integer ORDER_polymorph=852074
constant integer ORDER_possession=852196
constant integer ORDER_preservation=852568
constant integer ORDER_purge=852111
constant integer ORDER_rainofchaos=852237
constant integer ORDER_rainoffire=852238
constant integer ORDER_raisedead=852197
constant integer ORDER_raisedeadoff=852199
constant integer ORDER_raisedeadon=852198
constant integer ORDER_ravenform=852155
constant integer ORDER_recharge=852157
constant integer ORDER_rechargeoff=852159
constant integer ORDER_rechargeon=852158
constant integer ORDER_rejuvination=852160
constant integer ORDER_renew=852161
constant integer ORDER_renewoff=852163
constant integer ORDER_renewon=852162
constant integer ORDER_repair=852024
constant integer ORDER_repairoff=852026
constant integer ORDER_repairon=852025
constant integer ORDER_replenish=852542
constant integer ORDER_replenishlife=852545
constant integer ORDER_replenishlifeoff=852547
constant integer ORDER_replenishlifeon=852546
constant integer ORDER_replenishmana=852548
constant integer ORDER_replenishmanaoff=852550
constant integer ORDER_replenishmanaon=852549
constant integer ORDER_replenishoff=852544
constant integer ORDER_replenishon=852543
constant integer ORDER_request_hero=852239
constant integer ORDER_requestsacrifice=852201
constant integer ORDER_restoration=852202
constant integer ORDER_restorationoff=852204
constant integer ORDER_restorationon=852203
constant integer ORDER_resumebuild=851999
constant integer ORDER_resumeharvesting=852017
constant integer ORDER_resurrection=852094
constant integer ORDER_returnresources=852020
constant integer ORDER_revenge=852241
constant integer ORDER_revive=852039
constant integer ORDER_roar=852164
constant integer ORDER_robogoblin=852656
constant integer ORDER_root=852165
constant integer ORDER_sacrifice=852205
constant integer ORDER_sanctuary=852569
constant integer ORDER_scout=852181
constant integer ORDER_selfdestruct=852040
constant integer ORDER_selfdestructoff=852042
constant integer ORDER_selfdestructon=852041
constant integer ORDER_sentinel=852182
constant integer ORDER_setrally=851980
constant integer ORDER_shadowsight=852570
constant integer ORDER_shadowstrike=852527
constant integer ORDER_shockwave=852125
constant integer ORDER_silence=852592
constant integer ORDER_sleep=852227
constant integer ORDER_slow=852075
constant integer ORDER_slowoff=852077
constant integer ORDER_slowon=852076
constant integer ORDER_smart=851971
constant integer ORDER_soulburn=852668
constant integer ORDER_soulpreservation=852242
constant integer ORDER_spellshield=852571
constant integer ORDER_spellshieldaoe=852572
constant integer ORDER_spellsteal=852483
constant integer ORDER_spellstealoff=852485
constant integer ORDER_spellstealon=852484
constant integer ORDER_spies=852235
constant integer ORDER_spiritlink=852499
constant integer ORDER_spiritofvengeance=852528
constant integer ORDER_spirittroll=852573
constant integer ORDER_spiritwolf=852126
constant integer ORDER_stampede=852593
constant integer ORDER_standdown=852113
constant integer ORDER_starfall=852183
constant integer ORDER_stasistrap=852114
constant integer ORDER_steal=852574
constant integer ORDER_stomp=852127
constant integer ORDER_stoneform=852206
constant integer ORDER_stop=851972
constant integer ORDER_submerge=852604
constant integer ORDER_summonfactory=852658
constant integer ORDER_summongrizzly=852594
constant integer ORDER_summonphoenix=852489
constant integer ORDER_summonquillbeast=852595
constant integer ORDER_summonwareagle=852596
constant integer ORDER_tankdroppilot=852079
constant integer ORDER_tankloadpilot=852080
constant integer ORDER_tankpilot=852081
constant integer ORDER_taunt=852520
constant integer ORDER_thunderbolt=852095
constant integer ORDER_thunderclap=852096
constant integer ORDER_tornado=852597
constant integer ORDER_townbelloff=852083
constant integer ORDER_townbellon=852082
constant integer ORDER_tranquility=852184
constant integer ORDER_transmute=852665
constant integer ORDER_unavatar=852087
constant integer ORDER_unavengerform=852532
constant integer ORDER_unbearform=852139
constant integer ORDER_unburrow=852534
constant integer ORDER_uncoldarrows=852245
constant integer ORDER_uncorporealform=852494
constant integer ORDER_undeadbuild=851998
constant integer ORDER_undefend=852056
constant integer ORDER_undivineshield=852091
constant integer ORDER_unetherealform=852497
constant integer ORDER_unflamingarrows=852175
constant integer ORDER_unflamingattack=852541
constant integer ORDER_unholyfrenzy=852209
constant integer ORDER_unimmolation=852178
constant integer ORDER_unload=852047
constant integer ORDER_unloadall=852048
constant integer ORDER_unloadallcorpses=852054
constant integer ORDER_unloadallinstant=852049
constant integer ORDER_unpoisonarrows=852256
constant integer ORDER_unravenform=852156
constant integer ORDER_unrobogoblin=852657
constant integer ORDER_unroot=852166
constant integer ORDER_unstableconcoction=852500
constant integer ORDER_unstoneform=852207
constant integer ORDER_unsubmerge=852605
constant integer ORDER_unsummon=852210
constant integer ORDER_unwindwalk=852130
constant integer ORDER_vengeance=852521
constant integer ORDER_vengeanceinstant=852524
constant integer ORDER_vengeanceoff=852523
constant integer ORDER_vengeanceon=852522
constant integer ORDER_volcano=852669
constant integer ORDER_voodoo=852503
constant integer ORDER_ward=852504
constant integer ORDER_waterelemental=852097
constant integer ORDER_wateryminion=852598
constant integer ORDER_web=852211
constant integer ORDER_weboff=852213
constant integer ORDER_webon=852212
constant integer ORDER_whirlwind=852128
constant integer ORDER_windwalk=852129
constant integer ORDER_wispharvest=852214
constant integer ORDER_scrollofspeed=852285
constant integer ORDER_cancel=851976
constant integer ORDER_moveslot1=852002
constant integer ORDER_moveslot2=852003
constant integer ORDER_moveslot3=852004
constant integer ORDER_moveslot4=852005
constant integer ORDER_moveslot5=852006
constant integer ORDER_moveslot6=852007
constant integer ORDER_useslot1=852008
constant integer ORDER_useslot2=852009
constant integer ORDER_useslot3=852010
constant integer ORDER_useslot4=852011
constant integer ORDER_useslot5=852012
constant integer ORDER_useslot6=852013
constant integer ORDER_skillmenu=852000
constant integer ORDER_stunned=851973
constant integer ORDER_instant1=851991 //patrol sub-order
constant integer ORDER_instant2=851987 //?
constant integer ORDER_instant3=851975 //?
constant integer ORDER_instant4=852019 //?
endglobals
endlibrary
library NxList /* v1.0.0.2
************************************************************************************
*
* */uses/*
*
* */ ErrorMessage /* hiveworkshop.com/forums/submissions-414/snippet-error-message-239210/
*
************************************************************************************
*
* module NxList
*
* Description
* -------------------------
*
* NA
*
* Fields
* -------------------------
*
* readonly static integer sentinel
*
* readonly thistype list
*
* readonly thistype first
* readonly thistype last
*
* readonly thistype next
* readonly thistype prev
*
* Methods
* -------------------------
*
* method destroy takes nothing returns nothing
* - May only destroy lists
*
* method push takes nothing returns thistype
* method enqueue takes nothing returns thistype
*
* method pop takes nothing returns nothing
* method dequeue takes nothing returns nothing
*
* method remove takes nothing returns nothing
*
* method clear takes nothing returns nothing
* - Initializes list, use instead of create
*
* debug static method calculateMemoryUsage takes nothing returns integer
* debug static method getAllocatedMemoryAsString takes nothing returns string
*
************************************************************************************/
module NxList
private static thistype nodeCount = 0
debug private boolean isNode
debug private boolean isCollection
private thistype _list
method operator list takes nothing returns thistype
debug call ThrowError(this == 0, "NxList", "list", "thistype", this, "Attempted To Read Null Node.")
debug call ThrowError(not isNode, "NxList", "list", "thistype", this, "Attempted To Read Invalid Node.")
return _list
endmethod
private thistype _next
method operator next takes nothing returns thistype
debug call ThrowError(this == 0, "NxList", "next", "thistype", this, "Attempted To Go Out Of Bounds.")
debug call ThrowError(not isNode, "NxList", "next", "thistype", this, "Attempted To Read Invalid Node.")
return _next
endmethod
private thistype _prev
method operator prev takes nothing returns thistype
debug call ThrowError(this == 0, "NxList", "prev", "thistype", this, "Attempted To Go Out Of Bounds.")
debug call ThrowError(not isNode, "NxList", "prev", "thistype", this, "Attempted To Read Invalid Node.")
return _prev
endmethod
private thistype _first
method operator first takes nothing returns thistype
debug call ThrowError(this == 0, "NxList", "first", "thistype", this, "Attempted To Read Null List.")
debug call ThrowError(not isCollection, "NxList", "first", "thistype", this, "Attempted To Read Invalid List.")
return _first
endmethod
private thistype _last
method operator last takes nothing returns thistype
debug call ThrowError(this == 0, "NxList", "last", "thistype", this, "Attempted To Read Null List.")
debug call ThrowError(not isCollection, "NxList", "last", "thistype", this, "Attempted To Read Invalid List.")
return _last
endmethod
static method operator sentinel takes nothing returns integer
return 0
endmethod
private static method allocateNode takes nothing returns thistype
local thistype this = thistype(0)._next
if (0 == this) then
debug call ThrowError(nodeCount == 8191, "NxList", "allocateNode", "thistype", 0, "Overflow.")
set this = nodeCount + 1
set nodeCount = this
else
set thistype(0)._next = _next
endif
return this
endmethod
method push takes nothing returns thistype
local thistype node = allocateNode()
debug call ThrowError(this == 0, "NxList", "push", "thistype", this, "Attempted To Push On To Null List.")
debug call ThrowError(not isCollection, "NxList", "push", "thistype", this, "Attempted To Push On To Invalid List.")
debug set node.isNode = true
set node._list = this
if (_first == 0) then
set _first = node
set _last = node
set node._next = 0
else
set _first._prev = node
set node._next = _first
set _first = node
endif
set node._prev = 0
return node
endmethod
method enqueue takes nothing returns thistype
local thistype node = allocateNode()
debug call ThrowError(this == 0, "NxList", "enqueue", "thistype", this, "Attempted To Enqueue On To Null List.")
debug call ThrowError(not isCollection, "NxList", "enqueue", "thistype", this, "Attempted To Enqueue On To Invalid List.")
debug set node.isNode = true
set node._list = this
if (_first == 0) then
set _first = node
set _last = node
set node._prev = 0
else
set _last._next = node
set node._prev = _last
set _last = node
endif
set node._next = 0
return node
endmethod
method pop takes nothing returns nothing
local thistype node = _first
debug call ThrowError(this == 0, "NxList", "pop", "thistype", this, "Attempted To Pop Null List.")
debug call ThrowError(not isCollection, "NxList", "pop", "thistype", this, "Attempted To Pop Invalid List.")
debug call ThrowError(node == 0, "NxList", "pop", "thistype", this, "Attempted To Pop Empty List.")
debug set node.isNode = false
set _first._list = 0
set _first = _first._next
if (_first == 0) then
set _last = 0
else
set _first._prev = 0
endif
set node._next = thistype(0)._next
set thistype(0)._next = node
endmethod
method dequeue takes nothing returns nothing
local thistype node = _last
debug call ThrowError(this == 0, "NxList", "dequeue", "thistype", this, "Attempted To Dequeue Null List.")
debug call ThrowError(not isCollection, "NxList", "dequeue", "thistype", this, "Attempted To Dequeue Invalid List.")
debug call ThrowError(node == 0, "NxList", "dequeue", "thistype", this, "Attempted To Dequeue Empty List.")
debug set node.isNode = false
set _last._list = 0
set _last = _last._prev
if (_last == 0) then
set _first = 0
else
set _last._next = 0
endif
set node._next = thistype(0)._next
set thistype(0)._next = node
endmethod
method remove takes nothing returns nothing
local thistype node = this
set this = node._list
debug call ThrowError(node == 0, "NxList", "remove", "thistype", this, "Attempted To Remove Null Node.")
debug call ThrowError(not node.isNode, "NxList", "remove", "thistype", this, "Attempted To Remove Invalid Node (" + I2S(node) + ").")
debug set node.isNode = false
set node._list = 0
if (0 == node._prev) then
set _first = node._next
else
set node._prev._next = node._next
endif
if (0 == node._next) then
set _last = node._prev
else
set node._next._prev = node._prev
endif
set node._next = thistype(0)._next
set thistype(0)._next = node
endmethod
method clear takes nothing returns nothing
debug local thistype node = _first
debug call ThrowError(this == 0, "NxList", "clear", "thistype", this, "Attempted To Clear Null List.")
debug if (not isCollection) then
debug set isCollection = true
debug set _first = 0
debug set _last = 0
debug return
debug endif
static if DEBUG_MODE then
loop
exitwhen node == 0
set node.isNode = false
set node = node._next
endloop
endif
if (_first == 0) then
return
endif
set _last._next = thistype(0)._next
set thistype(0)._next = _first
set _first = 0
set _last = 0
endmethod
method destroy takes nothing returns nothing
debug call ThrowError(this == 0, "NxList", "destroy", "thistype", this, "Attempted To Destroy Null List.")
debug call ThrowError(not isCollection, "NxList", "destroy", "thistype", this, "Attempted To Destroy Invalid List.")
call clear()
debug set isCollection = false
endmethod
static if DEBUG_MODE then
static method calculateMemoryUsage takes nothing returns integer
local thistype start = 1
local thistype end = 8191
local integer count = 0
loop
exitwhen integer(start) > integer(end)
if (integer(start) + 500 > integer(end)) then
return count + checkRegion(start, end)
else
set count = count + checkRegion(start, start + 500)
set start = start + 501
endif
endloop
return count
endmethod
private static method checkRegion takes thistype start, thistype end returns integer
local integer count = 0
loop
exitwhen integer(start) > integer(end)
if (start.isNode) then
set count = count + 1
endif
if (start.isCollection) then
set count = count + 1
endif
set start = start + 1
endloop
return count
endmethod
static method getAllocatedMemoryAsString takes nothing returns string
local thistype start = 1
local thistype end = 8191
local string memory = null
loop
exitwhen integer(start) > integer(end)
if (integer(start) + 500 > integer(end)) then
if (memory != null) then
set memory = memory + ", "
endif
set memory = memory + checkRegion2(start, end)
set start = end + 1
else
if (memory != null) then
set memory = memory + ", "
endif
set memory = memory + checkRegion2(start, start + 500)
set start = start + 501
endif
endloop
return memory
endmethod
private static method checkRegion2 takes thistype start, thistype end returns string
local string memory = null
loop
exitwhen integer(start) > integer(end)
if (start.isNode) then
if (memory == null) then
set memory = I2S(start)
else
set memory = memory + ", " + I2S(start) + "N"
endif
endif
if (start.isCollection) then
if (memory == null) then
set memory = I2S(start)
else
set memory = memory + ", " + I2S(start) + "C"
endif
endif
set start = start + 1
endloop
return memory
endmethod
endif
endmodule
endlibrary
library ImageStruct/*v1.0
*************************************************************************************
*
* ImageStruct enables struct syntax with images.
* Useful when you have advanced systems which relies on images.
*
************************************************************************************
*
* */ uses /*
*
* */ ImageTools /* [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
* */ optional ARGB /* [url]http://www.wc3c.net/showthread.php?t=101858[/url]
*
************************************************************************************
*
* struct Image extends array
*
* Creator/Destructor:
* static method create takes string path, real sX, real sY, real pX, real pY, real pZ, integer imageTypeIndex, boolean render returns thistype
* - Allocates a new Image instance and calls NewImage.
* - Automatically renders the image if "render" is true.
*
* method destroy takes nothing returns nothing
*
* Methods which automatically re-draw the image:
*
* method setSizeX takes real size returns nothing
* method setSizeY takes real size returns nothing
* method setFile takes string filepath returns nothing
* method setImageType takes integer index returns nothing
*
* Methods which automatically re-locate the image:
*
* method setPosX takes real x returns nothing
* method setPosY takes real y returns nothing
* method setPosZ takes real z returns nothing
* method setPosition takes real x, real y, real z returns nothing
* method wrap takes boolean flag returns nothing
* - binds the image to the ground no matter what posZ is set.
*
* Additional render options, automatically re-renders the image.
*
* method renderForAll takes nothing returns nothing
* - renders this image for all players.
*
* method renderForForce takes force whichForce returns nothing
* - renders this image for all players in force whichForce.
*
* method renderLocalPlayer takes player whichPlayer returns nothing
* - renders this image only for a local client ( whichPlayer ).
*
* Colorize images with ARGB:
* method setColor takes ARGB paint returns nothing
*
* Colorize images without ARGB:
* method setColor takes integer Red, integer Green, integer Blue, integer Alpha returns nothing
*
* Extra:
*
* method setPositionEx takes real x, real y, real z returns nothing
* - Here x, y, z define the center of the image, similar to
* the coordinates which you use in other handle type
* i.e. CreateUnit(p, id, x, y , 0)
*
* Readonly fields:
*
* readonly image img
* readonly string file
* readonly real sizeX
* readonly real sizeY
* readonly real posX
* readonly real posY
* readonly real posZ
* readonly boolean show
* readonly integer imageType
* readonly boolean wrapped
* readonly integer green
* readonly integer red
* readonly integer blue
* readonly integer alpha
*
* 3. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* If you are using library ARGB, you should define a default color.
*
*/
globals
constant integer DEFAULT_IMAGE_COLOR = 0xFFFFFFFF
endglobals
//! textmacro IMAGE_TOOLS_IMPORT_STRUCT_CODE
private keyword IMAGE_TOOLS_M
struct Image extends array
implement IMAGE_TOOLS_M
readonly image img
readonly string file
readonly real sizeX
readonly real sizeY
readonly real posX
readonly real posY
readonly real posZ
readonly boolean show
readonly integer imageType
readonly boolean wrapped
private force frc
private player user
private method paint takes nothing returns nothing
static if LIBRARY_ARGB then
call SetImageColor(img, red, green, blue, alpha)
else
if hasColor then
call SetImageColor(img, red, green, blue, alpha)
endif
endif
endmethod
static if LIBRARY_ARGB then
readonly ARGB tint
method operator red takes nothing returns integer
return tint.red
endmethod
method operator green takes nothing returns integer
return tint.green
endmethod
method operator blue takes nothing returns integer
return tint.blue
endmethod
method operator alpha takes nothing returns integer
return tint.alpha
endmethod
method operator color takes nothing returns ARGB
return tint
endmethod
method setColor takes ARGB cool returns nothing
set tint = cool
call paint()
endmethod
else
readonly integer green
readonly integer red
readonly integer blue
readonly integer alpha
private boolean hasColor
method setColor takes integer Red, integer Green, integer Blue, integer Alpha returns nothing
set alpha = A/* */lpha
set red = R/* */ed
set green = G/* */reen
set blue = B/* */lue
set hasColor = true
call paint()
endmethod
endif
private method draw takes nothing returns nothing
if (img != null) then
call ReleaseImage(img)
set img = null
endif
set img = NewImage(file, sizeX, sizeY, posX, posY, posZ, imageType)
call SetImageConstantHeight(img, not wrapped, posZ)
call paint()
if (user == null) and (frc == null) then
call SetImageRenderAlways(img, show)
elseif (user != null) then
call SetImageRenderAlways(img, (show) and (GetLocalPlayer() == user))
else
call SetImageRenderAlways(img, (show) and IsPlayerInForce(GetLocalPlayer(), frc))
endif
endmethod
method wrap takes boolean flag returns nothing
set wrapped = flag
call SetImageConstantHeight(img, not wrapped, posZ)
endmethod
method renderForAll takes nothing returns nothing
set user = null
set frc = null
call draw()
endmethod
method renderLocalPlayer takes player whichPlayer returns nothing
set user = whichPlayer
call draw()
endmethod
method renderForForce takes force whichForce returns nothing
set frc = whichForce
call draw()
endmethod
method setSizeX takes real value returns nothing
set sizeX = value
call draw()
endmethod
method setSizeY takes real value returns nothing
set sizeY = value
call draw()
endmethod
method setFile takes string filepath returns nothing
set file = filepath
call draw()
endmethod
method setPosX takes real amount returns nothing
set posX = amount
call SetImagePosition(img, amount, posY, posZ)
endmethod
method setPosY takes real amount returns nothing
set posY = amount
call SetImagePosition(img, posX, amount, posZ)
endmethod
method setPosZ takes real value returns nothing
set posZ = value
call SetImageConstantHeight(img, true, value)
endmethod
method setPositionEx takes real x, real y, real z returns nothing
set posX = x - sizeX*.5
set posY = y - sizeY*.5
set posZ = z
call SetImagePosition(img, posX, posY, posZ)
call SetImageConstantHeight(img, not wrapped, z)
endmethod
method setPosition takes real x, real y, real z returns nothing
set posX = x
set posY = y
set posZ = z
call SetImagePosition(img, posX, posY, posZ)
call SetImageConstantHeight(img, not wrapped, z)
endmethod
// Checks for errors in NewImage
method setImageType takes integer i returns nothing
set imageType = i
call SetImageType(img, i)
call draw()
endmethod
static method create takes string path, real sX, real sY, real pX, real pY, real pZ, integer imageTypeIndex, boolean render returns thistype
local thistype this = thistype.allocate()
set file = path
set show = render
set sizeX = sX
set sizeY = sY
set posX = pX
set posY = pY
set posZ = pZ
set wrapped = false
set imageType = imageTypeIndex
static if not LIBRARY_ARGB then
set hasColor = false
else
set tint = DEFAULT_IMAGE_COLOR
endif
call draw()
return this
endmethod
method destroy takes nothing returns nothing
if (img != null) then
call ReleaseImage(img)
set img = null
endif
set user = null
set frc = null
endmethod
endstruct
private module IMAGE_TOOLS_M
private static integer array recycler
static method allocate takes nothing returns thistype
local thistype this = recycler[0]
debug if (this == 8192) then
debug call DebugMsg("Overflow - Requires an allocation module above 8910!")
debug return 0
debug endif
if (0 == recycler[this]) then
set recycler[0] = this + 1
else
set recycler[0] = recycler[this]
endif
debug set recycler[this] = -1
return this
endmethod
method deallocate takes nothing returns nothing
debug if (recycler[this] != -1) then
debug call DebugMsg("Attempted To Deallocate Null Instance. ( Double Free )")
debug endif
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
private static method onInit takes nothing returns nothing
set recycler[0] = 1
endmethod
endmodule
//! endtextmacro
endlibrary
// Written by BPower.
library ImageTools /*v2.0
*************************************************************************************
*
* For your image needs.
*
* Strictly speaking it provides two wrapper function for CreateImage & DestroyImage,
* named function NewImage & function ReleaseImage.
*
* You always want to use these wrapper functions! Why so? See required know-how.
*
*************************************************************************************
*
* Required know-how [ Image extends handles for Dummies ]:
* -----------------
*
* 1. An invalid filepath crashes the game.
* 2. An invalid image type crashes the game.
* 3. DestroyImage native on an invalid image handle crashes the game.
*
* ImageTools prevents you from these fatal errors
* plus prints out debug messages, so you can quickly fix your code.
*
* Hint: A very nice image tutorial link - [url]http://www.wc3c.net/showthread.php?t=107737[/url]
*
*************************************************************************************
*
* To Deaod
* -----------------------
*
* For the original ImageUtils library ( [url]http://www.wc3c.net/showthread.php?t=107707[/url] )
*
* To Bribe
* -----------------------
*
* For Table
*
* To Vexorian
* -----------------------
*
* For ARGB
*
*************************************************************************************
*
* */ uses /*
*
* */ Table /* [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
*
************************************************************************************
*
* 1. Import instruction
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the ImageTools script and library Table into your map.
* 2. API
* ¯¯¯¯¯¯
* function NewImage takes string file, real sizeX, real sizeY, real posX, real posY, real posZ, integer imageType returns image
* - Wrapper to the CreateImage native.
* - Does not crash the game, if the created image handle is invalid.
*
* function ReleaseImage takes image whichImage retuns nothing
* - Wrapper to the DestroyImage native
* - Does not crash the game if whichImage is invalid.
* - Does not crash the game if whichImage is the first ever created image in the map.
*
* function CreateImageCenter takes string file, real sizeX, real sizeY, real centerX, real centerY, real posZ, integer imageType returns image
* - Wrapper to the NewImage. It creates the image centered at centerX, centerY equal to other handle objects ( i.e. units ).
*
* 3. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* You don't have to setup anything.
*/
globals
/**
* The following are valid image types
* sorted from highest to lowest layer:
*/
constant integer IMAGE_TYPE_SELECTION = 1// above all other image types.
constant integer IMAGE_TYPE_OCCLUSION_MASK = 3// above image type 2 and 4
constant integer IMAGE_TYPE_INDICATOR = 2// above image type 4
constant integer IMAGE_TYPE_UBERSPLAT = 4// lowest layer. Tinting affected by time of day
// and is drawn below fog of war.
/**
* Those two are image types options in GUI's "Image - Create" wrapper of CreateImageBJ.
* Both are invalid image types and should not be used in any case.
* While an image with IMAGE_TYPE_SHADOW instantly crashes the game,
* an image using IMAGE_TYPE_TOPMOST will simply not be render-able.
* The game season however will continue normally.
*/
constant integer IMAGE_TYPE_SHADOW = 0// Will create an invalid image handle with an handle id of -1.
constant integer IMAGE_TYPE_TOPMOST = 5// Will create an invalid image handle, which can't be rendered.
/**
* If an image handle is invalid, it gets the handle id -1.
* Once you are using this handle somewhere, Warcraft III crashes,
* hence ALWAYS create an image handle via function NewImage.
*/
private constant integer INVALID_IMAGE_ID = -1
/**
* When using ARGB, this is the default color set on Image.create()
*/
endglobals
static if DEBUG_MODE then
private function DebugMsg takes string s returns nothing
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "|cffff0000IMAGE TOOLS ERROR:|r\n " + "|cff99b4d1" + "[" + s + "]|r" )
endfunction
endif
globals
private Table table = 0
endglobals
function NewImage takes string file, real sizeX, real sizeY, real posX, real posY, real posZ, integer imageType returns image
local image i = CreateImage(file, sizeX, sizeY, 0, posX, posY, posZ, 0, 0, 0, imageType)
if (0 > GetHandleId(i)) then
debug if (imageType < IMAGE_TYPE_SELECTION) or (imageType > IMAGE_TYPE_UBERSPLAT) then
debug call DebugMsg("function NewImage: Invalid image type [" + I2S(imageType) + "] for: " + file)
debug else
debug call DebugMsg("function NewImage: Can't find string path in data: " + file)
debug endif
return null
else
set table.boolean[GetHandleId(i)] = true
return i
endif
endfunction
function ReleaseImage takes image i returns nothing
local integer id = GetHandleId(i)
if (id > 0) and (table.boolean.has(id)) then
call table.boolean.remove(id)
call DestroyImage(i)
debug elseif (id > 0) then
debug call DebugMsg("function ReleaseImage: Attempt to double destroy an image handle: [" + I2S(id) + "]!" )
debug call DebugMsg("function ReleaseImage: Or even worse, you created an image without using NewImage!" )
debug else
debug call DebugMsg("function ReleaseImage: Attempt to destroy an invalid image handle! ( null )")
endif
endfunction
function CreateImageCenter takes string file, real sizeX, real sizeY, real centerX, real centerY, real centerZ, integer imageType returns image
return NewImage(file, sizeX, sizeY, centerX - sizeX*.5, centerY - sizeY*.5, centerZ, imageType)
endfunction
private module InitImageTools
private static method onInit takes nothing returns nothing
set table = Table.create()
endmethod
endmodule
private struct I extends array
implement InitImageTools
endstruct
//! runtextmacro optional IMAGE_TOOLS_IMPORT_STRUCT_CODE()
endlibrary
scope TestScope initializer Init
globals
private Table table
private player array owner
private integer array unitType
private real array startX
private real array startY
private real array startFacing
endglobals
private function EnableUnitCustomAttack takes unit u returns nothing
local CustomAttack attack = CustomAttack[u]
call CustomAttack.register(u)
set attack.melee = false
set attack.showShadow = false
set attack.cooldown = 2.00
set attack.damagePoint = 0.35
set attack.speed = 350.00
set attack.acceleration = 0.00
set attack.maxTargets = 5
set attack.acquisitionRange = 1100.00
set attack.range = 700.00
set attack.arc = 45.00*bj_DEGTORAD
set attack.maxDamage = 13.00
set attack.minDamage = 12.00
call attack.addAttackSfx("Abilities\\Weapons\\LichMissile\\LichMissile.mdl")
endfunction
private function CreateUnits takes nothing returns nothing
local integer count = 20
local real dA = 2.00*bj_PI/count
loop
exitwhen count == 0
call EnableUnitCustomAttack(CreateUnitByName(Player(1), "footman", 1600*Cos(dA*count), 1600*Sin(dA*count), dA*count*bj_RADTODEG))
set count = count - 1
endloop
call EnableUnitCustomAttack(CreateUnitByName(Player(1), "footman", 300.00, -300.00, 270))
endfunction
private function CreateItems takes nothing returns nothing
local integer i = 25
loop
exitwhen i == 0
call CreateItem('brac', -1000.00, i*80 - 1080)
call CreateItem('brac', 1000.00, i*80 - 1080)
call CreateItem('brac', i*80 - 1000, -1000.00)
call CreateItem('brac', i*80 - 1000, 1000.00)
set i = i - 1
endloop
endfunction
private function Revive takes nothing returns nothing
local timer expired = GetExpiredTimer()
local integer handleId = GetHandleId(expired)
local integer index = table[handleId]
if IsHeroUnitId(unitType[index]) then
call ReviveHero(GetUnitById(index), startX[index], startY[index], true)
else
call EnableUnitCustomAttack(CreateUnit(owner[index], unitType[index], startX[index], startY[index], startFacing[index]))
call RemoveUnit(GetUnitById(index))
endif
call table.remove(handleId)
call DestroyTimer(expired)
set expired = null
endfunction
private function OnDeath takes nothing returns nothing
local timer t
local unit u = GetTriggerUnit()
if GetUnitTypeId(u) != 'hpea' then
set t = CreateTimer()
set table[GetHandleId(t)] = GetUnitId(u)
call TimerStart(t, 3.00, false, function Revive)
set t = null
endif
set u = null
endfunction
private function OnIndex takes nothing returns boolean
set owner[GetIndexedUnitId()] = GetOwningPlayer(GetIndexedUnit())
set unitType[GetIndexedUnitId()] = GetUnitTypeId(GetIndexedUnit())
set startX[GetIndexedUnitId()] = GetUnitX(GetIndexedUnit())
set startY[GetIndexedUnitId()] = GetUnitY(GetIndexedUnit())
set startFacing[GetIndexedUnitId()] = GetUnitFacing(GetIndexedUnit())
return false
endfunction
private function OnEsc takes nothing returns nothing
call ClearTextMessages()
endfunction
private function InitMap takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerEvent(t, GetLocalPlayer(), EVENT_PLAYER_END_CINEMATIC)
call TriggerAddCondition(t, Filter(function OnEsc))
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function OnDeath)
call OnUnitIndex(function OnIndex)
call FogMaskEnable(false)
call FogEnable(false)
call PanCameraToTimed(0.00, 0.00, 0.00)
set table = Table.create()
endfunction
private function Init takes nothing returns nothing
call CreateUnits()
call CreateItems()
call InitMap()
call Print("- |cffffcc00You can press ESC to clear text messages|r")
endfunction
endscope
library Msg
globals
private key KEY
endglobals
private function Display takes nothing returns nothing
local timer expired = GetExpiredTimer()
local integer handleId = GetHandleId(expired)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 100000.00, Table(KEY).string[handleId])
call Table(KEY).string.remove(handleId)
call DestroyTimer(expired)
set expired = null
endfunction
function Print takes string msg returns nothing
local timer t = CreateTimer()
set Table(KEY).string[GetHandleId(t)] = msg
call TimerStart(t, 0.00, false, function Display)
set t = null
endfunction
endlibrary
scope AttackModifierTest initializer Init
globals
private constant integer SHOP_ID = 'shop'
private constant integer SUB_SHOP_ID = 'ssub'
private constant integer ITEM_ORB_FLAME = 'Ixo1'
private constant integer ITEM_ORB_FROST = 'Ixo2'
private constant integer ITEM_ORB_VENOM = 'Ixo3'
private constant integer ITEM_ORB_CORRUPTION = 'Ixo4'
private constant integer ITEM_ORB_SHADOW = 'Ixo5'
private constant integer ITEM_SPEED_INCREASE = 'Ixsi'
private constant integer ITEM_SPEED_DECREASE = 'Ixsd'
private constant integer ITEM_RANGE_INCREASE = 'Ixri'
private constant integer ITEM_RANGE_DECREASE = 'Ixrd'
private constant integer ITEM_COOLDOWN_INCREASE = 'Ixci'
private constant integer ITEM_COOLDOWN_DECREASE = 'Ixcd'
private constant integer ITEM_BOUNCES_INCREASE = 'Ixbi'
private constant integer ITEM_BOUNCES_DECREASE = 'Ixbd'
private constant integer ITEM_SHOP_1 = 'Iss1'
private constant integer ITEM_SHOP_2 = 'Iss2'
private constant integer ITEM_SHOP_EXIT = 'Iext'
private integer array orbCount
private boolean array frozenFlag
private boolean array poisonFlag
private integer array poisonCount
private real array freezeTimeRemaining
private real array poisonTimeRemaining
private unit shopMain
private unit shop1
private unit shop2
private Table table
private TableArray tableArray
endglobals
private function OnItemAcquire takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer id = GetItemTypeId(GetManipulatedItem())
local CustomAttack attack = CustomAttack[u]
if id == ITEM_SHOP_1 or id == ITEM_SHOP_2 or id == ITEM_SHOP_EXIT then
if GetLocalPlayer() == GetTriggerPlayer() then
if id == ITEM_SHOP_1 then
call ClearSelection()
call SelectUnit(shop1, true)
elseif id == ITEM_SHOP_2 then
call ClearSelection()
call SelectUnit(shop2, true)
elseif id == ITEM_SHOP_EXIT then
if IsUnitSelected(shopMain, GetLocalPlayer()) then
call ClearSelection()
else
call ClearSelection()
call SelectUnit(shopMain, true)
endif
endif
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_SPEED_INCREASE then
if attack.enabled then
set attack.speed = attack.speed + 50.00
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_SPEED_DECREASE then
if attack.enabled then
set attack.speed = attack.speed - 50.00
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_RANGE_INCREASE then
if attack.enabled then
set attack.range = attack.range + 50.00
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_RANGE_DECREASE then
if attack.enabled then
set attack.range = attack.range - 50.00
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_COOLDOWN_INCREASE then
if attack.enabled then
set attack.cooldown = attack.cooldown + 0.10
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_COOLDOWN_DECREASE then
if attack.enabled then
set attack.cooldown = attack.cooldown - 0.10
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_BOUNCES_INCREASE then
if attack.enabled then
set attack.maxBounces = attack.maxBounces + 1
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_BOUNCES_DECREASE then
if attack.enabled then
set attack.maxBounces = attack.maxBounces - 1
endif
call RemoveItem(GetManipulatedItem())
elseif id == ITEM_ORB_FLAME or id == ITEM_ORB_FROST or id == ITEM_ORB_VENOM or id == ITEM_ORB_CORRUPTION or id == ITEM_ORB_SHADOW then
if tableArray[attack][id] == 0 then
call AttackModifier(tableArray[0][id]).registerUnit(u)
endif
set tableArray[attack][id] = tableArray[attack][id] + 1
if orbCount[attack] == 0 and not attack.enabled then
set attack.enabled = true
endif
set orbCount[attack] = orbCount[attack] + 1
endif
set u = null
endfunction
private function OnItemDrop takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer id = GetItemTypeId(GetManipulatedItem())
local CustomAttack attack = CustomAttack[u]
if id == ITEM_ORB_FLAME or id == ITEM_ORB_FROST or id == ITEM_ORB_VENOM or id == ITEM_ORB_CORRUPTION or id == ITEM_ORB_SHADOW then
set tableArray[attack][id] = tableArray[attack][id] - 1
if tableArray[attack][id] == 0 then
call AttackModifier(tableArray[0][id]).unregisterUnit(u)
endif
set orbCount[attack] = orbCount[attack] - 1
if orbCount[attack] == 0 and attack.enabled then
set attack.enabled = false
endif
endif
set u = null
endfunction
private function OnFreezePeriodic takes nothing returns nothing
local timer expired = GetExpiredTimer()
local integer targetId = table[GetHandleId(expired)]
local unit target
set freezeTimeRemaining[targetId] = freezeTimeRemaining[targetId] - 0.031250000
if freezeTimeRemaining[targetId] > 0.00 and UnitAlive(GetUnitById(targetId)) then
if freezeTimeRemaining[targetId] - R2I(freezeTimeRemaining[targetId]/2)*2 == 0 then
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\AIob\\AIobSpecialArt.mdl", GetUnitById(targetId), "origin"))
endif
else
set target = GetUnitById(targetId)
call SetUnitMoveSpeed(target, GetUnitMoveSpeed(target)/(1.00 - 0.50))
call SetUnitVertexColor(target, 255, 255, 255, 255)
set frozenFlag[targetId] = false
call DestroyTimer(expired)
call table.remove(GetHandleId(expired))
set target = null
endif
set expired = null
endfunction
private function OnPoisonPeriodic takes nothing returns nothing
local timer expired = GetExpiredTimer()
local integer targetId = table[-GetHandleId(expired)]
set poisonTimeRemaining[targetId] = poisonTimeRemaining[targetId] - 0.031250000
if poisonTimeRemaining[targetId] > 0.00 and UnitAlive(GetUnitById(targetId)) then
call SetWidgetLife(GetUnitById(targetId), RMaxBJ(0.60, GetWidgetLife(GetUnitById(targetId)) - 2.00*poisonCount[targetId]*0.03125))
if poisonTimeRemaining[targetId] - R2I(poisonTimeRemaining[targetId]) == 0 then
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\OrbVenom\\OrbVenomSpecialArt.mdl", GetUnitById(targetId), "origin"))
endif
else
call SetUnitMoveSpeed(GetUnitById(targetId), GetUnitMoveSpeed(GetUnitById(targetId))/(1.00 - Pow(0.90, poisonCount[targetId])))
set poisonCount[targetId] = 0
set poisonFlag[targetId] = false
call DestroyTimer(expired)
call table.remove(-GetHandleId(expired))
endif
set expired = null
endfunction
private function FlameEffect takes nothing returns nothing
local unit u
local real radius = 200.00
local real splashFactor = 0.75
local group g = CreateGroup()
local effect sfx = AddSpecialEffect("Abilities\\Spells\\Items\\AIfb\\AIfbSpecialArt.mdl", GetEventAttackTargetX(), GetEventAttackTargetY())
call BlzSetSpecialEffectScale(sfx, radius*0.01)
call DestroyEffect(sfx)
call GroupEnumUnitsInRange(g, GetEventAttackTargetX(), GetEventAttackTargetY(), radius, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g, u)
if u != GetEventAttackTargetUnit() and IsUnitEnemy(u, GetOwningPlayer(GetEventAttackSource())) then
call UnitDamageTarget(GetEventAttackSource(), u, GetEventAttackDamage()*splashFactor, true, false, GetEventAttackType(), GetEventDamageType(), GetEventWeaponType())
endif
endloop
call DestroyGroup(g)
set g = null
set sfx = null
endfunction
private function FrostEffect takes nothing returns nothing
local timer t
local unit target = GetEventAttackTargetUnit()
local integer targetId = GetUnitId(target)
if IsUnitEnemy(target, GetOwningPlayer(GetEventAttackSource())) then
if not frozenFlag[targetId] then
set frozenFlag[targetId] = true
set t = CreateTimer()
set table[GetHandleId(t)] = targetId
call TimerStart(t, 0.031250000, true, function OnFreezePeriodic)
call SetUnitMoveSpeed(target, GetUnitMoveSpeed(target)*0.50)
call SetUnitVertexColor(target, 165, 165, 165, 255)
set t = null
endif
set freezeTimeRemaining[targetId] = 8.00
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\AIob\\AIobSpecialArt.mdl", target, "origin"))
endif
set target = null
endfunction
private function VenomEffect takes nothing returns nothing
local timer t
local unit target = GetEventAttackTargetUnit()
local integer targetId = GetUnitId(target)
if IsUnitEnemy(target, GetOwningPlayer(GetEventAttackSource())) then
if not poisonFlag[targetId] then
set poisonFlag[targetId] = true
set t = CreateTimer()
set table[-GetHandleId(t)] = targetId
call TimerStart(t, 0.031250000, true, function OnPoisonPeriodic)
set t = null
endif
call SetUnitMoveSpeed(target, GetUnitMoveSpeed(target)*0.90)
set poisonCount[targetId] = poisonCount[targetId] + 1
set poisonTimeRemaining[targetId] = 6.00
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\OrbVenom\\OrbVenomSpecialArt.mdl", target, "origin"))
endif
set target = null
endfunction
private function CorruptionEffect takes nothing returns nothing
local real lifeStealFactor = 0.25
if IsUnitEnemy(GetEventAttackTargetUnit(), GetOwningPlayer(GetEventAttackSource())) then
call SetWidgetLife(GetEventAttackSource(), GetWidgetLife(GetEventAttackSource()) + RMaxBJ(0.00, GetEventAttackDamage()*lifeStealFactor))
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", GetEventAttackSource(), "overhead"))
endif
endfunction
private function ShadowEffect takes nothing returns nothing
local real stunDuration = 0.30
if IsUnitEnemy(GetEventAttackTargetUnit(), GetOwningPlayer(GetEventAttackSource())) then
call Stun.apply(GetEventAttackTargetUnit(), stunDuration, false)
endif
endfunction
private function InitOrbs takes nothing returns nothing
local AttackModifier orbOfFire = AttackModifier.create()
local AttackModifier orbOfFrost = AttackModifier.create()
local AttackModifier orbOfVenom = AttackModifier.create()
local AttackModifier orbOfCorruption = AttackModifier.create()
local AttackModifier orbOfShadow = AttackModifier.create()
set orbOfFire.missileArt = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
set orbOfFrost.missileArt = "Abilities\\Weapons\\LichMissile\\LichMissile.mdl"
set orbOfVenom.missileArt = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
set orbOfCorruption.missileArt = "Abilities\\Spells\\Items\\OrbCorruption\\OrbCorruptionMissile.mdl"
set orbOfShadow.missileArt = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
call orbOfFire .registerHandler(function FlameEffect)
call orbOfFrost .registerHandler(function FrostEffect)
call orbOfVenom .registerHandler(function VenomEffect)
call orbOfCorruption .registerHandler(function CorruptionEffect)
call orbOfShadow .registerHandler(function ShadowEffect)
set tableArray[0][ITEM_ORB_FLAME] = orbOfFire
set tableArray[0][ITEM_ORB_FROST] = orbOfFrost
set tableArray[0][ITEM_ORB_VENOM] = orbOfVenom
set tableArray[0][ITEM_ORB_CORRUPTION] = orbOfCorruption
set tableArray[0][ITEM_ORB_SHADOW] = orbOfShadow
endfunction
private function InitShop takes nothing returns nothing
set shopMain = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), SHOP_ID, 0.00, 0.00, 270.00)
set shop1 = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), SUB_SHOP_ID, 0.00, 0.00, 270.00)
set shop2 = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), SUB_SHOP_ID, 0.00, 0.00, 270.00)
call SetUnitScale(shop1, 0.00, 0.00, 0.00)
call SetUnitScale(shop2, 0.00, 0.00, 0.00)
call AddItemToStock(shop1, ITEM_ORB_FLAME, 1, 1)
call AddItemToStock(shop1, ITEM_ORB_FROST, 1, 1)
call AddItemToStock(shop1, ITEM_ORB_VENOM, 1, 1)
call AddItemToStock(shop1, ITEM_ORB_CORRUPTION, 1, 1)
call AddItemToStock(shop1, ITEM_ORB_SHADOW, 1, 1)
call AddItemToStock(shop2, ITEM_COOLDOWN_INCREASE, 1, 1)
call AddItemToStock(shop2, ITEM_COOLDOWN_DECREASE, 1, 1)
call AddItemToStock(shop2, ITEM_SPEED_INCREASE, 1, 1)
call AddItemToStock(shop2, ITEM_SPEED_DECREASE, 1, 1)
call AddItemToStock(shop2, ITEM_RANGE_INCREASE, 1, 1)
call AddItemToStock(shop2, ITEM_RANGE_DECREASE, 1, 1)
call AddItemToStock(shop2, ITEM_BOUNCES_INCREASE, 1, 1)
call AddItemToStock(shop2, ITEM_BOUNCES_DECREASE, 1, 1)
call AddItemToStock(shopMain, ITEM_SHOP_1, 1, 1)
call AddItemToStock(shopMain, ITEM_SHOP_2, 1, 1)
call AddItemToStock(shopMain, ITEM_SHOP_EXIT, 1, 1)
call AddItemToStock(shop1, ITEM_SHOP_EXIT, 1, 1)
call AddItemToStock(shop2, ITEM_SHOP_EXIT, 1, 1)
call DisableTrigger(bj_stockItemPurchased)
call PauseTimer(bj_stockUpdateTimer)
endfunction
private function InitEvents takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function OnItemAcquire)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function OnItemDrop)
endfunction
private function Init takes nothing returns nothing
set table = Table.create()
set tableArray = TableArray[JASS_MAX_ARRAY_SIZE - 1]
call InitOrbs()
call InitShop()
call InitEvents()
call Print("- |cffffcc00You can buy attack-upgrading items in the Circle Of Power to test the capabilities of the Attack System|r
- |cffffcc00You need to buy at least 1 Orb in the shop to activate your attacks|r")
endfunction
endscope
scope HealAllies initializer Init // Inverts the damage if the target unit is an ally of the attacker
private function OnAttackFinish takes nothing returns nothing
if IsEventAttackMelee() and IsUnitAlly(GetEventAttackTargetUnit(), GetOwningPlayer(GetEventAttackSource())) then
call SetEventAttackDamage(-GetEventAttackDamage())
endif
endfunction
private function OnImpact takes nothing returns nothing
if IsUnitAlly(GetEventAttackTargetUnit(), GetOwningPlayer(GetEventAttackSource())) then
call SetEventAttackDamage(RMinBJ(-GetEventAttackDamage(), GetEventAttackDamage()))
else
call SetEventAttackDamage(RMaxBJ(-GetEventAttackDamage(), GetEventAttackDamage()))
endif
endfunction
private function Init takes nothing returns nothing
call RegisterAttackEventHandler(EVENT_ATTACK_FINISH, function OnAttackFinish)
call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_IMPACT, function OnImpact)
call Print("- |cffffcc00Your attacks can heal allies|r")
endfunction
endscope
scope EvasionTest initializer Init
private function OnChat takes nothing returns nothing
local string s = GetEventPlayerChatString()
local group temp
local unit u
if SubString(s, 0, 2) == "- " then
set s = SubString(s, 2, StringLength(s))
set temp = CreateGroup()
call GroupAddGroup(UnitDex.Group, temp)
loop
set u = FirstOfGroup(temp)
exitwhen u == null
call GroupRemoveUnit(temp, u)
if IsUnitSelected(u, GetLocalPlayer()) then
call SetUnitEvasion(u, S2R(s))
endif
endloop
call DestroyGroup(temp)
set temp = null
endif
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, GetLocalPlayer(), "- ", false)
call TriggerAddCondition(t, Filter(function OnChat))
set t = null
call Print("- |cffffcc00Type \"- xxxx\" to set the evasion chance of the currently selected unit(s) to 'xxxx' (where 'xxxx' is a decimal value)|r
* |cffffcc00A value of 1.00 gives 100% evasion chance|r")
endfunction
endscope
library StunSystem uses Table
//********************************************************************************
// Stun - Version 1.2.0.0 - By iAyanami aka Ayanami
//********************************************************************************
//
// Stun:
// - An easy to use system that stuns units
// - Able to keep track of the remaining stun duration
//
// Requirements:
// - JASS NewGen
// - Table
//
// Functions:
// - Stun.apply takes unit whichUnit, real duration, boolean stack returns nothing
// * whichUnit is the target to be stunned
// * duration is the duration of the stun
// * stack is to determine if the stun should be a stacking one or not
// * true - the stun will stack, the duration of the stun will add up with the previous duration
// * false - the stun will not stack, the unit will be stunned for the longer stun duration
//
// - Stun.getDuration takes unit whichUnit returns real
// * whichUnit is the target to check
// * returns the remaining stun duration
//
// - Stun.stop takes unit whichUnit returns nothing
// * removes stun from the target
//
// How to import:
// - Copy the whole "Stun" Trigger Folder into your map
// - Save the map. Close and re-opem the map.
// - You should have a new unit (Stun Dummy), ability (Stun (System)) and buff (Stun (System)) created
// - Read through the Configuration part of the code
//
// Credits:
// - Bribe for Table
//
//********************************************************************************
// CONFIGURABLES
//********************************************************************************
globals
// timer period. lower the value, the more accurate but might cause decrease in
// performance
private constant real PERIOD = 0.03125
// raw code of ability "Stun (System)"
private constant integer ABILID = 'ASTN'
// raw code of buff "Stun (System)"
private constant integer BUFFID = 'BSTN'
// raw code of unit "Stun Dummy"
private constant integer STUNID = 'sTUN'
endglobals
//********************************************************************************
// CODE
//********************************************************************************
// initialization
module Init
private static method onInit takes nothing returns nothing
set table = Table.create()
set caster = CreateUnit(Player(13), STUNID, 0, 0, 0)
call UnitAddAbility(caster, ABILID)
endmethod
endmodule
struct Stun extends array
private unit u
private real dur
private thistype next
private thistype prev
private static Table table
private static timer t = CreateTimer()
private static unit caster
private static integer count = 0
// remove the stun and deallocate
private method destroy takes nothing returns nothing
call UnitRemoveAbility(this.u, BUFFID)
if this.next != 0 then
set this.next.prev = this.prev
endif
set this.prev.next = this.next
set this.dur = 0
set this.prev = thistype(0).prev
set thistype(0).prev = this
if thistype(0).next == 0 then
call PauseTimer(t)
endif
call table.remove(GetHandleId(this.u))
endmethod
// iterating through all instances every PERIOD
private static method iterate takes nothing returns nothing
local thistype this = thistype(0)
loop
set this = this.next
exitwhen this == 0
if this.dur <= 0 or IsUnitType(this.u, UNIT_TYPE_DEAD) or GetUnitTypeId(this.u) == 0 then
call this.destroy()
else
set this.dur = this.dur - PERIOD
endif
endloop
endmethod
// immediately removes stun for the specified unit
// ex: call Stun.stop(whichTarget)
static method stop takes unit u returns nothing
local integer id = GetHandleId(u)
if table.has(id) then
call thistype(table[id]).destroy()
endif
endmethod
// gets the duration left for stun, not stunned units always return 0
// ex: local real r = Stun.getDuration(whichTarget)
static method getDuration takes unit u returns real
return thistype(table[GetHandleId(u)]).dur
endmethod
// stunning specified target and to see if the stun is a stacking one or not
// ex: call Stun.apply(whichTarget, 5.0, false)
static method apply takes unit u, real dur, boolean b returns nothing
local thistype this
local integer id = GetHandleId(u)
if table.has(id) then
set this = table[id]
else
if thistype(0).prev == 0 then
set count = count + 1
set this = count
else
set this = thistype(0).prev
set thistype(0).prev = thistype(0).prev.prev
endif
if thistype(0).next == 0 then
call TimerStart(t, PERIOD, true, function thistype.iterate)
else
set thistype(0).next.prev = this
endif
set this.next = thistype(0).next
set thistype(0).next = this
set this.prev = thistype(0)
set table[id] = this
set this.u = u
set this.dur = 0
call IssueTargetOrder(caster, "firebolt", this.u)
endif
if b and dur > 0 then
set this.dur = this.dur + dur
else
if this.dur < dur then
set this.dur = dur
endif
endif
endmethod
implement Init
endstruct
endlibrary