Name | Type | is_array | initial_value |
DemoMap_Hero | unit | No | |
Illusion_AddDuration | integer | No | |
Illusion_BaseUnit | unit | No | |
Illusion_CreateIllusion | integer | No | |
Illusion_CreateIllusionCopy | integer | No | |
Illusion_DamageDealtFactor | real | No | |
Illusion_DamageReceiveFactor | real | No | |
Illusion_DeathEffect | string | No | |
Illusion_DeathEvent | real | No | |
Illusion_Duration | real | No | |
Illusion_Facing | real | No | |
Illusion_Flag | boolean | No | |
Illusion_GetBaseUnit | integer | No | |
Illusion_GetDamageFactor | integer | No | |
Illusion_GetDuration | integer | No | |
Illusion_GetElapsedTime | integer | No | |
Illusion_GetFlag | integer | No | |
Illusion_GetReceiveFactor | integer | No | |
Illusion_GetRemainingTime | integer | No | |
Illusion_Operation | integer | No | |
Illusion_OperationTrigger | trigger | No | |
Illusion_Owner | player | No | |
Illusion_Position | location | No | |
Illusion_SetDamageFactor | integer | No | |
Illusion_SetDeathEffect | integer | No | |
Illusion_SetDuration | integer | No | |
Illusion_SetReceiveFactor | integer | No | |
Illusion_TriggerUnit | unit | No | |
Illusion_Unit | unit | No | |
Illusion_UnitType | unitcode | No | |
LS_AreaDamage | real | Yes | |
LS_AreaDamageBase | real | No | |
LS_AreaDamagePerLevel | real | No | |
LS_AttackType | attacktype | No | |
LS_Caster | unit | Yes | |
LS_CasterLocation | location | No | |
LS_CollisionSize | real | Yes | |
LS_CollisionSizeBase | real | No | |
LS_CollisionSizePerLevel | real | No | |
LS_Damage | real | Yes | |
LS_DamageBase | real | No | |
LS_DamagePerLevel | real | No | |
LS_DamageType | damagetype | No | |
LS_DestroyFlag | boolean | Yes | |
LS_DistanceTravelled | real | Yes | |
LS_Dummy | unit | Yes | |
LS_DummySfx | effect | Yes | |
LS_DummyUnitType | unitcode | No | |
LS_HeadingAngle | real | No | |
LS_ImpactRadius | real | Yes | |
LS_ImpactRadiusBase | real | No | |
LS_ImpactRadiusPerLevel | real | No | |
LS_ImpactSfxAttachPoint | string | No | |
LS_ImpactSfxModel | string | No | |
LS_Level | real | No | |
LS_MapMaxX | real | No | |
LS_MapMaxY | real | No | |
LS_MapMinX | real | No | |
LS_MapMinY | real | No | |
LS_MaxDistance | real | Yes | |
LS_MaxDistanceBase | real | No | |
LS_MaxDistancePerLevel | real | No | |
LS_MissileHeightBase | real | No | |
LS_MissileHeightPerLevel | real | No | |
LS_MissileOffset | real | No | |
LS_MissileSfxAttachPoint | string | No | |
LS_MissileSfxModel | string | No | |
LS_NewX | real | No | |
LS_NewY | real | No | |
LS_SpeedCos | real | Yes | |
LS_SpeedSin | real | Yes | |
LS_TempDestructable | destructable | No | |
LS_TempGroup | group | No | |
LS_TempInt | integer | No | |
LS_TempLocation | location | No | |
LS_TempUnit | unit | No | |
LS_Timer | timer | No | |
LS_TravelSpeed | real | Yes | |
LS_TravelSpeedBase | real | No | |
LS_TravelSpeedPerLevel | real | No | |
SPELL__Ability | abilcode | No | |
SPELL__AllyFilterFlag | boolean | No | |
SPELL__DeadFilterFlag | boolean | No | |
SPELL__DUMMY_PLAYER | player | No | |
SPELL__EnemyFilterFlag | boolean | No | |
SPELL__EnumCount | integer | No | |
SPELL__EnumedTargets | unit | Yes | |
SPELL__EnumerateTargetsInRange | trigger | No | |
SPELL__EnumRange | real | No | |
SPELL__Event | real | No | |
SPELL__EVENT_CAST | integer | No | |
SPELL__EVENT_CHANNEL | integer | No | |
SPELL__EVENT_EFFECT | integer | No | |
SPELL__EVENT_ENDCAST | integer | No | |
SPELL__EVENT_FINISH | integer | No | |
SPELL__EventType | integer | No | |
SPELL__ExitPeriodic | boolean | No | |
SPELL__FlyingFilterFlag | boolean | No | |
SPELL__FRAME_RATE | real | No | |
SPELL__HeroFilterFlag | boolean | No | |
SPELL__Index | integer | No | |
SPELL__InitializationEvent | real | No | |
SPELL__InvokeEvent | trigger | No | |
SPELL__Level | integer | No | |
SPELL__LivingFilterFlag | boolean | No | |
SPELL__MagicImmuneFilterFlag | boolean | No | |
SPELL__MechanicalFilterFlag | boolean | No | |
SPELL__NonHeroFilterFlag | boolean | No | |
SPELL__OnEndTrigger | trigger | No | |
SPELL__OnPeriodTrigger | trigger | No | |
SPELL__OnStartTrigger | trigger | No | |
SPELL__ORDER_NO_TARGET | integer | No | |
SPELL__ORDER_POINT_TARGET | integer | No | |
SPELL__ORDER_SINGLE_TARGET | integer | No | |
SPELL__OrderType | integer | No | |
SPELL__OverrideParams | trigger | No | |
SPELL__PERIOD | real | No | |
SPELL__RealLevel | real | No | |
SPELL__RegisterHandler | trigger | No | |
SPELL__StructureFilterFlag | boolean | No | |
SPELL__TargetDest | destructable | No | |
SPELL__TargetItem | item | No | |
SPELL__TargetPoint | location | No | |
SPELL__TargetUnit | unit | No | |
SPELL__TriggerPlayer | player | No | |
SPELL__TriggerUnit | unit | No | |
TempLoc | location | No | |
TempReal | real | No | |
TempUnit | unit | No |
library SpellFramework /* https://www.hiveworkshop.com/threads/325448/
*/uses /*
[FRAMEWORK CORE]
The core of the spell development framework contains three components namely, SpellEvent, SpellEventGUI,
and SpellCloner. SpellEvent and SpellCloner go hand in hand in creating an organized template for spell
development in vJass.
Below are VERY brief overview of the purpose of each component. A more intensive description of each one can
be found on the header of each listed library.
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
SpellEvent handles and automates common spell-related tasks such as spell indexing and event registration.
It further provides useful advanced features such as manual event invocation, event parameters overriding,
and event cancelling. It also provides event response variables/functions that are intuitive to use and
recursion-safe.
*/SpellCloner /* https://www.hiveworkshop.com/threads/324157/
SpellCloner provides a framework for ensuring the maintainability of custom spells. It divides the
custom spell into two sections namely, the spell mechanics, and the spell configuration. This provides
convenience in updating the code for the spell mechanics without touching the configuration section.
The spell configuration section can also contain more than one set of spell configurations - which is
cool.
*/optional SpellEventGUI /*
The SpellEventGUI in itself is a framework specifically made for GUI developers. It is built upon the
SpellEvent library, from which it derives many of its functionalities. It also provides utilities for
other spell-related tasks that are not so convenient in GUI such as filtered unit-group enumeration.
It also automatically handles channeling abilities interruption/finish for the spell developer unlike
in its vJass counterpart where freedom in usage is given more priority over total automation.
[UTILITY COMPONENTS]
These components are included as they are almost always used in spell making.
*/Alloc /* https://www.hiveworkshop.com/threads/324937/
Allocator module - compatible for recent patches with the updated JASS_MAX_ARRAY_SIZE
You can also use any other Alloc if you prefer but be sure to update it to adapt to JASS_MAX_ARRAY_SIZE
*/LinkedList /* https://www.hiveworkshop.com/threads/325635/
Library providing linked-list modules with different variants
[List of External Library Requirements]
Required:
> Table
https://www.hiveworkshop.com/threads/188084/
Optional:
> RegisterPlayerUnitEvent
https://www.hiveworkshop.com/threads/250266/
> ResourcePreloader
https://www.hiveworkshop.com/threads/287358/
> ErrorMessage
https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
*/
endlibrary
/*
* An Alias
*/
library SpellDevFramework uses SpellFramework
endlibrary
library SpellEvent /* v3.1.2 https://www.hiveworkshop.com/threads/301895/
*/uses /*
*/Table /* https://www.hiveworkshop.com/threads/188084/
*/LinkedList /* https://www.hiveworkshop.com/threads/325635/
*/optional Alloc /* https://www.hiveworkshop.com/threads/324937/
*/optional RegisterPlayerUnitEvent /* https://www.hiveworkshop.com/threads/250266/
*/optional ResourcePreloader /* https://www.hiveworkshop.com/threads/287358/
*/optional ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
*///! novjass
/*
A library that eases and expands the possibilities of custom spells development.
Core Features:
1. Two-phase spell event handlers
2. Manual spell event invocation
3. Event parameters overriding and event cancelling
4. Spell development template
1.) Spell event handlers are grouped into two: generic handlers that runs for every spell, and ability-specific
handlers. All generic handlers run first. Within them, you can do things such as changing the event parameters
(caster, target, etc.) as well as preventing the ability-specific handlers from running. The second phase are
the specific handlers which are for the spell developers to define the mechanics of the spell.
Note: Generic handlers only run for spells that have existing ability-specific handlers. This is because
generic handlers are intended as custom-spell modifier, not an ability handler. If you want to catch an event
that runs for just any ability (including normal OE abilities), you can easily use (in fact, you should) the
blizzard native events instead.
2.) You can invoke a spell event to run and define the parameters manually. This removes the need for dummy
casters in most cases.
3.) As mentioned in no. 1, within the generic event handlers, you can override the event parameters. The change
will only affect the ability-specific handlers. You can also stop the ability-specific handlers from running
(known as event cancelling).
Note: Event cancelling is currently incompatible with Reforged (Crashes the game).
4.) This library provides a framework for the flow of spell through the use of modules. This removes from the
spell developers the additional work of manual spell event registration, spell instance allocation, and other
minute tasks such as storing and looping through each active spell instance.
*/
|=========|
| Credits |
|=========|
/*
- AGD (Author)
- Bribe, Nestharus (SpellEffectEvent concept)
- Anitarf (Original SpellEvent Idea)
*/
|=========|
| Structs |
|=========|
/*
*/struct Spell extends array/*
*/static constant thistype GENERIC /* You can also use this like 'Spell.GENERIC.registerEventHandlers()'
Event Responses:
*/readonly static integer abilityId /*
*/readonly static integer eventType /*
*/readonly static integer orderType /*
*/readonly static integer level /*
*/readonly static player triggerPlayer /*
*/readonly static unit triggerUnit /*
*/readonly static unit targetUnit /* Fixed a mistake in the native event responses where target is set to caster for 'No-target' abilities based on channel (Is set to null instead)
*/readonly static item targetItem /*
*/readonly static destructable targetDest /*
*/readonly static widget target /*
*/readonly static real targetX /* Returns the x-coordinate of the caster if the spell is a 'No-target' ability
*/readonly static real targetY /* Returns the y-coordinate of the caster if the spell is a 'No-target' ability
Fields:
*/readonly integer abilId/*
- Rawcode of the activation ability for the spell
*/boolean handlersDisabled/*
- (Incompatible with Reforged versions - crashes the game)
- Value is always reset to false before running the generic spell handlers
- Set to <true> to increment the internal disabler counter or <false> to decrement counter
- If counter > 0, the ability-specific handlers won't run
Methods:
*/static method operator [] takes integer abilId returns Spell/*
- Returns a Spell instance based on the given activation-ability rawcode which can be used for event handler registrations
*/method setEventFlag takes integer eventType, boolean flag returns nothing/*
*/method getEventFlag takes integer eventType returns boolean/*
- Disables/Enables certain event types from running for a Spell (These flags are <true> by default)
*/method invokeNoTargetEvent takes integer eventType, integer level, unit caster returns nothing/*
*/method invokePointTargetEvent takes integer eventType, integer level, unit caster, real targetX, real targetY returns nothing/*
*/method invokeSingleTargetEvent takes integer eventType, integer level, unit caster, widget target returns nothing/*
- Manually invokes a spell event
*/static method overrideNoTargetParams takes integer level, unit caster returns nothing/*
*/static method overridePointTargetParams takes integer level, unit caster, real targetX, real targetY returns nothing/*
*/static method overrideSingleTargetParams takes integer level, unit caster, widget target returns nothing/*
- Overrides the values of the event response variables (Only effective when called inside a generic event handler)
- The values are only overriden in the ability-specific spell event handlers
*/method registerEventHandler takes integer eventType, code handler returns nothing/*
*/method unregisterEventHandler takes integer eventType, code handler returns nothing/*
*/method clearEventHandlers takes integer eventType returns nothing/*
*/method clearHandlers takes nothing returns nothing/*
- Manages ability-specific spell event handlers
*/static method registerGenericEventHandler takes integer eventType, code handler returns nothing/*
*/static method unregisterGenericEventHandler takes integer eventType, code handler returns nothing/*
*/static method clearGenericEventHandlers takes integer eventType returns nothing/*
*/static method clearGenericHandlers takes nothing returns nothing/*
- Manages generic spell event handlers
*/
|===========|
| Variables |
|===========|
/*
Spell Event Types
*/constant integer EVENT_SPELL_CAST/*
*/constant integer EVENT_SPELL_CHANNEL/*
*/constant integer EVENT_SPELL_EFFECT/*
*/constant integer EVENT_SPELL_ENDCAST/*
*/constant integer EVENT_SPELL_FINISH/*
Spell Order Types
*/constant integer SPELL_ORDER_TYPE_SINGLE_TARGET/*
*/constant integer SPELL_ORDER_TYPE_POINT_TARGET/*
*/constant integer SPELL_ORDER_TYPE_NO_TARGET/*
*/
|===========|
| Functions |
|===========|
/*
Equivalent functions for the methods above
(Event Responses)
*/constant function GetEventSpellAbilityId takes nothing returns integer/*
*/constant function GetEventSpellEventType takes nothing returns integer/*
*/constant function GetEventSpellOrderType takes nothing returns integer/*
*/constant function GetEventSpellLevel takes nothing returns integer/*
*/constant function GetEventSpellPlayer takes nothing returns player/*
*/constant function GetEventSpellCaster takes nothing returns unit/*
*/constant function GetEventSpellTargetUnit takes nothing returns unit/*
*/constant function GetEventSpellTargetItem takes nothing returns item/*
*/constant function GetEventSpellTargetDest takes nothing returns destructable/*
*/constant function GetEventSpellTarget takes nothing returns widget/*
*/constant function GetEventSpellTargetX takes nothing returns real/*
*/constant function GetEventSpellTargetY takes nothing returns real/*
*/function SetSpellEventFlag takes integer abilId, integer eventType, boolean flag returns nothing/*
*/function GetSpellEventFlag takes integer abilId, integer eventType returns boolean/*
*/function SpellCancelEventHandlers takes boolean cancel returns nothing/*
- This function is imcompatible with Reforged versions
*/function SpellInvokeNoTargetEvent takes integer abilId, integer eventType, integer level, unit caster returns nothing/*
*/function SpellInvokePointTargetEvent takes integer abilId, integer eventType, integer level, unit caster, real targetX, real targetY returns nothing/*
*/function SpellInvokeSingleTargetEvent takes integer abilId, integer eventType, integer level, unit caster, widget target returns nothing/*
*/function SpellOverrideNoTargetParams takes integer level, unit caster returns nothing/*
*/function SpellOverridePointTargetParams takes integer level, unit caster, real targetX, real targetY returns nothing/*
*/function SpellOverrideSingleTargetParams takes integer level, unit caster, widget target returns nothing/*
*/function SpellRegisterEventHandler takes integer abilId, integer eventType, code handler returns nothing/*
*/function SpellUnregisterEventHandler takes integer abilId, integer eventType, code handler returns nothing/*
*/function SpellClearEventHandlers takes integer abilId, integer eventType returns nothing/*
*/function SpellClearHandlers takes integer abilId returns nothing/*
*/function SpellRegisterGenericEventHandler takes integer eventType, code handler returns nothing/*
*/function SpellUnregisterGenericEventHandler takes integer eventType, code handler returns nothing/*
*/function SpellClearGenericEventHandlers takes integer eventType returns nothing/*
*/function SpellClearGenericHandlers takes nothing returns nothing/*
*/
|=========|
| Modules |
|=========|
/*
Automates spell event handler registration at map initialization
Modules <SpellEvent> and <SpellEventEx> cannot both be implemented in the same struct
*/module SpellEvent extends LinkedListLite/*
> Uses a single timer (per struct) for all active spell instances. Standard module designed for
periodic spells with high-frequency timeout (<= 0.5 seconds)
Fields:
*/readonly thistype prev/*
*/readonly thistype next/*
- 'Inherited' from LinkedListLite module
- Spell instances links
- Readonly attribute is only effective outside the implementing struct, though users are
also not supposed to change these values from inside the struct.
Public methods:
*/static method registerSpellEvent takes integer abilId, integer eventType returns nothing/*
- Manually registers an ability rawcode to trigger spell events
- Can be used for spells that involve more than one activation ability IDs
Member interfaces:
- Should be declared above the module implementation
*/interface static integer SPELL_ABILITY_ID /* Ability rawcode
*/interface static integer SPELL_EVENT_TYPE /* Spell event type
*/interface static real SPELL_PERIOD /* Spell periodic actions execution period
*/interface method onSpellStart takes nothing returns thistype/*
- Runs right after the spell event fires
- Returning zero or a negative value will not run the periodic operations for that instance
- Returning a negative value will try to find that value's positive node equivalent and
calls onSpellEnd() for that node and removes it from the list.
- You can return a different value or transmute 'this', provided that all your nodes/values
comes from the same node/value stack. The important thing to remember is that all nodes in
the list should be unique. Also remember to always deallocate what you manually allocated.
- The value returned will be added to the list of instances that will run onSpellPeriodic().
*/optional interface method onSpellPeriodic takes nothing returns boolean/*
- Runs periodically after the spell event fires until it returns true
*/optional interface method onSpellEnd takes nothing returns nothing/*
- Runs after method onSpellPeriodic() returns true
- If onSpellPeriodic() is not present, this will be called after onSpellStart() returns a valid instance
*/module SpellEventEx/*
> Uses 1 timer for each active spell instance. A module specifically designed for
periodic spells with low-frequency timeout (> 0.5 seconds) as it does not affect
the accuracy of the first 'tick' of the periodic operations. Here, you always
need to manually allocate/deallocate your spell instances.
Public methods:
*/static method registerSpellEvent takes integer abilId, integer eventType returns nothing/*
- Manually registers a spell rawcode to trigger spell events
- Can be used for spells that involves more than one abilityId
Member interfaces:
- Should be declared above the module implementation
*/interface static integer SPELL_ABILITY_ID /* Ability rawcode
*/interface static integer SPELL_EVENT_TYPE /* Spell event type
*/interface static real SPELL_PERIOD /* Spell periodic actions execution period
*/interface static method onSpellStart takes nothing returns thistype/*
- Runs right after the spell event fires
- User should manually allocate the spell instance and use it as a return value of this method
- Returning zero or a negative value will not run the periodic operations for that instance
- Returning a negative value will try to find that value's positive node equivalent and calls
onSpellEnd() for that node.
*/optional interface method onSpellPeriodic takes nothing returns boolean/*
- Runs periodically after the spell event fires until it returns true
*/optional interface method onSpellEnd takes nothing returns nothing/*
- Runs after method onSpellPeriodic() returns true
- If onSpellPeriodic() is not present, this will be called after onSpellStart() returns a valid instance
- User must manually deallocate the spell instance inside this method
*/module SpellEventGeneric/*
Member interfaces:
- Should be declared above the module implementation
*/optional interface static method onSpellEvent takes nothing returns nothing/*
- Runs on any generic spell event
*/optional interface static method onSpellCast takes nothing returns nothing/*
*/optional interface static method onSpellChannel takes nothing returns nothing/*
*/optional interface static method onSpellEffect takes nothing returns nothing/*
*/optional interface static method onSpellEndcast takes nothing returns nothing/*
*/optional interface static method onSpellFinish takes nothing returns nothing/*
- Runs on certain generic spell events
*///! endnovjass
/*=================================== SYSTEM CODE ===================================*/
globals
constant integer EVENT_SPELL_CAST = 0x1
constant integer EVENT_SPELL_CHANNEL = 0x2
constant integer EVENT_SPELL_EFFECT = 0x4
constant integer EVENT_SPELL_ENDCAST = 0x8
constant integer EVENT_SPELL_FINISH = 0x10
constant integer SPELL_ORDER_TYPE_SINGLE_TARGET = 0x12
constant integer SPELL_ORDER_TYPE_POINT_TARGET = 0x123
constant integer SPELL_ORDER_TYPE_NO_TARGET = 0x1234
endglobals
globals
private integer eventAbilityId = 0
private integer eventEventType = 0
private integer eventOrderType = 0
private integer eventLevel = 0
private player eventTriggerPlayer = null
private unit eventTriggerUnit = null
private unit eventTargetUnit = null
private item eventTargetItem = null
private destructable eventTargetDest = null
private real eventTargetX = 0.00
private real eventTargetY = 0.00
private integer tempOrderType = 0
private integer tempLevel = 0
private player tempTriggerPlayer = null
private unit tempTriggerUnit = null
private widget tempTarget = null
private real tempTargetX = 0.00
private real tempTargetY = 0.00
private boolexpr bridgeExpr
private TableArray table
private integer array eventTypeId
private integer array eventIndex
endglobals
private keyword Init
static if DEBUG_MODE then
private function IsValidEventType takes integer eventType returns boolean
return eventType > 0 and eventType <= (EVENT_SPELL_CAST + EVENT_SPELL_CHANNEL + EVENT_SPELL_EFFECT + EVENT_SPELL_ENDCAST + EVENT_SPELL_FINISH)
endfunction
private function IsEventSingleFlag takes integer eventType returns boolean
return eventType == EVENT_SPELL_CAST or/*
*/ eventType == EVENT_SPELL_CHANNEL or/*
*/ eventType == EVENT_SPELL_EFFECT or/*
*/ eventType == EVENT_SPELL_ENDCAST or/*
*/ eventType == EVENT_SPELL_FINISH
endfunction
private function AssertError takes boolean condition, string methodName, string structName, integer instance, string message returns nothing
static if LIBRARY_ErrorMessage then
call ThrowError(condition, SCOPE_PREFIX, methodName, structName, instance, message)
else
if condition then
call BJDebugMsg("|cffff0000[ERROR]|r [Library: " + SCOPE_PREFIX + "] [Struct: " + structName + "] [Method: " + methodName + "] [Instance: " + I2S(instance) + "] : |cffff0000" + message + "|r")
call PauseGame(true)
endif
endif
endfunction
endif
/*===================================================================================*/
private function OnOverrideParams takes nothing returns nothing
set eventOrderType = tempOrderType
set eventLevel = tempLevel
set eventTriggerPlayer = GetOwningPlayer(tempTriggerUnit)
set eventTriggerUnit = tempTriggerUnit
set eventTargetX = tempTargetX
set eventTargetY = tempTargetY
if tempTarget == null then
set eventTargetUnit = null
set eventTargetItem = null
set eventTargetDest = null
else
set table[0].widget[0] = tempTarget
set eventTargetUnit = table[0].unit[0]
set eventTargetItem = table[0].item[0]
set eventTargetDest = table[0].destructable[0]
call table[0].handle.remove(0)
endif
set tempOrderType = 0
set tempLevel = 0
set tempTriggerUnit = null
set tempTargetX = 0.00
set tempTargetY = 0.00
set tempTarget = null
endfunction
/*===================================================================================*/
/*
* One Allocator for the whole library. Yes, it would be unlikely for this system to
* reach JASS_MAX_ARRAY_SIZE instances of allocated nodes at a single time.
*
* Need to use custom Alloc because of the updated value for JASS_MAX_ARRAY_SIZE.
* Credits to MyPad for the allocation algorithm
*/
private struct Node extends array
static if LIBRARY_Alloc then
implement optional Alloc
else
private static thistype array stack
static method allocate takes nothing returns thistype
local thistype node = stack[0]
if stack[node] == 0 then
debug call AssertError(node == (JASS_MAX_ARRAY_SIZE - 1), "allocate()", "thistype", node, "Overflow")
set node = node + 1
set stack[0] = node
else
set stack[0] = stack[node]
set stack[node] = 0
endif
return node
endmethod
method deallocate takes nothing returns nothing
debug call AssertError(this == 0, "deallocate()", "thistype", 0, "Null node")
debug call AssertError(stack[this] > 0, "deallocate()", "thistype", this, "Double-free")
set stack[this] = stack[0]
set stack[0] = this
endmethod
endif
endstruct
private struct ConditionList extends array
triggercondition handle
private static method onRemove takes thistype node returns nothing
set node.handle = null
call Node(node).deallocate()
endmethod
implement List
endstruct
private struct ExprList extends array
boolexpr handle
method operator conditionList takes nothing returns ConditionList
return this
endmethod
private static method onRemove takes thistype node returns nothing
set node.handle = null
call Node(node).deallocate()
endmethod
implement List
method insertExpr takes boolexpr expr returns thistype
local thistype node = Node.allocate()
set node.handle = expr
call insert(this, node)
return node
endmethod
endstruct
private struct Handler extends array
readonly trigger trigger
boolean overrideParams
integer disablerCounter
private integer index
private static ExprList array genericList
private method operator exprList takes nothing returns ExprList
return this
endmethod
/*
* You might think that the process of registering handlers are expensive in performance
* due to constant rebuilding of triggerconditions each time, but setting up proper spell
* handlers are seldom done (often only once per spell) and a large part of them are done
* at map initialization.
*/
method updateHandlers takes nothing returns nothing
local ExprList exprNode = genericList[this.index].next
local ConditionList conditionNode
call TriggerClearConditions(this.trigger)
if exprNode != genericList[this.index].prev then
loop
exitwhen exprNode == genericList[this.index]
set conditionNode = exprNode.conditionList.next
loop
exitwhen conditionNode == exprNode.conditionList
call TriggerAddCondition(this.trigger, exprNode.handle)
set conditionNode = conditionNode.next
endloop
set exprNode = exprNode.next
endloop
endif
set exprNode = this.exprList.next
loop
exitwhen exprNode == this.exprList
set conditionNode = exprNode.conditionList.next
loop
exitwhen conditionNode == exprNode.conditionList
set conditionNode.handle = TriggerAddCondition(this.trigger, exprNode.handle)
set conditionNode = conditionNode.next
endloop
set exprNode = exprNode.next
endloop
endmethod
/*
* This method is registered in position after all the generic handlers and before the
* ability-specific handlers. Its position allows it to unlink all the ability-specific
* handlers positioned after it or to change the event parameters, should the user choose to
*/
private static method bridge takes nothing returns nothing
local integer triggerId = GetHandleId(GetTriggeringTrigger())
local thistype node = table[0][triggerId]
local trigger tempTrig
if node.disablerCounter > 0 then
if node.exprList.next != node.exprList then
set tempTrig = node.trigger
set node.trigger = CreateTrigger()
set table[0][GetHandleId(node.trigger)] = node
call node.updateHandlers()
call table[0].remove(triggerId)
call TriggerClearConditions(tempTrig)
call DestroyTrigger(tempTrig)
set tempTrig = null
endif
return
endif
if node.overrideParams and node.exprList.next != node.exprList then
call OnOverrideParams()
endif
endmethod
static method registerGeneric takes integer eventIndex, boolexpr expr returns nothing
local integer exprId = GetHandleId(expr)
local ExprList exprNode = table[genericList[eventIndex]][exprId]
if exprNode == 0 then
set exprNode = genericList[eventIndex].prev.prev.insertExpr(expr)
call ConditionList.makeHead(exprNode.conditionList)
set table[genericList[eventIndex]][exprId] = exprNode
endif
call exprNode.conditionList.pushBack(Node.allocate())
endmethod
static method unregisterGeneric takes integer eventIndex, integer exprId returns nothing
local ExprList exprNode = table[genericList[eventIndex]][exprId]
local ConditionList conditionNode = exprNode.conditionList.next
loop
exitwhen conditionNode == exprNode.conditionList
call ConditionList.remove(conditionNode)
set conditionNode = conditionNode.next
endloop
call table[genericList[eventIndex]].remove(exprId)
call exprList.remove(exprNode)
endmethod
static method clearGeneric takes integer eventIndex returns nothing
local ExprList exprNode = genericList[eventIndex].next
loop
exitwhen exprNode == genericList[eventIndex].prev
call unregisterGeneric(eventIndex, GetHandleId(exprNode.handle))
set exprNode = exprNode.next
endloop
endmethod
method register takes boolexpr expr returns nothing
local integer exprId = GetHandleId(expr)
local ExprList exprNode = table[this][exprId]
local ConditionList conditionNode = Node.allocate()
if this.exprList.empty then
set this.trigger = CreateTrigger()
set table[0][GetHandleId(this.trigger)] = this
endif
if exprNode == 0 then
set exprNode = this.exprList.prev.insertExpr(expr)
call ConditionList.makeHead(exprNode.conditionList)
set table[this][exprId] = exprNode
call exprNode.conditionList.pushBack(conditionNode)
call this.updateHandlers()
else
call exprNode.conditionList.pushBack(conditionNode)
if exprNode.next == this.exprList then
set conditionNode.handle = TriggerAddCondition(this.trigger, expr)
else
call this.updateHandlers()
endif
endif
endmethod
method unregister takes integer exprId returns nothing
local ExprList exprNode = table[this][exprId]
local ConditionList conditionNode = exprNode.conditionList.next
loop
exitwhen conditionNode == exprNode.conditionList
call TriggerRemoveCondition(this.trigger, conditionNode.handle)
call ConditionList.remove(conditionNode)
set conditionNode = conditionNode.next
endloop
call ExprList.remove(exprNode)
call table[this].remove(exprId)
if this.exprList.empty then
call table[0].remove(GetHandleId(this.trigger))
call DestroyTrigger(this.trigger)
set this.trigger = null
endif
endmethod
method clear takes nothing returns nothing
local ExprList exprNode = this.exprList.next
loop
exitwhen exprNode == this.exprList
call this.unregister(GetHandleId(exprNode.handle))
set exprNode = exprNode.next
endloop
endmethod
static method create takes integer eventIndex returns thistype
local thistype node = Node.allocate()
call ExprList.makeHead(node)
set node.index = eventIndex
set node.disablerCounter = 0
return node
endmethod
method destroy takes nothing returns nothing
call this.clear()
call Node(this).deallocate()
endmethod
debug static method hasGenericExpr takes integer eventIndex, boolexpr expr returns boolean
debug return table[genericList[eventIndex]][GetHandleId(expr)] != 0
debug endmethod
debug method hasExpr takes boolexpr expr returns boolean
debug return table[this][GetHandleId(expr)] != 0
debug endmethod
method operator enabled= takes boolean flag returns nothing
if flag then
call EnableTrigger(this.trigger)
else
call DisableTrigger(this.trigger)
endif
endmethod
method operator enabled takes nothing returns boolean
return IsTriggerEnabled(this.trigger)
endmethod
private static method initGenericList takes integer eventIndex returns nothing
local ExprList list = create(eventIndex)
local ExprList exprNode = list.insertExpr(bridgeExpr)
call ConditionList.makeHead(exprNode.conditionList)
call exprNode.conditionList.pushBack(Node.allocate())
set genericList[eventIndex] = list
endmethod
static method init takes nothing returns nothing
/*
* This bridge boolexpr executes after all the generic spell handlers
* before transitioning into the ability-specific spell handlers.
* This boolexpr is responsible for disabling the ability-specific handlers
* (if requested) as well as implementing the overriding of the event
* parameters.
*/
local code bridgeFunc = function thistype.bridge
set bridgeExpr = Filter(bridgeFunc)
call initGenericList(eventIndex[EVENT_SPELL_CAST])
call initGenericList(eventIndex[EVENT_SPELL_CHANNEL])
call initGenericList(eventIndex[EVENT_SPELL_EFFECT])
call initGenericList(eventIndex[EVENT_SPELL_ENDCAST])
call initGenericList(eventIndex[EVENT_SPELL_FINISH])
endmethod
endstruct
/*===================================================================================*/
struct Spell extends array
readonly integer abilId
private static integer spellCount = 0
private static Node spellKey
private static Handler array eventHandler
static if not LIBRARY_ResourcePreloader then
private static unit preloadDummy
endif
static constant method operator abilityId takes nothing returns integer
return eventAbilityId
endmethod
static constant method operator eventType takes nothing returns integer
return eventEventType
endmethod
static constant method operator orderType takes nothing returns integer
return eventOrderType
endmethod
static constant method operator level takes nothing returns integer
return eventLevel
endmethod
static constant method operator triggerPlayer takes nothing returns player
return eventTriggerPlayer
endmethod
static constant method operator triggerUnit takes nothing returns unit
return eventTriggerUnit
endmethod
static constant method operator targetUnit takes nothing returns unit
return eventTargetUnit
endmethod
static constant method operator targetItem takes nothing returns item
return eventTargetItem
endmethod
static constant method operator targetDest takes nothing returns destructable
return eventTargetDest
endmethod
static constant method operator target takes nothing returns widget
if eventTargetUnit != null then
return eventTargetUnit
elseif eventTargetItem != null then
return eventTargetItem
elseif eventTargetDest != null then
return eventTargetDest
endif
return null
endmethod
static constant method operator targetX takes nothing returns real
return eventTargetX
endmethod
static constant method operator targetY takes nothing returns real
return eventTargetY
endmethod
static method operator [] takes integer abilId returns thistype
local thistype this = table[spellKey][abilId]
local integer offset
if this == 0 then
debug call AssertError(spellCount > R2I(JASS_MAX_ARRAY_SIZE/5), "Spell[]", "thistype", 0, "Overflow")
static if LIBRARY_ResourcePreloader then
call PreloadAbility(abilId)
else
if UnitAddAbility(preloadDummy, abilId) then
call UnitRemoveAbility(preloadDummy, abilId)
endif
endif
set spellCount = spellCount + 1
set thistype(spellCount).abilId = abilId
set table[spellKey][abilId] = spellCount
set offset = (spellCount - 1)*5
set eventHandler[offset + eventIndex[EVENT_SPELL_CAST]] = Handler.create(eventIndex[EVENT_SPELL_CAST])
set eventHandler[offset + eventIndex[EVENT_SPELL_CHANNEL]] = Handler.create(eventIndex[EVENT_SPELL_CHANNEL])
set eventHandler[offset + eventIndex[EVENT_SPELL_EFFECT]] = Handler.create(eventIndex[EVENT_SPELL_EFFECT])
set eventHandler[offset + eventIndex[EVENT_SPELL_ENDCAST]] = Handler.create(eventIndex[EVENT_SPELL_ENDCAST])
set eventHandler[offset + eventIndex[EVENT_SPELL_FINISH]] = Handler.create(eventIndex[EVENT_SPELL_FINISH])
return spellCount
endif
return this
endmethod
static method operator GENERIC takes nothing returns thistype
return thistype[0]
endmethod
static method registerGenericEventHandler takes integer eventType, code handler returns nothing
local boolexpr expr = Filter(handler)
local integer eventId = 0x10
local integer node
if eventType != 0 then
debug call AssertError(not IsValidEventType(eventType), "registerGenericEventHandler()", "thistype", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
call Handler.registerGeneric(eventIndex[eventId], expr)
set node = spellCount
loop
exitwhen node == 0
set node = node - 1
call eventHandler[node*5 + eventIndex[eventId]].updateHandlers()
endloop
endif
set eventId = eventId/2
endloop
endif
set expr = null
endmethod
static method unregisterGenericEventHandler takes integer eventType, code handler returns nothing
local boolexpr expr = Filter(handler)
local integer eventId = 0x10
local integer node
if eventType != 0 then
debug call AssertError(not IsValidEventType(eventType), "unregisterGenericEventHandler()", "thistype", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
debug call AssertError(not Handler.hasGenericExpr(eventIndex[eventId], expr), "unregisterGenericEventHandler()", "thistype", 0, "EventType(" + I2S(eventType) + "): Code is not registered")
call Handler.unregisterGeneric(eventIndex[eventId], GetHandleId(expr))
set node = spellCount
loop
exitwhen node == 0
set node = node - 1
call eventHandler[node*5 + eventIndex[eventId]].updateHandlers()
endloop
endif
set eventId = eventId/2
endloop
endif
set expr = null
endmethod
static method clearGenericEventHandlers takes integer eventType returns nothing
local integer eventId = 0x10
local integer node
if eventType != 0 then
debug call AssertError(not IsValidEventType(eventType), "clearGenericEventHandlers()", "thistype", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
call Handler.clearGeneric(eventIndex[eventId])
set node = spellCount
loop
exitwhen node == 0
set node = node - 1
call eventHandler[node*5 + eventIndex[eventId]].updateHandlers()
endloop
endif
set eventId = eventId/2
endloop
endif
endmethod
static method clearGenericHandlers takes nothing returns nothing
call clearGenericEventHandlers(EVENT_SPELL_CAST + EVENT_SPELL_CHANNEL + EVENT_SPELL_EFFECT + EVENT_SPELL_ENDCAST + EVENT_SPELL_FINISH)
endmethod
method registerEventHandler takes integer eventType, code handler returns nothing
local boolexpr expr = Filter(handler)
local integer offset = (this - 1)*5
local integer eventId = 0x10
if eventType != 0 then
debug call AssertError((this) < 1 or (this) > spellCount, "registerEventHandler()", "thistype", this, "Invalid Spell instance")
debug call AssertError(not IsValidEventType(eventType), "registerEventHandler()", "thistype", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
if this == GENERIC then
call registerGenericEventHandler(eventType, handler)
else
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
call eventHandler[offset + eventIndex[eventId]].register(expr)
endif
set eventId = eventId/2
endloop
endif
endif
set expr = null
endmethod
method unregisterEventHandler takes integer eventType, code handler returns nothing
local boolexpr expr = Filter(handler)
local integer offset = (this - 1)*5
local integer eventId = 0x10
if eventType != 0 then
debug call AssertError((this) < 1 or (this) > spellCount, "unregisterEventHandler()", "thistype", this, "Invalid Spell instance")
debug call AssertError(not IsValidEventType(eventType), "unregisterEventHandler()", "thistype", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
if this == GENERIC then
call unregisterGenericEventHandler(eventType, handler)
else
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
debug call AssertError(not eventHandler[offset + eventIndex[eventId]].hasExpr(expr), "registerEventHandler()", "thistype", this, "EventType(" + I2S(eventType) + "): Code is already unregistered")
call eventHandler[offset + eventIndex[eventId]].unregister(GetHandleId(expr))
endif
set eventId = eventId/2
endloop
endif
endif
set expr = null
endmethod
method clearEventHandlers takes integer eventType returns nothing
local integer offset = (this - 1)*5
local integer eventId = 0x10
if eventType != 0 then
debug call AssertError((this) < 1 or (this) > spellCount, "SpellEvent", "clearEventHandlers()", this, "Invalid Spell instance")
debug call AssertError(not IsValidEventType(eventType), "SpellEvent", "clearEventHandlers()", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
if this == GENERIC then
call clearGenericEventHandlers(eventType)
else
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
call eventHandler[offset + eventIndex[eventId]].clear()
endif
set eventId = eventId/2
endloop
endif
endif
endmethod
method clearHandlers takes nothing returns nothing
debug call AssertError((this) < 1 or (this) > spellCount, "clearHandlers()", "thistype", this, "Invalid Spell instance")
if this == GENERIC then
call this.clearGenericHandlers()
else
call this.clearEventHandlers(EVENT_SPELL_CAST + EVENT_SPELL_CHANNEL + EVENT_SPELL_EFFECT + EVENT_SPELL_ENDCAST + EVENT_SPELL_FINISH)
endif
endmethod
method setEventFlag takes integer eventType, boolean flag returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "setEventFlag()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
set eventHandler[(this - 1)*5 + eventIndex[eventType]].enabled = flag
endmethod
method getEventFlag takes integer eventType returns boolean
debug call AssertError(not IsEventSingleFlag(eventType), "getEventFlag()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
return eventHandler[(this - 1)*5 + eventIndex[eventType]].enabled
endmethod
method operator handlersDisabled= takes boolean disabled returns nothing
local Handler handler
if eventAbilityId == this.abilId then
set handler = eventHandler[(this - 1)*5 + eventIndex[eventEventType]]
if disabled then
set handler.disablerCounter = handler.disablerCounter + 1
else
set handler.disablerCounter = handler.disablerCounter - 1
endif
endif
endmethod
method operator handlersDisabled takes nothing returns boolean
if eventAbilityId != this.abilId then
return false
endif
return eventHandler[(this - 1)*5 + eventIndex[eventEventType]].disablerCounter > 0
endmethod
private static method overrideParams takes integer orderType, integer level, unit triggerUnit, widget target, real targetX, real targetY returns nothing
if eventAbilityId != 0 then
set Handler(table[0][GetHandleId(GetTriggeringTrigger())]).overrideParams = true
set tempOrderType = orderType
set tempLevel = level
set tempTriggerPlayer = GetOwningPlayer(triggerUnit)
set tempTriggerUnit = triggerUnit
set tempTargetX = targetX
set tempTargetY = targetY
set tempTarget = target
endif
endmethod
static method overrideNoTargetParams takes integer level, unit triggerUnit returns nothing
call overrideParams(SPELL_ORDER_TYPE_NO_TARGET, level, triggerUnit, null, GetUnitX(triggerUnit), GetUnitY(triggerUnit))
endmethod
static method overridePointTargetParams takes integer level, unit triggerUnit, real targetX, real targetY returns nothing
call overrideParams(SPELL_ORDER_TYPE_POINT_TARGET, level, triggerUnit, null, targetX, targetY)
endmethod
static method overrideSingleTargetParams takes integer level, unit triggerUnit, widget target returns nothing
call overrideParams(SPELL_ORDER_TYPE_SINGLE_TARGET, level, triggerUnit, target, GetWidgetX(target), GetWidgetY(target))
endmethod
private static method setTargetData takes real x, real y, integer orderType returns nothing
set eventTargetX = x
set eventTargetY = y
set eventOrderType = orderType
endmethod
private static method executeEventHandler takes Handler eventHandler, integer currentId, boolean manualInvoke, integer eventFlag, integer orderType, integer level, unit triggerUnit, widget target, real targetX, real targetY returns nothing
local integer disablerCounter = eventHandler.disablerCounter
local boolean overrideParams = eventHandler.overrideParams
local integer prevAbilityId = eventAbilityId
local integer prevEventType = eventEventType
local integer prevOrderType = eventOrderType
local integer prevLevel = eventLevel
local player prevTriggerPlayer = eventTriggerPlayer
local unit prevTriggerUnit = eventTriggerUnit
local real prevTargetX = eventTargetX
local real prevTargetY = eventTargetY
local unit prevTargetUnit = eventTargetUnit
local item prevTargetItem = eventTargetItem
local destructable prevTargetDest = eventTargetDest
local location tempLoc
set eventAbilityId = currentId
if manualInvoke then
call setTargetData(targetX, targetY, orderType)
set eventEventType = eventFlag
set eventLevel = level
set eventTriggerPlayer = GetOwningPlayer(triggerUnit)
set eventTriggerUnit = triggerUnit
set table[0].widget[0] = target
set eventTargetUnit = table[0].unit[0]
set eventTargetItem = table[0].item[0]
set eventTargetDest = table[0].destructable[0]
else
set eventEventType = eventTypeId[GetHandleId(GetTriggerEventId())]
set eventTriggerPlayer = GetTriggerPlayer()
set eventTriggerUnit = GetTriggerUnit()
set eventLevel = GetUnitAbilityLevel(eventTriggerUnit, eventAbilityId)
set eventTargetUnit = GetSpellTargetUnit()
set eventTargetItem = GetSpellTargetItem()
set eventTargetDest = GetSpellTargetDestructable()
if eventTargetUnit != null then
if eventTargetUnit == eventTriggerUnit and/*
*/ not (GetSpellTargetX() != 0.) and/*
*/ not (GetSpellTargetY() != 0.) then
/* Special Case (for no-target spells based on channel) */
call setTargetData(GetUnitX(eventTriggerUnit), GetUnitY(eventTriggerUnit), SPELL_ORDER_TYPE_NO_TARGET)
set eventTargetUnit = null
else
call setTargetData(GetUnitX(eventTargetUnit), GetUnitY(eventTargetUnit), SPELL_ORDER_TYPE_SINGLE_TARGET)
endif
elseif eventTargetItem != null then
call setTargetData(GetWidgetX(eventTargetItem), GetWidgetY(eventTargetItem), SPELL_ORDER_TYPE_SINGLE_TARGET)
elseif eventTargetDest != null then
call setTargetData(GetWidgetX(eventTargetDest), GetWidgetY(eventTargetDest), SPELL_ORDER_TYPE_SINGLE_TARGET)
else
set tempLoc = GetSpellTargetLoc()
if tempLoc == null then
/* Special Case (for some no-target spells) */
call setTargetData(GetUnitX(eventTriggerUnit), GetUnitY(eventTriggerUnit), SPELL_ORDER_TYPE_NO_TARGET)
else
call RemoveLocation(tempLoc)
set tempLoc = null
call setTargetData(GetSpellTargetX(), GetSpellTargetY(), SPELL_ORDER_TYPE_POINT_TARGET)
endif
endif
endif
set eventHandler.disablerCounter = 0
set eventHandler.overrideParams = false
call TriggerEvaluate(eventHandler.trigger)
set eventHandler.disablerCounter = disablerCounter
set eventHandler.overrideParams = overrideParams
call setTargetData(prevTargetX, prevTargetY, prevOrderType)
set eventAbilityId = prevAbilityId
set eventEventType = prevEventType
set eventLevel = prevLevel
set eventTriggerPlayer = prevTriggerPlayer
set eventTriggerUnit = prevTriggerUnit
set eventTargetUnit = prevTargetUnit
set eventTargetItem = prevTargetItem
set eventTargetDest = prevTargetDest
set prevTriggerPlayer = null
set prevTriggerUnit = null
set prevTargetUnit = null
set prevTargetItem = null
set prevTargetDest = null
endmethod
private method invokeEvent takes integer eventType, integer orderType, integer level, unit triggerUnit, widget target, real targetX, real targetY returns nothing
local Handler handler = eventHandler[(this - 1)*5 + eventIndex[eventType]]
if handler != 0 and handler.enabled then
call executeEventHandler(handler, this.abilId, true, eventType, orderType, level, triggerUnit, target, targetX, targetY)
endif
endmethod
method invokeNoTargetEvent takes integer eventType, integer level, unit triggerUnit returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "invokeNoTargetEvent()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
call this.invokeEvent(eventType, SPELL_ORDER_TYPE_NO_TARGET, level, triggerUnit, null, GetUnitX(triggerUnit), GetUnitY(triggerUnit))
endmethod
method invokePointTargetEvent takes integer eventType, integer level, unit triggerUnit, real targetX, real targetY returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "invokePointTargetEvent()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
call this.invokeEvent(eventType, SPELL_ORDER_TYPE_POINT_TARGET, level, triggerUnit, null, targetX, targetY)
endmethod
method invokeSingleTargetEvent takes integer eventType, integer level, unit triggerUnit, widget target returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "invokeSingleTargetEvent()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
call this.invokeEvent(eventType, SPELL_ORDER_TYPE_SINGLE_TARGET, level, triggerUnit, target, GetWidgetX(target), GetWidgetY(target))
endmethod
private static method onSpellEvent takes integer eventIndex returns nothing
local integer id = GetSpellAbilityId()
local Handler handler = eventHandler[(table[spellKey][id] - 1)*5 + eventIndex]
if handler != 0 and handler.enabled then
call executeEventHandler(handler, id, false, 0, 0, 0, null, null, 0.00, 0.00)
endif
endmethod
private static method onSpellCast takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_CAST])
endmethod
private static method onSpellChannel takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_CHANNEL])
endmethod
private static method onSpellEffect takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_EFFECT])
endmethod
private static method onSpellEndcast takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_ENDCAST])
endmethod
private static method onSpellFinish takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_FINISH])
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
static if not LIBRARY_ResourcePreloader then
private static method initPreloadDummy takes nothing returns nothing
local rect world = GetWorldBounds()
set preloadDummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hpea', 0.00, 0.00, 0.00)
call SetUnitY(preloadDummy, GetRectMaxY(world) + 1000.00)
call UnitAddAbility(preloadDummy, 'AInv')
call UnitAddAbility(preloadDummy, 'Avul')
call UnitRemoveAbility(preloadDummy, 'Amov')
call RemoveRect(world)
set world = null
endmethod
endif
private static method init takes nothing returns nothing
set spellKey = Node.allocate()
set spellCount = spellCount + 1
set table[spellKey][0] = spellCount
set eventIndex[EVENT_SPELL_CAST] = 1
set eventIndex[EVENT_SPELL_CHANNEL] = 2
set eventIndex[EVENT_SPELL_EFFECT] = 3
set eventIndex[EVENT_SPELL_ENDCAST] = 4
set eventIndex[EVENT_SPELL_FINISH] = 5
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_CAST)] = EVENT_SPELL_CAST
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_CHANNEL)] = EVENT_SPELL_CHANNEL
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_EFFECT)] = EVENT_SPELL_EFFECT
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_ENDCAST)] = EVENT_SPELL_ENDCAST
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_FINISH)] = EVENT_SPELL_FINISH
call registerEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function thistype.onSpellCast)
call registerEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onSpellChannel)
call registerEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onSpellEffect)
call registerEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onSpellEndcast)
call registerEvent(EVENT_PLAYER_UNIT_SPELL_FINISH, function thistype.onSpellFinish)
static if not LIBRARY_ResourcePreloader then
call initPreloadDummy()
endif
endmethod
implement Init
endstruct
private module Init
private static method onInit takes nothing returns nothing
set table = TableArray[JASS_MAX_ARRAY_SIZE]
call init()
call Handler.init()
endmethod
endmodule
/*===================================================================================*/
constant function GetEventSpellAbilityId takes nothing returns integer
return Spell.abilityId
endfunction
constant function GetEventSpellEventType takes nothing returns integer
return Spell.eventType
endfunction
constant function GetEventSpellOrderType takes nothing returns integer
return Spell.orderType
endfunction
constant function GetEventSpellLevel takes nothing returns integer
return Spell.level
endfunction
constant function GetEventSpellPlayer takes nothing returns player
return Spell.triggerPlayer
endfunction
constant function GetEventSpellCaster takes nothing returns unit
return Spell.triggerUnit
endfunction
constant function GetEventSpellTargetUnit takes nothing returns unit
return Spell.targetUnit
endfunction
constant function GetEventSpellTargetItem takes nothing returns item
return Spell.targetItem
endfunction
constant function GetEventSpellTargetDest takes nothing returns destructable
return Spell.targetDest
endfunction
constant function GetEventSpellTarget takes nothing returns widget
return Spell.target
endfunction
constant function GetEventSpellTargetX takes nothing returns real
return Spell.targetX
endfunction
constant function GetEventSpellTargetY takes nothing returns real
return Spell.targetY
endfunction
function SetSpellEventFlag takes integer abilId, integer eventType, boolean flag returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "SetSpellEventFlag()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell[abilId].setEventFlag(eventType, flag)
endfunction
function GetSpellEventFlag takes integer abilId, integer eventType returns boolean
debug call AssertError(not IsEventSingleFlag(eventType), "GetSpellEventFlag()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
return Spell[abilId].getEventFlag(eventType)
endfunction
function SpellCancelEventHandlers takes boolean cancel returns nothing
set Spell[GetEventSpellAbilityId()].handlersDisabled = cancel
endfunction
function SpellInvokeNoTargetEvent takes integer abilId, integer eventType, integer level, unit caster returns nothing
call Spell[abilId].invokeNoTargetEvent(eventType, level, caster)
endfunction
function SpellInvokePointTargetEvent takes integer abilId, integer eventType, integer level, unit caster, real targetX, real targetY returns nothing
call Spell[abilId].invokePointTargetEvent(eventType, level, caster, targetX, targetY)
endfunction
function SpellInvokeSingleTargetEvent takes integer abilId, integer eventType, integer level, unit caster, widget target returns nothing
call Spell[abilId].invokeSingleTargetEvent(eventType, level, caster, target)
endfunction
function SpellOverrideNoTargetParams takes integer level, unit caster returns nothing
call Spell.overrideNoTargetParams(level, caster)
endfunction
function SpellOverridePointTargetParams takes integer level, unit caster, real targetX, real targetY returns nothing
call Spell.overridePointTargetParams(level, caster, targetX, targetY)
endfunction
function SpellOverrideSingleTargetParams takes integer level, unit caster, widget target returns nothing
call Spell.overrideSingleTargetParams(level, caster, target)
endfunction
function SpellRegisterEventHandler takes integer abilId, integer eventType, code handler returns nothing
debug call AssertError(eventType != 0 and not IsValidEventType(eventType), "SpellRegisterEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell[abilId].registerEventHandler(eventType, handler)
endfunction
function SpellUnregisterEventHandler takes integer abilId, integer eventType, code handler returns nothing
debug call AssertError(eventType != 0 and not IsValidEventType(eventType), "SpellUnregisterEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell[abilId].unregisterEventHandler(eventType, handler)
endfunction
function SpellClearEventHandlers takes integer abilId, integer eventType returns nothing
debug call AssertError(eventType != 0 and not IsValidEventType(eventType), "SpellClearEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell[abilId].clearEventHandlers(eventType)
endfunction
function SpellClearHandlers takes integer abilId returns nothing
call Spell[abilId].clearHandlers()
endfunction
function SpellRegisterGenericEventHandler takes integer eventType, code handler returns nothing
debug call AssertError(eventType != 0 and not IsValidEventType(eventType), "SpellRegisterGenericEventHandler()", "", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell.registerGenericEventHandler(eventType, handler)
endfunction
function SpellUnregisterGenericEventHandler takes integer eventType, code handler returns nothing
debug call AssertError(eventType != 0 and not IsValidEventType(eventType), "SpellUnregisterGenericEventHandler()", "", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell.unregisterGenericEventHandler(eventType, handler)
endfunction
function SpellClearGenericEventHandlers takes integer eventType returns nothing
debug call AssertError(eventType != 0 and not IsValidEventType(eventType), "SpellClearGenericEventHandlers()", "", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell.clearGenericEventHandlers(eventType)
endfunction
function SpellClearGenericHandlers takes nothing returns nothing
call Spell.clearGenericHandlers()
endfunction
/*===================================================================================*/
private function DestroyTimerEx takes timer whichTimer returns nothing
call PauseTimer(whichTimer)
call DestroyTimer(whichTimer)
endfunction
private function TimerStartEx takes integer node, real period, code callback returns integer
local timer t
if node > 0 then
set t = CreateTimer()
set table[0].timer[node] = t
set table[0][GetHandleId(t)] = node
call TimerStart(t, period, true, callback)
set t = null
elseif node < 0 then
set node = -node
if table[0].handle.has(node) then
set t = table[0].timer[node]
call table[0].handle.remove(node)
call table[0].remove(GetHandleId(t))
call DestroyTimerEx(t)
set t = null
return node
endif
endif
return 0
endfunction
private function RegisterSpell takes integer abilId, integer eventType, code onSpellEvent returns nothing
if abilId != 0 then
call SpellRegisterEventHandler(abilId, eventType, onSpellEvent)
endif
endfunction
module SpellEvent
static if thistype.onSpellPeriodic.exists then
implement LinkedListLite
private boolean internal
private static timer periodicTimer
private method deleteNode takes nothing returns nothing
call remove(this)
static if thistype.onSpellEnd.exists then
call this.onSpellEnd()
endif
if this.internal then
set this.internal = false
call Node(this).deallocate()
endif
if thistype(0).next == 0 then
call DestroyTimerEx(periodicTimer)
endif
endmethod
private static method onPeriodic takes nothing returns nothing
local thistype node = thistype(0).next
local thistype next
debug local thistype prev
debug local boolean array traversed
/*
* Additional safety check
*/
if node == 0 then
call DestroyTimerEx(periodicTimer)
return
endif
loop
exitwhen node == 0
debug call AssertError(traversed[node], "[Internal] onSpellPeriodic()", "thistype", node, "[Node Links Bugged] : A node in the list was traversed twice in one traversal")
debug set traversed[node] = true
debug set prev = node.prev
set next = node.next
if node.onSpellPeriodic() then
debug call AssertError(node.next.prev != node, "[Internal onSpellPeriodic()", "thistype", node, "[Node Links Bugged] : Node links are messed up!")
call node.deleteNode()
debug set traversed[node] = false
debug elseif next.prev == prev then
debug set traversed[node] = false
endif
set node = next
endloop
endmethod
private static method onSpellEvent takes nothing returns nothing
local thistype node = Node.allocate()
local boolean prevEmpty = thistype(0).next == 0
local thistype used = node.onSpellStart()
/*
* Add the new node into the list
*/
if used == node then
debug call AssertError(used.next.prev == used, "[Internal] onSpellStart()", "thistype", used, "[Node Already Exists] : Make sure your nodes are all from the same stack..")
set used.internal = true
call insert(thistype(0).prev, used)
/*
* If the user returned a different node than the one he was given,
* deallocate the earlier node and replace it with the new node
* from the user.
*/
else
call Node(node).deallocate()
if used > 0 then
debug call AssertError(used.next.prev == used, "[Internal] onSpellStart()", "thistype", used, "[Node Already Exists] : Make sure your nodes are all from the same stack..")
call insert(thistype(0).prev, used)
elseif used < 0 then
set used = -used
if used.next.prev == used then
call used.deleteNode()
endif
endif
endif
/*
* We need to use this kind of check in case the user returned 0
* but manually added some node in the list inside onSpellStart()
*/
if prevEmpty and thistype(0).next != 0 then
set periodicTimer = CreateTimer()
call TimerStart(periodicTimer, SPELL_PERIOD, true, function thistype.onPeriodic)
endif
endmethod
else
/*
* If no periodic operations
*/
private static method onSpellEvent takes nothing returns nothing
local thistype node = Node.allocate()
static if thistype.onSpellEnd.exists then
local thistype used = node.onSpellStart()
if used > 0 then
call used.onSpellEnd()
endif
else
call node.onSpellStart()
endif
call Node(node).deallocate()
endmethod
endif
private static method onInit takes nothing returns nothing
call RegisterSpell(SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.onSpellEvent)
static if thistype.onSpellEventModuleInit.exists then
call onSpellEventModuleInit()
endif
endmethod
static method registerSpellEvent takes integer abilId, integer eventType returns nothing
call SpellRegisterEventHandler(abilId, eventType, function thistype.onSpellEvent)
endmethod
endmodule
module SpellEventEx
static if thistype.onSpellPeriodic.exists then
private static method onPeriodic takes nothing returns nothing
local thistype node = table[0][GetHandleId(GetExpiredTimer())]
if node.onSpellPeriodic() then
static if thistype.onSpellEnd.exists then
call node.onSpellEnd()
endif
call TimerStartEx(-node, 0.00, null)
endif
endmethod
endif
private static method onSpellEvent takes nothing returns nothing
static if thistype.onSpellPeriodic.exists then
static if thistype.onSpellEnd.exists then
local thistype node = TimerStartEx(onSpellStart(), SPELL_PERIOD, function thistype.onPeriodic)
if node > 0 then
call node.onSpellEnd()
endif
else
call TimerStartEx(onSpellStart(), SPELL_PERIOD, function thistype.onPeriodic)
endif
elseif thistype.onSpellEnd.exists then
local thistype node = onSpellStart()
if node > 0 then
call node.onSpellEnd()
endif
else
call onSpellStart()
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpell(SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.onSpellEvent)
static if thistype.onSpellEventModuleInit.exists then
call onSpellEventModuleInit()
endif
endmethod
static method registerSpellEvent takes integer abilId, integer eventType returns nothing
call SpellRegisterEventHandler(abilId, eventType, function thistype.onSpellEvent)
endmethod
endmodule
module SpellEventGeneric
private static method onSpellResponse takes nothing returns nothing
static if thistype.onSpellEvent.exists then
call onSpellEvent()
endif
static if thistype.onSpellCast.exists then
if GetEventSpellEventType() == EVENT_SPELL_CAST then
call onSpellCast()
endif
endif
static if thistype.onSpellChannel.exists then
if GetEventSpellEventType() == EVENT_SPELL_CHANNEL then
call onSpellChannel()
endif
endif
static if thistype.onSpellEffect.exists then
if GetEventSpellEventType() == EVENT_SPELL_EFFECT then
call onSpellEffect()
endif
endif
static if thistype.onSpellEndcast.exists then
if GetEventSpellEventType() == EVENT_SPELL_ENDCAST then
call onSpellEndcast()
endif
endif
static if thistype.onSpellFinish.exists then
if GetEventSpellEventType() == EVENT_SPELL_FINISH then
call onSpellFinish()
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call SpellRegisterGenericEventHandler(EVENT_SPELL_CAST + EVENT_SPELL_CHANNEL + EVENT_SPELL_EFFECT + EVENT_SPELL_ENDCAST + EVENT_SPELL_FINISH, function thistype.onSpellResponse)
static if thistype.onSpellEventGenericModuleInit.exists then
call onSpellEventGenericModuleInit()
endif
endmethod
endmodule
endlibrary
library SpellEventGUI /* v1.0.3
*/uses /*
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/Table /* https://www.hiveworkshop.com/threads/188084/
*/optional ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
*///! novjass
|=========|
| CREDITS |
|=========|
/*
- AGD (Author)
- Bribe (SpellSystem GUI, some features of which I adapted into this GUI Plugin)
*///! endnovjass
/*=============================================================================================*/
native UnitAlive takes unit u returns boolean
globals
private constant integer STOP_ORDER_ID = 851972
private integer array nodeAbilityId
private integer array nodeEventType
private integer array nodeOrderType
private integer array nodeLevel
private player array nodeTriggerPlayer
private unit array nodeTriggerUnit
private real array nodeTargetX
private real array nodeTargetY
private location array nodeTargetPoint
private unit array nodeTargetUnit
private item array nodeTargetItem
private destructable array nodeTargetDest
private integer array filterInstance
private integer array eventIndex
private location staticLocation = Location(0.00, 0.00)
private group enumGroup = CreateGroup()
private integer prevEnumCount = 0
endglobals
private function ResolveFilterFlag takes boolean state, boolean allowTrue, boolean allowFalse returns boolean
return (not state or allowTrue) and (state or allowFalse)
endfunction
private function GetTarget takes nothing returns widget
if udg_SPELL__TargetUnit != null then
return udg_SPELL__TargetUnit
elseif udg_SPELL__TargetItem != null then
return udg_SPELL__TargetItem
elseif udg_SPELL__TargetDest != null then
return udg_SPELL__TargetDest
endif
return null
endfunction
private function GetLevel takes nothing returns integer
if udg_SPELL__Level == 0 then
return R2I(udg_SPELL__RealLevel)
endif
return udg_SPELL__Level
endfunction
private function InitEventParams takes integer abilityId, integer eventType, integer orderType, integer level, player triggerPlayer, unit triggerUnit, real targetX, real targetY, location targetPoint, unit targetUnit, item targetItem, destructable targetDest returns nothing
call MoveLocation(targetPoint, targetX, targetY)
set udg_SPELL__Ability = abilityId
set udg_SPELL__EventType = eventType
set udg_SPELL__OrderType = orderType
set udg_SPELL__Level = level
set udg_SPELL__RealLevel = level
set udg_SPELL__TriggerPlayer = triggerPlayer
set udg_SPELL__TriggerUnit = triggerUnit
set udg_SPELL__TargetPoint = targetPoint
set udg_SPELL__TargetUnit = targetUnit
set udg_SPELL__TargetItem = targetItem
set udg_SPELL__TargetDest = targetDest
endfunction
private function LoadEventParams takes integer node returns nothing
call InitEventParams(/*
*/nodeAbilityId[node], /*
*/nodeEventType[node], /*
*/nodeOrderType[node], /*
*/nodeLevel[node], /*
*/nodeTriggerPlayer[node], /*
*/nodeTriggerUnit[node], /*
*/nodeTargetX[node], /*
*/nodeTargetY[node], /*
*/nodeTargetPoint[node], /*
*/nodeTargetUnit[node], /*
*/nodeTargetItem[node], /*
*/nodeTargetDest[node])
endfunction
private function SaveEventParams takes integer node returns nothing
set nodeAbilityId[node] = udg_SPELL__Ability
set nodeEventType[node] = udg_SPELL__EventType
set nodeOrderType[node] = udg_SPELL__OrderType
set nodeLevel[node] = udg_SPELL__Level
set nodeTriggerPlayer[node] = udg_SPELL__TriggerPlayer
set nodeTriggerUnit[node] = udg_SPELL__TriggerUnit
set nodeTargetX[node] = GetLocationX(udg_SPELL__TargetPoint)
set nodeTargetY[node] = GetLocationY(udg_SPELL__TargetPoint)
set nodeTargetPoint[node] = udg_SPELL__TargetPoint
set nodeTargetUnit[node] = udg_SPELL__TargetUnit
set nodeTargetItem[node] = udg_SPELL__TargetItem
set nodeTargetDest[node] = udg_SPELL__TargetDest
endfunction
private function EvaluateHandler takes trigger handlerTrigger returns nothing
if IsTriggerEnabled(handlerTrigger) and TriggerEvaluate(handlerTrigger) then
call TriggerExecute(handlerTrigger)
endif
endfunction
private struct HandlerList extends array
readonly thistype current
readonly thistype prev
readonly thistype next
readonly trigger onStartTrigger
readonly trigger onPeriodTrigger
readonly trigger onEndTrigger
boolean enemyFlag
boolean allyFlag
boolean deadFlag
boolean livingFlag
boolean magicImmuneFlag
boolean mechanicalFlag
boolean structureFlag
boolean flyingFlag
boolean heroFlag
boolean nonHeroFlag
private static thistype node = 0
method nextNode takes nothing returns nothing
set this.current = this.current.next
endmethod
static method create takes nothing returns thistype
set node = node + 1
set node.prev = node
set node.next = node
set node.current = node
return node
endmethod
method pushBack takes trigger onStart, trigger onPeriod, trigger onEnd returns thistype
local thistype next = this.next
set node = node + 1
set node.prev = this
set node.next = next
set next.prev = node
set this.next = node
set node.onStartTrigger = onStart
set node.onPeriodTrigger = onPeriod
set node.onEndTrigger = onEnd
return node
endmethod
endstruct
private struct SpellEventGUI extends array
/*
* Default spell period: value will be set by the GUI configuration
*/
private static real SPELL_PERIOD
private static constant integer SPELL_ABILITY_ID = 0
private static constant integer SPELL_EVENT_TYPE = 0
private HandlerList spellHandler
private static HandlerList array handlerList
private static key table
private method onSpellStart takes nothing returns thistype
local integer casterId
local thistype prevNode = udg_SPELL__Index
local HandlerList list = handlerList[Spell[GetEventSpellAbilityId()]*5 + eventIndex[GetEventSpellEventType()]]
call list.nextNode()
call SaveEventParams(prevNode)
call InitEventParams(/*
*/GetEventSpellAbilityId(), /*
*/GetEventSpellEventType(), /*
*/GetEventSpellOrderType(), /*
*/GetEventSpellLevel(), /*
*/GetEventSpellPlayer(), /*
*/GetEventSpellCaster(), /*
*/GetEventSpellTargetX(), /*
*/GetEventSpellTargetY(), /*
*/staticLocation, /*
*/GetEventSpellTargetUnit(), /*
*/GetEventSpellTargetItem(), /*
*/GetEventSpellTargetDest())
set udg_SPELL__Index = this
call EvaluateHandler(list.current.onStartTrigger)
set this = udg_SPELL__Index
set udg_SPELL__Index = prevNode
if this > 0 then
set this.spellHandler = list.current
call SaveEventParams(this)
endif
if list.current.next == list then
call list.nextNode()
endif
if GetEventSpellEventType() == EVENT_SPELL_CHANNEL then
set Table(table)[GetHandleId(GetEventSpellCaster())] = this
elseif GetEventSpellEventType() == EVENT_SPELL_ENDCAST then
set casterId = GetHandleId(GetEventSpellCaster())
set prevNode = Table(table)[casterId]
call Table(table).remove(casterId)
if prevNode != 0 then
call LoadEventParams(prevNode)
return -prevNode
endif
endif
call LoadEventParams(prevNode)
return this
endmethod
private method onSpellPeriodic takes nothing returns boolean
local boolean exit
local boolean prevExitFlag = udg_SPELL__ExitPeriodic
local thistype prevNode = udg_SPELL__Index
call SaveEventParams(prevNode)
call LoadEventParams(this)
set udg_SPELL__ExitPeriodic = false
set udg_SPELL__Index = this
call EvaluateHandler(this.spellHandler.onPeriodTrigger)
set exit = udg_SPELL__ExitPeriodic
set udg_SPELL__ExitPeriodic = prevExitFlag
set udg_SPELL__Index = prevNode
call LoadEventParams(prevNode)
if exit then
if nodeEventType[this] != EVENT_SPELL_CHANNEL then
return true
endif
call IssueImmediateOrderById(nodeTriggerUnit[this], STOP_ORDER_ID)
endif
return false
endmethod
private method onSpellEnd takes nothing returns nothing
local thistype prevNode = udg_SPELL__Index
call SaveEventParams(prevNode)
call LoadEventParams(this)
set udg_SPELL__Index = this
call EvaluateHandler(this.spellHandler.onEndTrigger)
set udg_SPELL__Index = prevNode
set nodeTargetPoint[this] = null
set nodeTargetUnit[this] = null
set nodeTargetItem[this] = null
set nodeTargetDest[this] = null
set nodeTriggerUnit[this] = null
set this.spellHandler = 0
call LoadEventParams(prevNode)
endmethod
implement SpellEvent
private static method registerSpell takes nothing returns nothing
local Spell spell = Spell[udg_SPELL__Ability]
local integer eventType = udg_SPELL__EventType
local integer eventId = 0x10
local integer index
local HandlerList node
if eventType == EVENT_SPELL_CHANNEL then
set eventType = eventType + EVENT_SPELL_ENDCAST
endif
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
set index = spell*5 + eventIndex[eventId]
if handlerList[index] == 0 then
set handlerList[index] = HandlerList.create()
endif
set node = handlerList[index].pushBack(udg_SPELL__OnStartTrigger, udg_SPELL__OnPeriodTrigger, udg_SPELL__OnEndTrigger)
set node.enemyFlag = udg_SPELL__EnemyFilterFlag
set node.allyFlag = udg_SPELL__AllyFilterFlag
set node.deadFlag = udg_SPELL__DeadFilterFlag
set node.livingFlag = udg_SPELL__LivingFilterFlag
set node.magicImmuneFlag = udg_SPELL__MagicImmuneFilterFlag
set node.mechanicalFlag = udg_SPELL__MechanicalFilterFlag
set node.structureFlag = udg_SPELL__StructureFilterFlag
set node.flyingFlag = udg_SPELL__FlyingFilterFlag
set node.heroFlag = udg_SPELL__HeroFilterFlag
set node.nonHeroFlag = udg_SPELL__NonHeroFilterFlag
call registerSpellEvent(udg_SPELL__Ability, eventId)
endif
set eventId = eventId/2
endloop
endmethod
private static method onSpellInvoke takes nothing returns nothing
local widget target
if udg_SPELL__OrderType == udg_SPELL__ORDER_NO_TARGET then
call SpellInvokeNoTargetEvent(udg_SPELL__Ability, udg_SPELL__EventType, GetLevel(), udg_SPELL__TriggerUnit)
elseif udg_SPELL__OrderType == udg_SPELL__ORDER_POINT_TARGET then
call SpellInvokePointTargetEvent(udg_SPELL__Ability, udg_SPELL__EventType, GetLevel(), udg_SPELL__TriggerUnit, GetLocationX(udg_SPELL__TargetPoint), GetLocationY(udg_SPELL__TargetPoint))
elseif udg_SPELL__OrderType == udg_SPELL__ORDER_SINGLE_TARGET then
set target = GetTarget()
if target != null then
call SpellInvokeSingleTargetEvent(udg_SPELL__Ability, udg_SPELL__EventType, GetLevel(), udg_SPELL__TriggerUnit, target)
set target = null
endif
endif
endmethod
private static method onOverrideParams takes nothing returns nothing
local widget target
if udg_SPELL__OrderType == udg_SPELL__ORDER_NO_TARGET then
call SpellOverrideNoTargetParams(GetLevel(), udg_SPELL__TriggerUnit)
elseif udg_SPELL__OrderType == udg_SPELL__ORDER_POINT_TARGET then
call SpellOverridePointTargetParams(GetLevel(), udg_SPELL__TriggerUnit, GetLocationX(udg_SPELL__TargetPoint), GetLocationY(udg_SPELL__TargetPoint))
elseif udg_SPELL__OrderType == udg_SPELL__ORDER_SINGLE_TARGET then
set target = GetTarget()
if target != null then
call SpellOverrideSingleTargetParams(GetLevel(), udg_SPELL__TriggerUnit, target)
set target = null
endif
endif
endmethod
private static method onEnumTargets takes nothing returns nothing
local HandlerList node = thistype(udg_SPELL__Index).spellHandler
local integer count = 0
local integer currentCount
local unit u
if node.structureFlag then
call GroupEnumUnitsInRangeOfLoc(enumGroup, udg_SPELL__TargetPoint, udg_SPELL__EnumRange + 197.00, null)
else
call GroupEnumUnitsInRangeOfLoc(enumGroup, udg_SPELL__TargetPoint, udg_SPELL__EnumRange + 64.00, null)
endif
loop
set u = FirstOfGroup(enumGroup)
exitwhen u == null
call GroupRemoveUnit(enumGroup, u)
if (ResolveFilterFlag(IsUnitEnemy(u, udg_SPELL__TriggerPlayer), node.enemyFlag, node.allyFlag)) and/*
*/ (ResolveFilterFlag(IsUnitType(u, UNIT_TYPE_HERO), node.heroFlag, node.nonHeroFlag)) and/*
*/ (ResolveFilterFlag(UnitAlive(u), node.livingFlag, node.deadFlag)) and/*
*/ (node.magicImmuneFlag or not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and/*
*/ (node.mechanicalFlag or not IsUnitType(u, UNIT_TYPE_MECHANICAL)) and/*
*/ (node.structureFlag or not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and/*
*/ (node.flyingFlag or not IsUnitType(u, UNIT_TYPE_FLYING)) then
set count = count + 1
set udg_SPELL__EnumedTargets[count] = u
if count == udg_SPELL__EnumCount then
exitwhen true
endif
endif
endloop
set udg_SPELL__EnumCount = count
if count < prevEnumCount then
/*
* Free unused references
*/
loop
exitwhen count == prevEnumCount
set count = count + 1
set udg_SPELL__EnumedTargets[count] = null
endloop
endif
set prevEnumCount = udg_SPELL__EnumCount
endmethod
private static method onSpellEvent takes nothing returns nothing
local thistype prevNode = udg_SPELL__Index
local integer abilityId = udg_SPELL__Ability
local integer eventType = udg_SPELL__EventType
local integer orderType = udg_SPELL__OrderType
local integer level = udg_SPELL__Level
local player triggerPlayer = udg_SPELL__TriggerPlayer
local unit triggerUnit = udg_SPELL__TriggerUnit
local location targetPoint = udg_SPELL__TargetPoint
local unit targetUnit = udg_SPELL__TargetUnit
local item targetItem = udg_SPELL__TargetItem
local destructable targetDest = udg_SPELL__TargetDest
local real targetX = GetLocationX(udg_SPELL__TargetPoint)
local real targetY = GetLocationY(udg_SPELL__TargetPoint)
call InitEventParams(/*
*/GetEventSpellAbilityId(), /*
*/GetEventSpellEventType(), /*
*/GetEventSpellOrderType(), /*
*/GetEventSpellLevel(), /*
*/GetEventSpellPlayer(), /*
*/GetEventSpellCaster(), /*
*/GetEventSpellTargetX(), /*
*/GetEventSpellTargetY(), /*
*/staticLocation, /*
*/GetEventSpellTargetUnit(), /*
*/GetEventSpellTargetItem(), /*
*/GetEventSpellTargetDest())
set udg_SPELL__Index = 0
set udg_SPELL__Event = 0.00
set udg_SPELL__Event = I2R(eventIndex[GetEventSpellEventType()])
set udg_SPELL__Event = 0.00
set udg_SPELL__Index = prevNode
call InitEventParams(/*
*/abilityId, /*
*/eventType, /*
*/orderType, /*
*/level, /*
*/triggerPlayer, /*
*/triggerUnit, /*
*/targetX, /*
*/targetY, /*
*/targetPoint, /*
*/targetUnit, /*
*/targetItem, /*
*/targetDest)
set triggerUnit = null
set targetPoint = null
set targetUnit = null
set targetItem = null
set targetDest = null
endmethod
implement SpellEventGeneric
/*=================================================================================*/
static method initTriggers takes nothing returns nothing
set udg_SPELL__RegisterHandler = CreateTrigger()
call TriggerAddAction(udg_SPELL__RegisterHandler, function thistype.registerSpell)
set udg_SPELL__InvokeEvent = CreateTrigger()
call TriggerAddAction(udg_SPELL__InvokeEvent, function thistype.onSpellInvoke)
set udg_SPELL__OverrideParams = CreateTrigger()
call TriggerAddAction(udg_SPELL__OverrideParams, function thistype.onOverrideParams)
set udg_SPELL__EnumerateTargetsInRange = CreateTrigger()
call TriggerAddAction(udg_SPELL__EnumerateTargetsInRange, function thistype.onEnumTargets)
endmethod
static method initConfiguration takes nothing returns nothing
set SPELL_PERIOD = 1.00/udg_SPELL__FRAME_RATE
set udg_SPELL__PERIOD = SPELL_PERIOD
set udg_SPELL__EVENT_CAST = EVENT_SPELL_CAST
set udg_SPELL__EVENT_CHANNEL = EVENT_SPELL_CHANNEL
set udg_SPELL__EVENT_EFFECT = EVENT_SPELL_EFFECT
set udg_SPELL__EVENT_ENDCAST = EVENT_SPELL_EFFECT
set udg_SPELL__EVENT_FINISH = EVENT_SPELL_FINISH
set udg_SPELL__ORDER_NO_TARGET = SPELL_ORDER_TYPE_NO_TARGET
set udg_SPELL__ORDER_POINT_TARGET = SPELL_ORDER_TYPE_POINT_TARGET
set udg_SPELL__ORDER_SINGLE_TARGET = SPELL_ORDER_TYPE_SINGLE_TARGET
set eventIndex[EVENT_SPELL_CAST] = 1
set eventIndex[EVENT_SPELL_CHANNEL] = 2
set eventIndex[EVENT_SPELL_EFFECT] = 3
set eventIndex[EVENT_SPELL_ENDCAST] = 4
set eventIndex[EVENT_SPELL_FINISH] = 5
endmethod
endstruct
static if DEBUG_MODE and LIBRARY_ErrorMessage then
private function OnRemoveLocation takes location whichLocation returns nothing
call ThrowError(whichLocation == staticLocation, SCOPE_PREFIX, "native RemoveLocation()", "", 0, "Destroyed a system-generated handle")
endfunction
hook RemoveLocation OnRemoveLocation
endif
public function Initialize takes nothing returns nothing
call SpellEventGUI.initTriggers()
call SpellEventGUI.initConfiguration()
set udg_SPELL__InitializationEvent = 1.00
set udg_SPELL__InitializationEvent = 0.00
endfunction
endlibrary
library SpellCloner /* v2.2.0 https://www.hiveworkshop.com/threads/324157/
*/uses /*
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/Table /* https://www.hiveworkshop.com/threads/188084/
*///! novjass
/*
CREDITS:
- AGD (Author)
- JAKEZINC (Feedbacks and suggestions, which helped bring the system into its current form)
*/
|=====|
| API |
|=====|
readonly static SpellCloner.configuredInstance/*
- Use this variable inside the configuration function to refer to the spell
instance being configured
*/module SpellClonerHeader/*
- Implement this module at the top of your spell struct
*/static method hasActivationAbility takes integer abilId returns boolean/*
*/static method hasConfiguration takes integer configStructId returns boolean/*
- Only call this from inside a spell event handler (generic or specific)
- Could be useful for especially in generic handlers to see if the casted
ability activates a certain type of spell, as cloned spell can have many
activation abilities
*/method initSpellConfiguration takes integer abilId returns integer/*
- Call this method at the top of you onSpellStart() method to initialize the
correct local configuration of your spell instance based on the activation
ability id
- Returns the struct type id of the struct containing the configuration
*/method loadSpellConfiguration takes integer configStructId returns nothing/*
- Call this method with the value returned by initSpellConfiguration() as the
parameter
- Like initSpellConfiguration(), loads the correct local configuration of the
spell, but based on the typeid of the configuration struct
*/module SpellClonerFooter/*
- Implement this module at the bottom of your spell struct, below your SpellEvent implementation
*/static method create takes integer configStructId, integer abilId, integer spellEventType, code configurationFunc returns thistype/*
- Creates a new local configuration instance for the spell (Return value is obsolete)
*/module SpellCloner/*
- A supplement to using both SpellClonerHeader and SpellClonerFotter. Implement this
module at the bottom of your spell struct, no need to implement the SpellEvent module
*/interface method onClonedSpellStart takes nothing returns thistype/*
*/interface method onClonedSpellPeriodic takes nothing returns boolean/*
*/interface method onClonedSpellEnd takes nothing returns nothing/*
- Supplement to the onSpellStart(), onSpellPeriodic(), and onSpellEnd() methods from
SpellEvent module
- All these interface methods follow the same rules as their SpellEvent interface methods
counterpart
- You no longer need to call this.initSpellConfiguration(ABIL_ID) on onClonedSpellStart()
to run your configuration function, this is already done internally by the system.
*/static method hasActivationAbility takes integer abilId returns boolean/*
*/static method hasConfiguration takes integer configStructId returns boolean/*
- Already defined above (see SpellClonerHeader module)
*/static method create takes integer configStructId, integer abilId, integer spellEventType, code configurationFunc returns thistype/*
- Creates a new local configuration instance for the spell (Return value is obsolete)
*///! endnovjass
globals
private trigger evaluator = CreateTrigger()
private integer array eventIndex
private integer configuredSpellInstance = 0
private TableArray table
private TableArray configStructNode
endglobals
private module Init
private static method onInit takes nothing returns nothing
set table = TableArray[JASS_MAX_ARRAY_SIZE]
set configStructNode = TableArray[JASS_MAX_ARRAY_SIZE]
set eventIndex[EVENT_SPELL_CAST] = 1
set eventIndex[EVENT_SPELL_CHANNEL] = 2
set eventIndex[EVENT_SPELL_EFFECT] = 3
set eventIndex[EVENT_SPELL_ENDCAST] = 4
set eventIndex[EVENT_SPELL_FINISH] = 5
endmethod
endmodule
struct SpellCloner extends array
static method operator configuredInstance takes nothing returns thistype
return configuredSpellInstance
endmethod
implement Init
endstruct
private struct SpellConfigList extends array
thistype current
readonly thistype prev
readonly thistype next
readonly integer structId
readonly boolexpr configExpr
private static thistype node = 0
method evaluateExpr takes integer spellInstance returns nothing
local integer prevInstance = configuredSpellInstance
set configuredSpellInstance = spellInstance
call TriggerAddCondition(evaluator, this.configExpr)
call TriggerEvaluate(evaluator)
call TriggerClearConditions(evaluator)
set configuredSpellInstance = prevInstance
endmethod
method insert takes integer id, boolexpr expr returns thistype
local thistype next = this.next
set node = node + 1
set node.structId = id
set node.configExpr = expr
set node.prev = this
set node.next = next
set next.prev = node
set this.next = node
return node
endmethod
static method create takes nothing returns thistype
set node = node + 1
set node.prev = node
set node.next = node
set node.current = node
return node
endmethod
endstruct
public function HasActivationAbility takes integer spellStructId, integer abilId returns boolean
if GetEventSpellEventType() == 0 then
return table[spellStructId*5 + 1].has(-abilId)
endif
return table[spellStructId*5 + eventIndex[GetEventSpellEventType()]].has(abilId)
endfunction
public function HasConfiguration takes integer spellStructId, integer configStructId returns boolean
if GetEventSpellEventType() == 0 then
return configStructNode[spellStructId*5 + 1].has(-configStructId)
endif
return configStructNode[spellStructId*5 + eventIndex[GetEventSpellEventType()]].has(configStructId)
endfunction
public function InitSpellConfiguration takes integer spellStructId, integer spellInstance, integer abilId returns integer
local integer configStructId
local SpellConfigList configList
if GetEventSpellEventType() == 0 then
set configList = table[spellStructId*5 + 1][-abilId]
else
set configList = table[spellStructId*5 + eventIndex[GetEventSpellEventType()]][abilId]
endif
set configList.current = configList.current.next
set configStructId = configList.current.structId
call configList.current.evaluateExpr(spellInstance)
if configList.current.next == configList then
set configList.current = configList
endif
return configStructId
endfunction
public function LoadSpellConfiguration takes integer spellStructId, integer spellInstance, integer configStructId returns nothing
if GetEventSpellEventType() == 0 then
call SpellConfigList(configStructNode[spellStructId*5 + 1][-configStructId]).evaluateExpr(spellInstance)
else
call SpellConfigList(configStructNode[spellStructId*5 + eventIndex[GetEventSpellEventType()]][configStructId]).evaluateExpr(spellInstance)
endif
endfunction
public function CloneSpell takes integer spellStructId, integer configStructId, integer abilId, integer eventType, code configFunc returns nothing
local SpellConfigList configList
local integer eventId = 0x10
local integer key
if eventType == 0 then
set key = spellStructId*5 + 1
if configStructNode[key][-configStructId] == 0 then
set configList = table[key][-abilId]
if configList == 0 then
set configList = SpellConfigList.create()
set table[key][-abilId] = configList
endif
set configStructNode[key][-configStructId] = configList.prev.insert(configStructId, Filter(configFunc))
endif
else
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
set key = spellStructId*5 + eventIndex[eventId]
if configStructNode[key][configStructId] == 0 then
set configList = table[key][abilId]
if configList == 0 then
set configList = SpellConfigList.create()
set table[key][abilId] = configList
endif
set configStructNode[key][configStructId] = configList.prev.insert(configStructId, Filter(configFunc))
endif
endif
set eventId = eventId/2
endloop
endif
endfunction
private module SpellClonerCommonHeader
static constant integer SPELL_ABILITY_ID = 0
static constant integer SPELL_EVENT_TYPE = 0
static method hasActivationAbility takes integer abilId returns boolean
return HasActivationAbility(thistype.typeid, abilId)
endmethod
static method hasConfiguration takes integer configStructId returns boolean
return HasConfiguration(thistype.typeid, configStructId)
endmethod
endmodule
module SpellClonerHeader
implement SpellClonerCommonHeader
method initSpellConfiguration takes integer abilId returns integer
return InitSpellConfiguration(thistype.typeid, this, abilId)
endmethod
method loadSpellConfiguration takes integer configStructId returns nothing
call LoadSpellConfiguration(thistype.typeid, this, configStructId)
endmethod
endmodule
module SpellClonerFooter
static method create takes integer configStructId, integer abilId, integer spellEventType, code configurationFunc returns thistype
call CloneSpell(thistype.typeid, configStructId, abilId, spellEventType, configurationFunc)
call registerSpellEvent(abilId, spellEventType)
return 0
endmethod
endmodule
module SpellCloner
implement SpellClonerCommonHeader
method onSpellStart takes nothing returns thistype
local integer configStructId = InitSpellConfiguration(thistype.typeid, this, GetEventSpellAbilityId())
local thistype node = this.onClonedSpellStart()
if node > 0 then
if node != this then
call SpellConfigList(configStructNode[thistype.typeid][configStructId]).evaluateExpr(node)
endif
return node
endif
return 0
endmethod
method onSpellPeriodic takes nothing returns boolean
return this.onClonedSpellPeriodic()
endmethod
method onSpellEnd takes nothing returns nothing
call this.onClonedSpellEnd()
endmethod
implement SpellEvent
implement SpellClonerFooter
endmodule
endlibrary
library Alloc /* v1.1.0 https://www.hiveworkshop.com/threads/324937/
*/uses /*
*/Table /* https://www.hiveworkshop.com/threads/188084/
*/optional ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
*///! novjass
/*
Written by AGD, based on MyPad's allocation algorithm
A allocator module using a single global indexed stack. Allocated values are
within the JASS_MAX_ARRAY_SIZE. No more need to worry about code bloats behind
the module implementation as it generates the least code possible (6 lines of
code in non-DEBUG_MODE), nor does it use an initialization function. This system
also only uses ONE variable (for the whole map) for the hashtable.
*/
|-----|
| API |
|-----|
/*
*/module GlobalAlloc/*
- Uses a single stack globally
*/module Alloc/*
- Uses a unique stack per struct
*/debug readonly boolean allocated/* Is node allocated?
*/static method allocate takes nothing returns thistype/*
*/method deallocate takes nothing returns nothing/*
*///! endnovjass
/*===========================================================================*/
globals
private key stack
endglobals
static if DEBUG_MODE then
private function AssertError takes boolean condition, string methodName, string structName, integer node, string message returns nothing
static if LIBRARY_ErrorMessage then
call ThrowError(condition, SCOPE_PREFIX, methodName, structName, node, message)
else
if condition then
call BJDebugMsg("[Library: " + SCOPE_PREFIX + "] [Struct: " + structName + "] [Method: " + methodName + "] [Instance: " + I2S(node) + "] : |cffff0000" + message + "|r")
endif
endif
endfunction
public function IsAllocated takes integer typeId, integer node returns boolean
return node > 0 and Table(stack)[typeId*JASS_MAX_ARRAY_SIZE + node] == 0
endfunction
endif
public function Allocate takes integer typeId returns integer
local integer offset = typeId*JASS_MAX_ARRAY_SIZE
local integer node = Table(stack)[offset]
local integer stackNext = Table(stack)[offset + node]
debug call AssertError(typeId < 0, "allocate()", Table(stack).string[-typeId], 0, "Invalid struct ID (" + I2S(typeId) + ")")
if stackNext == 0 then
debug call AssertError(node == (JASS_MAX_ARRAY_SIZE - 1), "allocate()", Table(stack).string[-typeId], node, "Overflow")
set node = node + 1
set Table(stack)[offset] = node
else
set Table(stack)[offset] = stackNext
set Table(stack)[offset + node] = 0
endif
return node
endfunction
public function Deallocate takes integer typeId, integer node returns nothing
local integer offset = typeId*JASS_MAX_ARRAY_SIZE
debug call AssertError(node == 0, "deallocate()", Table(stack).string[-typeId], 0, "Null node")
debug call AssertError(Table(stack)[offset + node] > 0, "deallocate()", Table(stack).string[-typeId], node, "Double-free")
set Table(stack)[offset + node] = Table(stack)[offset]
set Table(stack)[offset] = node
endfunction
module Alloc
debug method operator allocated takes nothing returns boolean
debug return IsAllocated(thistype.typeid, this)
debug endmethod
static method allocate takes nothing returns thistype
return Allocate(thistype.typeid)
endmethod
method deallocate takes nothing returns nothing
call Deallocate(thistype.typeid, this)
endmethod
debug private static method onInit takes nothing returns nothing
debug set Table(stack).string[-thistype.typeid] = "thistype"
debug endmethod
endmodule
module GlobalAlloc
debug method operator allocated takes nothing returns boolean
debug return IsAllocated(0, this)
debug endmethod
static method allocate takes nothing returns thistype
debug call AssertError(Table(stack)[0] == (JASS_MAX_ARRAY_SIZE - 1), "allocate()", "thistype", JASS_MAX_ARRAY_SIZE - 1, "Overflow")
return Allocate(0)
endmethod
method deallocate takes nothing returns nothing
debug call AssertError(this == 0, "deallocate()", "thistype", 0, "Null node")
debug call AssertError(Table(stack)[this] > 0, "deallocate()", "thistype", this, "Double-free")
call Deallocate(0, this)
endmethod
endmodule
endlibrary
library LinkedList /* v1.3.0 https://www.hiveworkshop.com/threads/linkedlist-modules.325635/
*/uses /*
*/optional ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
*///! novjass
/*
Author:
- AGD
Credits:
- Nestharus, Dirac, Bribe
> For their scripts and discussions which I used as reference
Pros:
- Feature-rich (Can be)
- Modular
- Safety-oriented (On DEBUG_MODE, but not 100% fool-proof ofcourse)
- Flexible (Does not enforce a built-in allocator - allows user to choose between a custom Alloc
or the default vjass allocator, or neither)
- Extensible (Provides interfaces)
Note:
If you are using using Dirac's 'LinkedListModule' library, you need to replace its contents with
the compatibility lib provided alongside this library for all to work seamlessly.
*/
|-----|
| API |
|-----|
/*
Note: All the fields except from 'prev' and 'next' are actually operators, so you might want to
avoid using them from the interface methods that would be declared above them.
=====================================================================================================
List Fields Modules (For those who want to write or inline the core linked-list operations themselves)
*/module LinkedListFields/*
*/readonly thistype prev/*
*/readonly thistype next/*
*/module StaticListFields extends LinkedListFields/*
*/readonly static constant thistype sentinel/*
*/readonly static thistype front/*
*/readonly static thistype back/*
*/readonly static boolean empty/*
*/module ListFields extends LinkedListFields/*
*/readonly thistype front/*
*/readonly thistype back/*
*/readonly boolean empty/*
=====================================================================================================
Lite List Modules (Should be enough for most cases)
*/module LinkedListLite extends LinkedListFields/*
*/optional interface static method onInsert takes thistype node returns nothing/*
*/optional interface static method onRemove takes thistype node returns nothing/*
*/optional interface method onTraverse takes thistype node returns boolean/*
*/static method insert takes thistype prev, thistype node returns nothing/*
*/static method remove takes thistype node returns nothing/*
*/method traverseForwards takes nothing returns nothing/*
*/method traverseBackwards takes nothing returns nothing/*
- Only present if onTraverse() is also present
*/module StaticListLite extends StaticListFields, LinkedListLite/*
*/static method pushFront takes thistype node returns nothing/*
*/static method popFront takes nothing returns thistype/*
*/static method pushBack takes thistype node returns nothing/*
*/static method popBack takes nothing returns thistype/*
*/module ListLite extends ListFields, LinkedListLite/*
*/method pushFront takes thistype node returns nothing/*
*/method popFront takes nothing returns thistype/*
*/method pushBack takes thistype node returns nothing/*
*/method popBack takes nothing returns thistype/*
*/module InstantiatedListLite extends ListLite/*
*/interface static method allocate takes nothing returns thistype/*
*/interface method deallocate takes nothing returns nothing/*
*/optional interface method onConstruct takes nothing returns nothing/*
*/optional interface method onDestruct takes nothing returns nothing/*
*/static method create takes nothing returns thistype/*
*/method destroy takes nothing returns nothing/*
=====================================================================================================
Standard List Modules
*/module LinkedList extends LinkedListLite/*
*/static method isLinked takes thistype node returns boolean/*
*/module StaticList extends StaticListLite, LinkedList/*
*/static method clear takes nothing returns nothing/*
*/static method flush takes nothing returns nothing/*
*/module List extends ListLite, LinkedList/*
*/static method makeHead takes thistype node returns nothing/*
*/method clear takes nothing returns nothing/*
*/method flush takes nothing returns nothing/*
*/module InstantiatedList extends InstantiatedListLite, List/*
=====================================================================================================
Feature-rich List Modules (For those who somehow need exotic linked-list operations)
*/module LinkedListEx extends LinkedList/*
*/static method move takes thistype prev, thistype node returns nothing/*
*/static method swap takes thistype nodeA, thistype nodeB returns nothing/*
*/module StaticListEx extends StaticList, LinkedListEx/*
*/static method contains takes thistype node returns boolean/*
*/static method getSize takes nothing returns integer/*
*/static method rotateLeft takes nothing returns nothing/*
*/static method rotateRight takes nothing returns nothing/*
*/module ListEx extends List, LinkedListEx/*
*/method contains takes thistype node returns boolean/*
*/method getSize takes nothing returns integer/*
*/method rotateLeft takes nothing returns nothing/*
*/method rotateRight takes nothing returns nothing/*
*/module InstantiatedListEx extends InstantiatedList, ListEx/*
*///! endnovjass
/*========================================= CONFIGURATION ===========================================
* Only affects DEBUG_MODE
* If false, throws warnings instead (Errors pauses the game (Or stops the thread) while warnings do not)
*/
globals
private constant boolean THROW_ERRORS = true
endglobals
/*====================================== END OF CONFIGURATION =====================================*/
static if DEBUG_MODE then
public function AssertError takes boolean condition, string methodName, string structName, integer node, string message returns nothing
static if LIBRARY_ErrorMessage then
static if THROW_ERRORS then
call ThrowError(condition, SCOPE_PREFIX, methodName, structName, node, message)
else
call ThrowWarning(condition, SCOPE_PREFIX, methodName, structName, node, message)
endif
else
if condition then
static if THROW_ERRORS then
call BJDebugMsg("|cffff0000[ERROR]|r [Library: " + SCOPE_PREFIX + "] [Struct: " + structName + "] [Method: " + methodName + "] [Instance: " + I2S(node) + "] : |cffff0000" + message + "|r")
call PauseGame(true)
else
call BJDebugMsg("|cffffcc00[WARNING]|r [Library: " + SCOPE_PREFIX + "] [Struct: " + structName + "] [Method: " + methodName + "] [Instance: " + I2S(node) + "] : |cffffcc00" + message + "|r")
endif
endif
endif
endfunction
endif
private module LinkedListUtils
method p_clear takes nothing returns nothing
set this.next.prev = 0
set this.prev.next = 0
set this.prev = this
set this.next = this
endmethod
method p_flush takes nothing returns nothing
local thistype node = this.prev
loop
exitwhen node == this
call remove(node)
set node = node.prev
endloop
endmethod
endmodule
private module LinkedListUtilsEx
implement LinkedListUtils
method p_contains takes thistype toFind returns boolean
local thistype node = this.next
loop
exitwhen node == this
if node == toFind then
return true
endif
set node = node.next
endloop
return false
endmethod
method p_getSize takes nothing returns integer
local integer count = 0
local thistype node = this.next
loop
exitwhen node == this
set count = count + 1
set node = node.next
endloop
return count
endmethod
endmodule
private module LinkedListLiteBase
implement LinkedListFields
debug method p_isEmptyHead takes nothing returns boolean
debug return this == this.next and this == this.prev
debug endmethod
static method p_insert takes thistype this, thistype node returns nothing
local thistype next = this.next
set node.prev = this
set node.next = next
set next.prev = node
set this.next = node
endmethod
static method p_remove takes thistype node returns nothing
set node.next.prev = node.prev
set node.prev.next = node.next
endmethod
static method insert takes thistype this, thistype node returns nothing
debug call AssertError(node == 0, "insert()", "thistype", 0, "Cannot insert null node")
debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "insert()", "thistype", 0, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
call p_insert(this, node)
static if thistype.onInsert.exists then
call onInsert(node)
endif
endmethod
static method remove takes thistype node returns nothing
debug call AssertError(node == 0, "remove()", "thistype", 0, "Cannot remove null node")
debug call AssertError(node.next.prev != node and node.prev.next != node, "remove()", "thistype", 0, "Invalid node [" + I2S(node) + "]")
static if thistype.onRemove.exists then
call onRemove(node)
endif
call p_remove(node)
endmethod
static if thistype.onTraverse.exists then
method p_traverse takes boolean forward returns nothing
local thistype node
local thistype next
debug local thistype prev
debug local boolean array traversed
if forward then
set node = this.next
loop
exitwhen node == this or node.prev.next != node
debug call AssertError(traversed[node], "traverseForwards()", "thistype", this, "A node was traversed twice in a single traversal")
debug set traversed[node] = true
debug set prev = node.prev
set next = node.next
if this.onTraverse(node) then
call remove(node)
debug set traversed[node] = false
debug elseif next.prev == prev then
debug set traversed[node] = false
endif
set node = next
endloop
else
set node = this.prev
loop
exitwhen node == this or node.next.prev != node
debug call AssertError(traversed[node], "traverseBackwards()", "thistype", this, "A node was traversed twice in a single traversal")
debug set traversed[node] = true
debug set prev = node.next
set next = node.prev
if this.onTraverse(node) then
call remove(node)
debug set traversed[node] = false
debug elseif next.prev == prev then
debug set traversed[node] = false
endif
set node = next
endloop
endif
endmethod
method traverseForwards takes nothing returns nothing
call this.p_traverse(true)
endmethod
method traverseBackwards takes nothing returns nothing
call this.p_traverse(false)
endmethod
endif
endmodule
private module LinkedListBase
implement LinkedListLiteBase
static method isLinked takes thistype node returns boolean
return node.next.prev == node or node.prev.next == node
endmethod
endmodule
module LinkedListFields
readonly thistype prev
readonly thistype next
endmodule
module LinkedListLite
implement LinkedListLiteBase
implement optional LinkedListLiteModuleCompatibility // For API compatibility with Dirac's 'LinkedListModule' library
endmodule
module LinkedList
implement LinkedListBase
implement optional LinkedListModuleCompatibility // For API compatibility with Dirac's 'LinkedListModule' library
endmodule
module LinkedListEx
implement LinkedListBase
static method p_move takes thistype prev, thistype node returns nothing
call p_remove(node)
call p_insert(prev, node)
endmethod
static method move takes thistype prev, thistype node returns nothing
debug call AssertError(not isLinked(node), "move()", "thistype", 0, "Cannot use unlinked node [" + I2S(node) + "]")
call p_move(prev, node)
endmethod
static method swap takes thistype this, thistype node returns nothing
local thistype thisPrev = this.prev
local thistype thisNext = this.next
debug call AssertError(this == 0, "swap()", "thistype", 0, "Cannot swap null node")
debug call AssertError(node == 0, "swap()", "thistype", 0, "Cannot swap null node")
debug call AssertError(not isLinked(this), "swap()", "thistype", 0, "Cannot use unlinked node [" + I2S(this) + "]")
debug call AssertError(not isLinked(node), "swap()", "thistype", 0, "Cannot use unlinked node [" + I2S(node) + "]")
call p_move(node, this)
if thisNext != node then
call p_move(thisPrev, node)
endif
endmethod
endmodule
module StaticListFields
implement LinkedListFields
static constant method operator head takes nothing returns thistype
return 0
endmethod
static method operator back takes nothing returns thistype
return head.prev
endmethod
static method operator front takes nothing returns thistype
return head.next
endmethod
static method operator empty takes nothing returns boolean
return front == head
endmethod
endmodule
module StaticListLite
implement StaticListFields
implement LinkedListLiteBase
static method pushFront takes thistype node returns nothing
debug call AssertError(node == 0, "pushFront()", "thistype", 0, "Cannot use null node")
debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "pushFront()", "thistype", 0, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
call insert(head, node)
endmethod
static method popFront takes nothing returns thistype
local thistype node = front
debug call AssertError(node.prev != head, "popFront()", "thistype", 0, "Invalid list")
call remove(node)
return node
endmethod
static method pushBack takes thistype node returns nothing
debug call AssertError(node == 0, "pushBack()", "thistype", 0, "Cannot use null node")
debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "pushBack()", "thistype", 0, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
call insert(back, node)
endmethod
static method popBack takes nothing returns thistype
local thistype node = back
debug call AssertError(node.next != head, "popBack()", "thistype", 0, "Invalid list")
call remove(node)
return node
endmethod
endmodule
module StaticList
implement StaticListLite
implement LinkedListBase
implement LinkedListUtils
static method clear takes nothing returns nothing
call head.p_clear()
endmethod
static method flush takes nothing returns nothing
call head.p_flush()
endmethod
endmodule
module StaticListEx
implement StaticList
implement LinkedListEx
implement LinkedListUtilsEx
static method contains takes thistype node returns boolean
return head.p_contains(node)
endmethod
static method getSize takes nothing returns integer
return head.p_getSize()
endmethod
static method rotateLeft takes nothing returns nothing
call p_move(back, front)
endmethod
static method rotateRight takes nothing returns nothing
call p_move(head, back)
endmethod
endmodule
module ListFields
implement LinkedListFields
method operator back takes nothing returns thistype
return this.prev
endmethod
method operator front takes nothing returns thistype
return this.next
endmethod
method operator empty takes nothing returns boolean
return this.next == this
endmethod
endmodule
module ListLite
implement ListFields
implement LinkedListLiteBase
method pushFront takes thistype node returns nothing
debug call AssertError(this == 0, "pushFront()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "pushFront()", "thistype", this, "Invalid list")
debug call AssertError(node == 0, "pushFront()", "thistype", this, "Cannot insert null node")
debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "pushFront()", "thistype", this, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
call insert(this, node)
endmethod
method popFront takes nothing returns thistype
local thistype node = this.next
debug call AssertError(this == 0, "popFront()", "thistype", 0, "Null list")
debug call AssertError(node.prev != this, "popFront()", "thistype", this, "Invalid list")
call remove(node)
return node
endmethod
method pushBack takes thistype node returns nothing
debug call AssertError(this == 0, "pushBack()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "pushBack()", "thistype", this, "Invalid list")
debug call AssertError(node == 0, "pushBack()", "thistype", this, "Cannot insert null node")
debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "pushBack()", "thistype", this, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
call insert(this.prev, node)
endmethod
method popBack takes nothing returns thistype
local thistype node = this.prev
debug call AssertError(this == 0, "popBack()", "thistype", 0, "Null list")
debug call AssertError(node.next != this, "pushFront()", "thistype", this, "Invalid list")
call remove(node)
return node
endmethod
endmodule
module List
implement ListLite
implement LinkedListBase
implement LinkedListUtils
static method makeHead takes thistype node returns nothing
set node.prev = node
set node.next = node
endmethod
method clear takes nothing returns nothing
debug call AssertError(this == 0, "clear()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "clear()", "thistype", this, "Invalid list")
call this.p_clear()
endmethod
method flush takes nothing returns nothing
debug call AssertError(this == 0, "flush()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "flush()", "thistype", this, "Invalid list")
call this.p_flush()
endmethod
endmodule
module ListEx
implement List
implement LinkedListEx
implement LinkedListUtilsEx
method contains takes thistype node returns boolean
debug call AssertError(this == 0, "contains()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "contains()", "thistype", this, "Invalid list")
return this.p_contains(node)
endmethod
method getSize takes nothing returns integer
debug call AssertError(this == 0, "getSize()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "getSize()", "thistype", this, "Invalid list")
return this.p_getSize()
endmethod
method rotateLeft takes nothing returns nothing
debug call AssertError(this == 0, "rotateLeft()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "rotateLeft()", "thistype", this, "Invalid list")
call p_move(this.back, this.front)
endmethod
method rotateRight takes nothing returns nothing
debug call AssertError(this == 0, "rotateRight()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev == this, "rotateRight()", "thistype", this, "Invalid list")
call p_move(this, this.back)
endmethod
endmodule
module InstantiatedListLite
implement ListLite
debug private boolean valid
static method create takes nothing returns thistype
local thistype node = allocate()
set node.prev = node
set node.next = node
debug set node.valid = true
static if thistype.onConstruct.exists then
call node.onConstruct()
endif
return node
endmethod
method destroy takes nothing returns nothing
debug call AssertError(this == 0, "destroy()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "destroy()", "thistype", this, "Invalid list")
debug call AssertError(not this.valid, "destroy()", "thistype", this, "Double-free")
debug set this.valid = false
static if thistype.flush.exists then
call this.flush()
endif
static if thistype.onDestruct.exists then
call this.onDestruct()
endif
debug set this.prev = 0
debug set this.next = 0
call this.deallocate()
endmethod
endmodule
module InstantiatedList
implement List
implement InstantiatedListLite
endmodule
module InstantiatedListEx
implement ListEx
implement InstantiatedList
endmodule
endlibrary
//! novjass
|-------------------|
| API Documentation |
|-------------------|
/*
v1.3.0
LinkedList library
If you are looking for a comprehensive listing of API available for each module, see the header of the
library itself. What you can find here is the description of ALL avaiable API.
The library supports 4 different implementations of linked-list:
- Free lists, where you have freedom in linking and dealing with nodes
without being restricted by the concept of a 'head node' or a 'container node'
- Static Lists, which turns a struct into a single list, with the 'head node'
representing the list itself
- Non-static Lists, which allows you to have multiple lists within a struct
- Instantiated Lists, similar to non-static lists but comes with its own methods
for creating and destroying a list (requires allocate() and deallocate() in the
implementing struct)
Note: In all these kind of lists, all nodes should be unique together with the head nodes.
Lists can also be circular or linear.
|-----------------|
| STATIC LIST API |
|-----------------|
Interfaces:
*/optional interface static method onInsert takes thistype node returns nothing/*
*/optional interface static method onRemove takes thistype node returns nothing/*
- onInsert() is called after a node is inserted everytime insert(), pushFront(),
or pushBack() is called
- onRemove() is called before a node is removed everytime remove(), popFront(),
or popBack() is called
- <node> is the node being inserted/removed
*/optional interface method onTraverse takes thistype node returns boolean/*
- Runs in response to traverseForwards()/traverseBackwards() calls
- <this> is the head node
- <node> is the currently traversed node
- Returning <true> removes <node> from the list <this>
Fields:
*/readonly thistype prev/*
*/readonly thistype next/*
*/readonly static thistype front/*
*/readonly static thistype back/*
*/static constant thistype head/*
*/readonly static boolean empty/*
- <front>, <back>, <head>, and <empty> are method operators
Methods:
*/static method isLinked takes thistype node returns boolean/*
- Checks if a node is currently belongs to a list
*/static method move takes thistype prev, thistype node returns nothing/*
- Moves <node> next to <prev>
- Only works if <node> is already linked
*/static method swap takes thistype nodeA, thistype nodeB returns nothing/*
- Swaps the placement of two nodes
*/static method contains takes thistype node returns boolean/*
- Checks if <head> contains the given node
*/static method getSize takes nothing returns integer/*
- Gets the size of the list <head>
- Time complexity: O(n)
*/static method traverseForwards takes nothing returns nothing/*
*/static method traverseBackwards takes nothing returns nothing/*
- Traverses a list forwards/backwards and calls onTraverse() for
each node in the list
*/static method rotateLeft takes nothing returns nothing/*
*/static method rotateRight takes nothing returns nothing/*
*/static method insert takes thistype prev, thistype node returns nothing/*
*/static method remove takes thistype node returns nothing/*
*/static method pushFront takes thistype node returns nothing/*
- Inlines to insert() if not on DEBUG_MODE
*/static method popFront takes nothing returns thistype/*
*/static method pushBack takes thistype node returns nothing/*
- Inlines to insert() if not on DEBUG_MODE
*/static method popBack takes nothing returns thistype/*
*/static method clear takes nothing returns nothing/*
- Does not call remove() for any node, but only unlinks them from the list
*/static method flush takes nothing returns nothing/*
- Calls remove() for each node on the list starting from the front to the back node
|---------------------|
| NON-STATIC LIST API |
|---------------------|
*/optional interface static method onInsert takes thistype node returns nothing/*
*/optional interface static method onRemove takes thistype node returns nothing/*
- onInsert() is called after a node is inserted everytime insert(), pushFront(),
or pushBack() is called
- onRemove() is called before a node is removed everytime remove(), popFront(),
or popBack() is called
- <node> is the node being inserted/removed
*/optional interface method onConstruct takes nothing returns nothing/*
*/optional interface method onDestruct takes nothing returns nothing/*
- This methods will be called when calling create()/destroy()
- <this> refers to the list to be created/destroyed
*/interface static method allocate takes nothing returns thistype/*
- The value returned by this method will be the value returned by create()
*/interface method deallocate takes nothing returns nothing/*
- This method will be called when calling destroy()
*/optional interface method onTraverse takes thistype node returns boolean/*
- Runs in response to traverseForwards()/traverseBackwards() calls
- <this> is the head node
- <node> is the currently traversed node
- Returning <true> removes <node> from the list <this>
Fields:
*/readonly thistype prev/*
*/readonly thistype next/*
*/readonly thistype front/*
*/readonly thistype back/*
*/readonly boolean empty/*
- <front>, <back>, and <empty> are method operators
Methods:
*/static method isLinked takes thistype node returns boolean/*
- Checks if a node is currently belongs to a list
*/static method move takes thistype prev, thistype node returns nothing/*
- Moves <node> next to <prev>
- Only works if <node> is already linked
*/static method swap takes thistype nodeA, thistype nodeB returns nothing/*
- Swaps the placement of two nodes
*/method contains takes thistype node returns boolean/*
- Checks if <head> contains the given node
*/method getSize takes nothing returns integer/*
- Gets the size of the list <head>
- Time complexity: O(n)
*/method traverseForwards takes nothing returns nothing/*
*/method traverseBackwards takes nothing returns nothing/*
- traverses a list forwards/backwards and calls onTraverse() for
each node in the list
*/method rotateLeft takes nothing returns nothing/*
*/method rotateRight takes nothing returns nothing/*
*/static method makeHead takes thistype node returns nothing/*
- Turns a node into a list
*/static method insert takes thistype prev, thistype node returns nothing/*
*/static method remove takes thistype node returns nothing/*
*/method pushFront takes thistype node returns nothing/*
- Inlines to insert() if not on DEBUG_MODE
*/method popFront takes nothing returns thistype/*
*/method pushBack takes thistype node returns nothing/*
- Inlines to insert() if not on DEBUG_MODE
*/method popBack takes nothing returns thistype/*
*/method clear takes nothing returns nothing/*
- Does not call remove() for any node, but only unlinks them from the list
*/method flush takes nothing returns nothing/*
- Calls remove() for each node on the list starting from the front to the back node
*/static method create takes nothing returns thistype/*
- Creates a new list
*/method destroy takes nothing returns nothing/*
- Destroys the list (Also calls flush internally for InstantiatedList and InstantiatedListEx)
*///! endnovjass
/*
* For compatibility with Dirac's LinkedListModule
*
* Note:
* No changes needed to be made in your old code that's using Dirac's 'LinkedListModule'.
* Just replace his 'LinkedListModule' library in your map with this one.
*/
library LinkedListModule
module LinkedListModuleCompatibility
implement LinkedListLiteModuleCompatibility
static method createNode takes nothing returns thistype
local thistype node = allocate()
//! runtextmacro LINKED_LIST_HEAD("node")
return node
endmethod
method insertNode takes thistype node returns nothing
call insert(this, node)
endmethod
method removeNode takes nothing returns nothing
call remove(this)
endmethod
method clearNode takes nothing returns nothing
//! runtextmacro LINKED_LIST_CLEAR("this")
endmethod
method flushNode takes nothing returns nothing
//! runtextmacro LINKED_LIST_FLUSH("this")
endmethod
endmodule
module LinkedListLiteModuleCompatibility
private static integer instanceCount = 0
private static thistype _base = JASS_MAX_ARRAY_SIZE - 3
boolean head
static method operator base takes nothing returns thistype
if not _base.head then
set _base.prev = _base
set _base.next = _base
set _base.head = true
endif
return _base
endmethod
static method allocate takes nothing returns thistype
local thistype this = thistype(base + 1).prev
if this == 0 then
debug if instanceCount == base then
debug call BJDebugMsg("[Linked List]: Overflow")
debug return 0
debug endif
set instanceCount = instanceCount + 1
return instanceCount
endif
set thistype(base + 1).prev = this.prev
return this
endmethod
method deallocate takes nothing returns nothing
set this.prev = thistype(base + 1).prev
set thistype(base + 1).prev = this
set this.head = false
endmethod
endmodule
//! textmacro LINKED_LIST_HEAD takes node
set $node$.next = $node$
set $node$.prev = $node$
set $node$.head = true
//! endtextmacro
//! textmacro LINKED_LIST_CLEAR takes node
if $node$!=$node$.next then
set $node$.next.prev = thistype(base + 1).prev
set thistype(base + 1).prev = $node$.prev
set $node$.next = $node$
set $node$.prev = $node$
endif
//! endtextmacro
//! textmacro LINKED_LIST_FLUSH takes node
set $node$.next.prev = thistype(base + 1).prev
set thistype(base + 1).prev = $node$
set $node$.head = false
//! endtextmacro
//! textmacro LINKED_LIST_INSERT takes node, toInsert
set $node$.prev.next = $toInsert$
set $toInsert$.prev = $node$.prev
set $node$.prev = $toInsert$
set $toInsert$.next = $node$
//! endtextmacro
//! textmacro LINKED_LIST_REMOVE takes node
set $node$.prev.next = $node$.next
set $node$.next.prev = $node$.prev
//! endtextmacro
//! textmacro LINKED_LIST_MERGE takes nodeA, nodeB
set $nodeA$.next.prev = $nodeB$.prev
set $nodeB$.prev.next = $nodeA$.next
set $nodeA$.next = $nodeB$
set $nodeB$.prev = $nodeA$
//! endtextmacro
endlibrary
//! novjass
/*
This folder contains templates for spell-making using the various components of the SpellFramework Library.
Multiple templates are given to accomodate the varying design categories of spells.
|==================================================================================|
| These are six (6) templates provided for making spells using the SpellEvent ONLY |
|==================================================================================|
Spell Template A:
- In this template, we used the simplest form of spell as an example. The spell has no periodic operations
and immediately performs its effects after cast.
Spell Template B:
- Uses the most common form of spell that we usually see in public spell resource submissions. The spell
has a setup section (onSpellStart), periodic section (onSpellPeriodic), and a cleanup section (onSpellEnd).
The spell has no child components and can easily be described using a single node that is created after a
spell is cast.
Spell Template C:
- Similar to Spell Template B but is more flexible. It automatically detects if the spell must be 'channeling'
based on the value of <SPELL_EVENT_TYPE> in the configuration by checking if it is equal to <EVENT_SPELL_CHANNEL
+ EVENT_SPELL_ENDCAST>. Any other event combination will turn the spell into non-channeling.
The template shows how the framework easily allows the developer to make the method of invoking the spell
(in this case, whether the spell is channeled or not) configurable by the end users.
Spell Template D:
- The featured spell is another common format seen in public submissions. The spell has child components
(could be one or more levels) that together, make up the overall characteristic of the spell. When the
primary node (the spell instance) expires, all existing/remaining children nodes are destroyed alongside
it.
Spell Template E:
- The featured spell is a bit similar to the one in Spell Template D. However, there are times when we want
the children nodes to outlive the primary node. We can usually see this in spells that involve summoning
a separate entity that in turn summons sub-entities over time. For example, a Turret summoning spell. The
summoned Turret will periodically launch missiles towards enemy units within a certain radius. When the
Turret expires, we don't want the remaining missiles still travelling to disappear also. Therefore, the
missiles themselves need to be totally independent from their parent/primary node.
Spell Template F:
- The featured spell is not so common but still warrants a template :D. It usually has no significant
operations concerning its primary node except that it creates multiple child components at the time of cast.
Its components are usually the ones that provide the overall effect of the spell themselves. Due to this,
we can easily disregard the primary node (not include it to the list for which the periodic operations run)
and let its children do all the work.
|================================================================================================|
| These are three (3) templates provided for making spells using BOTH SpellEvent AND SpellCloner |
|================================================================================================|
Cloneable Spell Template A:
- Here it feature a spell similar to that in Spell Template B, but using SpellCloner. The method of cloning
here can also be applied to other spells featured in Spell Templates A, D, & E.
Cloneable Spell Template B:
- Featured spell is similar to that used in Spell Template F, but using SpellCloner.
Cloneable Spell Template C:
- Featured spell is similar to that used in Cloneable Spell Template A, but only uses the 'SpellCloner'
module unlike the usual 'SpellClonerHeader' + 'SpellClonerFooter' + 'SpellEvent' combination. This method
also provides better automation when it comes to running the spells configuration setup.
*/
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/
/*****************************************************************
* SPELL CONFIGURATION *
*****************************************************************/
private module SpellConfiguration
static constant integer SPELL_ABILITY_ID = 'XXXX'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
public struct <SpellName> extends array
implement SpellConfiguration
effect spellSfx
...
private static method onSpellStart takes nothing returns thistype
local thistype node
if <InvalidCastCondition> then
return 0 // Does not run onSpellEnd()
endif
set node = ...
set node.spellSfx = AddSpecialEffectTarget(...)
...
return node
endmethod
private method onSpellEnd takes nothing returns nothing
call DestroyEffect(this.spellSfx)
...
set this.spellSfx = null
endmethod
implement SpellEventEx
endstruct
endlibrary
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/
/*****************************************************************
* SPELL CONFIGURATION *
*****************************************************************/
private module SpellConfiguration
static constant integer SPELL_ABILITY_ID = 'XXXX'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
static constant real SPELL_PERIOD = 1.00/32.00
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
public struct <SpellName> extends array
implement SpellConfiguration
unit dummy
effect spellSfx
...
private method onSpellStart takes nothing returns thistype
if <InvalidCastCondition> then
return 0
endif
set this.dummy = CreateUnit(...)
set this.spellSfx = AddSpecialEffectTarget(..., this.dummy, ...)
...
return this
endmethod
private method onSpellPeriodic takes nothing returns boolean
...
return <EndPeriodicCondition> // Return true to end the periodic process and proceed to onSpellEnd()
endmethod
private method onSpellEnd takes nothing returns nothing
call DestroyEffect(this.spellSfx)
call UnitApplyTimedLife(this.dummy, 'BTLF', SFX_DEATH_TIME)
...
set this.spellSfx = null
set this.dummy = null
endmethod
implement SpellEvent
endstruct
endlibrary
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/UnitDex /*
*/
/*****************************************************************
* SPELL CONFIGURATION *
*****************************************************************/
private module SpellConfiguration
static constant integer SPELL_ABILITY_ID = 'XXXX'
/*
* Any other event combination will make the spell non-channeling
*/
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_CHANNEL + EVENT_SPELL_ENDCAST
static constant real SPELL_PERIOD = 1.00/32.00
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
/*
* Note: For seamless manipulation of channeling ability, I suggest using
* an ability based on CHANNEL then be sure to set its 'Follow through
* time' field into a really high value such as <99999> and set the
* 'Disable other abilities' to false.
* This allows you to configure the channeling (follow through time) duration
* in the code instead of using the Object Editor.
*/
private constant function FollowThroughTime takes integer level returns real
return 10.00 + 0.00*level
endfunction
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
private constant function IsSpellChanneling takes nothing returns boolean
return SPELL_EVENT_TYPE == EVENT_SPELL_CHANNEL + EVENT_SPELL_ENDCAST
endfunction
public struct <SpellName> extends array
implement SpellConfiguration
unit dummy
effect spellSfx
real followThroughTime
integer unitId
...
private static thistype array channelingInstance
private method onSpellStart takes nothing returns thistype
local integer unitId
if <InvalidCastCondition> then
return 0
endif
if IsSpellChanneling() then
set unitId = GetUnitId(Spell.triggerUnit)
/*
* We do this to limit the channeling instance to only one per unit
*/
if channelingInstance[unitId] == 0 then
set channelingInstance[unitId] = this
set this.unitId = unitId
else
set this = channelingInstance[unitId]
endif
if Spell.eventType == EVENT_SPELL_ENDCAST then
/*
* This block executes when a channeling spell ends, whether because
* it was finished or was interrupted
*
* Returning a negative value will remove the positive equivalent node
* and call onSpellEnd() for that node if it exists in the list
*/
return -this
endif
endif
set this.dummy = CreateUnit(...)
set this.spellSfx = AddSpecialEffectTarget(..., this.dummy, ...)
set this.followThroughTime = FollowThroughTime(Spell.level)
...
return this
endmethod
/*
* Notice that onSpellPeriodic() stops executing when the caster stops channeling,
* either when the channeling is finished or is interrupted
*/
private method onSpellPeriodic takes nothing returns boolean
...
if IsSpellChanneling() then
/*
* Spell is channeling
*/
set this.followThroughTime = this.followThroughTime - SPELL_PERIOD
if not (this.followThroughTime > 0.00 and <ContinueChannelingCondition>) then
/*
* If it is time to end the spell, instead of manually returning true, we
* order the caster to "stop" to trigger onSpellStart() which will in turn
* stop the periodic proccess after detecting that the event is EVENT_SPELL_ENDCAST
*/
call IssueImmediateOrderById(GetUnitById(this.unitId), STOP_ORDER_ID)
endif
return false
endif
/*
* Spell is non-channeling
*/
return <EndPeriodicCondition> // Return true to end the periodic process and proceed to onSpellEnd()
endmethod
private method onSpellEnd takes nothing returns nothing
local boolean finished = IsSpellChanneling() and this.followThroughTime <= 0.00
if finished then
/*
* The channel was finished
*/
...
else
/*
* The channel was interrupted
*/
...
endif
call DestroyEffect(this.spellSfx)
call UnitApplyTimedLife(this.dummy, 'BTLF', SFX_DEATH_TIME)
...
set this.spellSfx = null
set this.dummy = null
...
set channelingInstance[this.unitId] = 0
endmethod
implement SpellEvent
endstruct
endlibrary
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/
/*****************************************************************
* SPELL CONFIGURATION *
*****************************************************************/
private module SpellConfiguration
static constant integer SPELL_ABILITY_ID = 'XXXX'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
static constant real SPELL_PERIOD = 1.00/32.00
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
private struct Node extends array
implement Alloc
endstruct
private struct SpellComponent extends array
...
static method create takes ... returns thistype
local thistype node = Node.allocate()
...
return node
endmethod
method destroy takes nothing returns nothing
...
call Node(this).deallocate()
endmethod
endstruct
private struct SpellComponentList extends array
method operator component takes nothing returns SpellComponent
return this
endmethod
private static method onRemove takes thistype node returns nothing
call node.component.destroy()
endmethod
private static method allocate takes nothing returns thistype
return Node.allocate()
endmethod
private method deallocate takes nothing returns nothing
call Node(this).deallocate()
endmethod
implement InstantiatedList // Refer to the LinkedList library's documentation to know the above methods
// Or you can use your own linked list
endstruct
public struct <SpellName> extends array
implement SpellConfiguration
unit dummy
effect spellSfx
SpellComponentList componentList
...
private method onSpellStart takes nothing returns thistype
if <InvalidCastCondition> then
return 0
endif
set this.dummy = CreateUnit(...)
set this.spellSfx = AddSpecialEffectTarget(..., this.dummy, ...)
...
set this.componentList = SpellComponentList.create()
return this
endmethod
private method onSpellPeriodic takes nothing returns boolean
local thistype node
...
if <CreateComponentCondition> then
set node = SpellComponent.create(...)
...
call this.componentList.pushBack(node)
endif
set node = this.componentList.next
loop
exitwhen node == this.componentList
...
if <DestroyComponentCondition> then
call node.remove()
endif
set node = node.next
endloop
...
return <EndPeriodicCondition>
endmethod
private method onSpellEnd takes nothing returns nothing
// Destroy remaining component nodes
call this.componentList.destroy() // Calls flush() which in turn calls remove() for each node on the list
call DestroyEffect(this.spellSfx)
call UnitApplyTimedLife(this.dummy, 'BTLF', SFX_DEATH_TIME)
...
set this.spellSfx = null
set this.dummy = null
endmethod
implement SpellEvent
endstruct
endlibrary
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/
/*****************************************************************
* SPELL CONFIGURATION *
*****************************************************************/
private module SpellConfiguration
static constant integer SPELL_ABILITY_ID = 'XXXX'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
static constant real SPELL_PERIOD = 1.00/32.00
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
private struct Node extends array
implement Alloc
endstruct
private struct SpellComponent extends array
...
static method create takes ... returns thistype
local thistype node = Node.allocate()
...
return node
endmethod
method destroy takes nothing returns nothing
...
call Node(this).deallocate()
endmethod
endstruct
public struct <SpellName> extends array
implement SpellConfiguration
unit dummy
effect spellSfx
boolean primaryNode
...
private method operator component takes nothing returns SpellComponent
return this
endmethod
private method onSpellStart takes nothing returns thistype
if <InvalidCastCondition> then
return 0
endif
set this = Node.allocate()
set this.dummy = CreateUnit(...)
set this.spellSfx = AddSpecialEffectTarget(..., this.dummy, ...)
...
set this.primaryNode = true
return this
endmethod
private method onSpellPeriodic takes nothing returns boolean
local thistype node
...
if this.primaryNode then
if <CreateComponentCondition> then
set node = SpellComponent.create(...)
set node.primaryNode = false
set node.next = 0
set node.prev = thistype(0).prev
set thistype(0).prev.next = node
set thistype(0).prev = node
endif
...
return <EndPeriodicCondition>
else
...
/*
* No longer need to manually remove component node from list - it
* will automatically be removed upon returning true
*/
return <DestroyComponentCondition>
endif
endmethod
private method onSpellEnd takes nothing returns nothing
if this.primaryNode then
set this.primaryNode = false
call DestroyEffect(this.spellSfx)
call UnitApplyTimedLife(this.dummy, 'BTLF', SFX_DEATH_TIME)
...
set this.spellSfx = null
set this.dummy = null
call Node(this).deallocate()
else
call this.component.destroy()
endif
endmethod
implement SpellEvent
endstruct
endlibrary
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/
/*****************************************************************
* SPELL CONFIGURATION *
*****************************************************************/
private module SpellConfiguration
static constant integer SPELL_ABILITY_ID = 'XXXX'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
static constant real SPELL_PERIOD = 1.00/32.00
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
private constant function SummonCount takes integer level returns integer
return 5 + 5*level
endfunction
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
private struct Node extends array
implement Alloc
endstruct
private struct SpellComponent extends array
...
static method create takes ... returns thistype
local thistype node = Node.allocate()
...
return node
endmethod
method destroy takes nothing returns nothing
...
call Node(this).deallocate()
endmethod
endstruct
public struct <SpellName> extends array
implement SpellConfiguration
...
private method operator component takes nothing returns SpellComponent
return this
endmethod
private method onSpellStart takes nothing returns thistype
local integer count
local thistype node
if not <InvalidCastCondition> then
set count = SummonCount(GetEventSpellLevel())
if count > 0 then
loop
exitwhen count == 0
set node = SpellComponent.create(...)
...
set node.next = 0
set node.prev = thistype(0).prev
set thistype(0).prev.next = node
set thistype(0).prev = node
set count = count - 1
endloop
...
endif
endif
return 0
endmethod
private method onSpellPeriodic takes nothing returns boolean
...
/*
* Again, no need to manually remove the component node, it will
* automatically be removed from the list when returning true.
*/
return <EndPeriodicCondition>
endmethod
private method onSpellEnd takes nothing returns nothing
...
call this.component.destroy()
endmethod
implement SpellEvent
endstruct
endlibrary
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/
/*****************************************************************
* GLOBAL SPELL CONFIGURATION *
*****************************************************************/
private module GlobalSpellConfiguration
static constant real SPELL_PERIOD = 1.00/32.00
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF GLOBAL SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
public struct <SpellName> extends array
implement GlobalSpellConfiguration
implement SpellClonerHeader
string staticSfxModel
string staticSfxAttachPoint
real spellDuration
...
private method onSpellStart takes nothing returns thistype
call this.initSpellConfiguration(GetEventSpellAbilityId())
if <InvalidCastCondition> then
return 0
endif
...
return this
endmethod
private method onSpellPeriodic takes nothing returns boolean
...
set this.spellDuration = this.spellDuration - SPELL_PERIOD
return this.spellDuration <= 0.00 or <OtherEndPeriodicCondition>
endmethod
private method onSpellEnd takes nothing returns nothing
...
endmethod
implement SpellEvent
implement SpellClonerFooter
endstruct
/*
* This is the module that you will implement into the struct that you wish to
* contain the local spell configurations
*/
module <SpellName>Configuration
private static method configHandler takes nothing returns nothing
/*
* This configuration setup is run whenever <node>.initSpellConfiguration(ABIL_ID) or
* <node>.loadSpellConfiguration(CONFIG_ID)
*/
local <SpellName> node = SpellCloner.configuredInstance
set node.staticSfxModel = STATIC_SFX_MODEL
set node.staticSfxAttachPoint = STATIC_SFX_ATTACHPOINT
...
set node.spellDuration = spellDuration(GetEventSpellLevel())
...
endmethod
private static method onInit takes nothing returns nothing
call <SpellName>.create(thistype.typeid, SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.configHandler)
endmethod
endmodule
endlibrary
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/
/*****************************************************************
* GLOBAL SPELL CONFIGURATION *
*****************************************************************/
private module GlobalSpellConfiguration
static constant real SPELL_PERIOD = 1.00/32.00
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
private constant function SummonCount takes integer level returns integer
return 5 + 5*level
endfunction
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF GLOBAL SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
private struct Node extends array
implement Alloc
endstruct
private struct SpellComponent extends array
...
static method create takes ... returns thistype
local thistype node = Node.allocate()
...
return node
endmethod
method destroy takes nothing returns nothing
...
call Node(this).deallocate()
endmethod
endstruct
public struct <SpellName> extends array
implement GlobalSpellConfiguration
implement SpellClonerHeader
real summonedNodeDuration
...
private method operator component takes nothing returns SpellComponent
return this
endmethod
private method onSpellStart takes nothing returns thistype
/*
* It is important that you only call initSpellConfiguration() once per cast because
* sometimes, it is possible that a similar activation abilityId is assigned to multiple
* configuration struct, in which case, whenever the ability is cast, onSpellStart() will
* run for each configuration struct. Calling initSpellConfiguration(ABIL_ID) will move
* its internal configuration struct iterator to the next one in the list of configurations
* belonging to <ABIL_ID>. Therefore, we just have to save current configuration struct ID
* (The return value) so we can load it in the loop later below.
*
* Note: The 'this' below is useless. You can use an arbitrary instance or even 'thistype(0)'.
*/
local integer configurationId = this.initSpellConfiguration(GetEventSpellAbilityId())
local integer count
local thistype node
if not <InvalidCastCondition> then
set count = SummonCount(GetEventSpellLevel())
if count > 0 then
loop
exitwhen count == 0
set node = SpellComponent.create(...)
call node.loadSpellConfiguration(configurationId)
set node.next = 0
set node.prev = thistype(0).prev
set thistype(0).prev.next = node
set thistype(0).prev = node
...
set count = count - 1
endloop
...
endif
endif
return 0
endmethod
private method onSpellPeriodic takes nothing returns boolean
...
set this.summonedNodeDuration = this.summonedNodeDuration - SPELL_PERIOD
return this.summonedNodeDuration <= 0.00 or <OtherEndPeriodicCondition>
endmethod
private method onSpellEnd takes nothing returns nothing
...
call this.component.destroy()
endmethod
implement SpellEvent
implement SpellClonerFooter
endstruct
/*
* This is the module that you will implement into the struct that you wish to
* contain the local spell configurations
*/
module <SpellName>Configuration
private static method configHandler takes nothing returns nothing
/*
* This configuration setup is run whenever <node>.initSpellConfiguration(ABIL_ID) or
* <node>.loadSpellConfiguration(CONFIG_ID)
*/
local <SpellName> node = SpellCloner.configuredInstance
...
set node.summonedNodeDuration = summonDuration(GetEventSpellLevel())
...
endmethod
private static method onInit takes nothing returns nothing
call <SpellName>.create(thistype.typeid, SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.configHandler)
endmethod
endmodule
endlibrary
//! endnovjass
//! novjass
library <SpellName> /*
*/uses /*
*/SpellFramework /*
*/
/*****************************************************************
* GLOBAL SPELL CONFIGURATION *
*****************************************************************/
private module GlobalSpellConfiguration
static constant real SPELL_PERIOD = 1.00/32.00
static constant real SFX_DEATH_TIME = 1.50
...
endmodule
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target)
endfunction
...
/*****************************************************************
* END OF GLOBAL SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
public struct <SpellName> extends array
implement GlobalSpellConfiguration
string staticSfxModel
string staticSfxAttachPoint
real spellDuration
...
private method onClonedSpellStart takes nothing returns thistype
if <InvalidCastCondition> then
return 0
endif
...
return this
endmethod
private method onClonedSpellPeriodic takes nothing returns boolean
...
set this.spellDuration = this.spellDuration - SPELL_PERIOD
return this.spellDuration <= 0.00 or <OtherEndPeriodicCondition>
endmethod
private method onClonedSpellEnd takes nothing returns nothing
...
endmethod
implement SpellCloner
endstruct
/*
* This is the module that you will implement into the struct that you wish to
* contain the local spell configurations
*/
module <SpellName>Configuration
private static method configHandler takes nothing returns nothing
/*
* This configuration setup is run automatically by the system before calling
* onClonedSpellStart().
*/
local <SpellName> node = SpellCloner.configuredInstance
set node.staticSfxModel = STATIC_SFX_MODEL
set node.staticSfxAttachPoint = STATIC_SFX_ATTACHPOINT
...
set node.spellDuration = spellDuration(GetEventSpellLevel())
...
endmethod
private static method onInit takes nothing returns nothing
call <SpellName>.create(thistype.typeid, SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.configHandler)
endmethod
endmodule
endlibrary
//! endnovjass
//! novjass
library <SpellName>ConfigurationModule /*
*/uses /*
*/SpellFramework /*
*/<SpellName>
/*
* Configuration struct and its members are better to be public (Just use public keyword
* on struct for less collision). This would allow spell modifier systems running on
* generic spell events to be able to modify spells freely.
*/
public struct Configuration1 extends array
static constant integer SPELL_ABILITY_ID = 'A000'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
static constant string STATIC_SFX_MODEL = ""
static constant string STATIC_SFX_ATTACHPOINT = "origin"
...
static method spellDuration takes integer level returns real
return 10.00 + 0.00*level
endmethod
...
implement <SpellName>Configuration
endstruct
public struct Configuration2 extends array
/*
* Activation ability ids can even be similar, in which case a single cast would
* activate two (or more) spells
*/
static constant integer SPELL_ABILITY_ID = 'A001'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
static constant string STATIC_SFX_MODEL = ""
static constant string STATIC_SFX_ATTACHPOINT = "origin"
...
static method spellDuration takes integer level returns real
return 5.00 + 1.00*level
endmethod
...
implement <SpellName>Configuration
endstruct
...
endlibrary
//! endnovjass
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
library ErrorMessage /* v1.0.2.0
*************************************************************************************
*
* Issue Compliant Error Messages
*
************************************************************************************
*
* 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
*
* function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
*
************************************************************************************/
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
endlibrary
library ResourcePreloader /* v1.4d
*/uses /*
*/optional BJObjectId /* http://www.hiveworkshop.com/threads/bjobjectid.287128/
*/optional Table /* http://www.hiveworkshop.com/threads/snippet-new-table.188084/
*/optional UnitRecycler /* http://www.hiveworkshop.com/threads/snippet-unit-recycler.286701/
*///! novjass
[CREDITS]
/*
AGD - Author
IcemanBo - for suggesting further improvements
Silvenon - for the sound preloading method
*/
|-----|
| API |
|-----|
function PreloadUnit takes integer rawcode returns nothing/*
- Assigns a certain type of unit to be preloaded
*/function PreloadItem takes integer rawcode returns nothing/*
- Assigns a certain type of item to be preloaded
*/function PreloadAbility takes integer rawcode returns nothing/*
- Assigns a certain type of ability to be preloaded
*/function PreloadEffect takes string modelPath returns nothing/*
- Assigns a certain type of effect to be preloaded
*/function PreloadSound takes string soundPath returns nothing/*
- Assigns a certain type of sound to be preloaded
The following functions requires the BJObjectId library:
*/function PreloadUnitEx takes integer start, integer end returns nothing/*
- Assigns a range of units to be preloaded
*/function PreloadItemEx takes integer start, integer end returns nothing/*
- Assigns a range of items to be preloaded
*/function PreloadAbilityEx takes integer start, integer end returns nothing/*
- Assigns a range of abilities to be preloaded
*///! endnovjass
/*========================================================================================================*/
/* Do not try to change below this line if you're not so sure on what you're doing. */
/*========================================================================================================*/
private keyword S
/*============================================== TextMacros ==============================================*/
//! textmacro PRELOAD_TYPE takes NAME, ARG, TYPE, INDEX, I
function Preload$NAME$ takes $ARG$ what returns nothing
static if LIBRARY_Table then
if S.tb[$I$].boolean[$INDEX$] then
return
endif
set S.tb[$I$].boolean[$INDEX$] = true
call Do$NAME$Preload(what)
else
if LoadBoolean(S.tb, $I$, $INDEX$) then
return
endif
call SaveBoolean(S.tb, $I$, $INDEX$, true)
call Do$NAME$Preload(what)
endif
endfunction
//! endtextmacro
//! textmacro RANGED_PRELOAD_TYPE takes NAME
function Preload$NAME$Ex takes integer start, integer end returns nothing
local boolean forward = start < end
loop
call Preload$NAME$(start)
exitwhen start == end
if forward then
set start = BJObjectId(start).plus_1()
exitwhen start > end
else
set start = BJObjectId(start).minus_1()
exitwhen start < end
endif
endloop
endfunction
//! endtextmacro
/*========================================================================================================*/
private function DoUnitPreload takes integer id returns nothing
static if LIBRARY_UnitRecycler then
call RecycleUnitEx(CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), id, 0, 0, 270))
else
call RemoveUnit(CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), id, 0, 0, 0))
endif
endfunction
private function DoItemPreload takes integer id returns nothing
call RemoveItem(UnitAddItemById(S.dummy, id))
endfunction
private function DoAbilityPreload takes integer id returns boolean
return UnitAddAbility(S.dummy, id) and UnitRemoveAbility(S.dummy, id)
endfunction
private function DoEffectPreload takes string path returns nothing
call DestroyEffect(AddSpecialEffectTarget(path, S.dummy, "origin"))
endfunction
private function DoSoundPreload takes string path returns nothing
local sound s = CreateSound(path, false, false, false, 10, 10, "")
call SetSoundVolume(s, 0)
call StartSound(s)
call KillSoundWhenDone(s)
set s = null
endfunction
//! runtextmacro PRELOAD_TYPE("Unit", "integer", "unit", "what", "0")
//! runtextmacro PRELOAD_TYPE("Item", "integer", "item", "what", "1")
//! runtextmacro PRELOAD_TYPE("Ability", "integer", "ability", "what", "2")
//! runtextmacro PRELOAD_TYPE("Effect", "string", "effect", "StringHash(what)", "3")
//! runtextmacro PRELOAD_TYPE("Sound", "string", "sound", "StringHash(what)", "4")
static if LIBRARY_BJObjectId then
//! runtextmacro RANGED_PRELOAD_TYPE("Unit")
//! runtextmacro RANGED_PRELOAD_TYPE("Item")
//! runtextmacro RANGED_PRELOAD_TYPE("Ability")
endif
/*========================================================================================================*/
private module Init
private static method onInit takes nothing returns nothing
local rect world = GetWorldBounds()
static if LIBRARY_Table then
set tb = TableArray[5]
endif
set dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hpea', 0, 0, 0)
call UnitAddAbility(dummy, 'AInv')
call UnitAddAbility(dummy, 'Avul')
call UnitRemoveAbility(dummy, 'Amov')
call SetUnitY(dummy, GetRectMaxY(world) + 1000)
call RemoveRect(world)
set world = null
endmethod
endmodule
private struct S extends array
static if LIBRARY_Table then
static TableArray tb
else
static hashtable tb = InitHashtable()
endif
static unit dummy
implement Init
endstruct
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
/*****************************************************************************
*
* 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
library TeethConfiguration /*
*/uses /*
*/Teeth /*
*/SpellFramework /*
*/
public struct Default extends array
//
static constant integer SPELL_ABILITY_ID = 'Teet'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
//
// ==========================
// Effect path
//
// Projectile model
static constant string PROJECTILE_MODEL = "Abilities\\Weapons\\SkeletalMageMissile\\SkeletalMageMissile.mdl"
//
// Critical hit (on viable target)
static constant string CRITICAL_HIT_MODEL = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
//
// Regular on hit (with viable target)
static constant string REGULAR_HIT_MODEL = "Abilities\\Weapons\\BloodElfMissile\\BloodElfMissile.mdl"
//
// ==========================
// Stats
//
// Allows Multiple Teeth to hit the same target (Default: FALSE)
// "Big fat rabbits are jumpin' in the grass, wait'll they hear my ol' shotgun blast~."
static constant boolean SHOTGUN_BOOGEY = false
//
static constant attacktype ATTACK_TYPE = ATTACK_TYPE_HERO
static constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
static constant weapontype WEAPON_TYPE = null
//
// Height Adjustment to tooth
static constant real TOOTH_HEIGHT = 65.00
//
// Missile Offset from caster on creation
static constant real TEETH_OFFSET = 40.00
//
static constant method toothDamage takes integer level returns real
return 0.00 + 50.00*level
endmethod
//
// Collision of missile
static constant method toothCollision takes integer level returns real
return 40.00
endmethod
//
// Number of teeth per cast
static constant method teethCount takes integer level returns integer
return 5 + 5*level
endmethod
//
// Crit chance
static constant method critChance takes integer level returns real
return 0.00 + 0.01*level
endmethod
//
// How much bonus damage does the tooth hit for? (Default 200%)
static constant method critPercentage takes integer level returns real
return 2.00 + 0.00*level
endmethod
//
// How far does the projectile travel before dissipating?
static constant method travelDistance takes integer level returns real
return 1500.00 + 0.00*level
endmethod
//
// Speed of tooth
static constant method toothSpeed takes integer level returns real
return 384.00 + 0.00*level
endmethod
//
// How close the teeth sit together at initial cast
// a small value will make the teeth bunch together like a single projectile, while a larger value will spread them out in front of the caster.
static constant method clusterWidth takes integer level returns real
return 135.00 + 0.00*level
endmethod
implement TeethConfiguration
endstruct
endlibrary
library Teeth /*
This spell is originally created by Devalut. I simply converted it to vJass.
Here is the link to the original resource:
- https://www.hiveworkshop.com/threads/teeth-ver-gangrenous-gillyweed.316053/
*/uses /*
*/SpellFramework /* https://www.hiveworkshop.com/threads/325448/
*/ResourcePreloader /* https://www.hiveworkshop.com/threads/287358/
*/
/*****************************************************************
* GLOBAL SPELL CONFIGURATION *
*****************************************************************/
private module GlobalConfiguration
static constant real SPELL_PERIOD = 1.00/32.00
static constant integer DUMMY_UNIT_ID = 'dumi'
static constant player DUMMY_OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
static constant real SFX_DEATH_TIME = 1.50
static constant string SFX_ATTACH_POINT = "origin"
endmodule
private function TargetFilter takes unit target, unit caster returns boolean
return UnitAlive(target) and /*
*/ target != caster and /*
*/ not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ IsUnitEnemy(target, GetOwningPlayer(caster))
endfunction
/*****************************************************************
* END OF GLOBAL SPELL CONFIGURATION *
*****************************************************************/
/*========================= SPELL CODE =========================*/
native UnitAlive takes unit u returns boolean
private struct Teeth extends array
implement GlobalConfiguration
implement Alloc
implement SpellClonerHeader
string toothModel
string critHitModel
string normalHitModel
boolean shotgunBoogey
integer count
real toothHeight
real teethOffset
real damage
real collision
real critChance
real critMultiplier
real distance
real speed
real width
attacktype attackType
damagetype damageType
weapontype weaponType
private thistype mainNode
private boolean critical
private unit caster
private unit dummy
private effect effect
private group group
private real facing
private static thistype tempInstance
private static rect tempRect
private static group enumGroup = CreateGroup()
private static location loc = Location(0.00, 0.00)
private static method onDestCollide takes nothing returns boolean
local destructable d = GetFilterDestructable()
local real dx = GetWidgetX(d) - GetRectCenterX(tempRect)
local real dy = GetWidgetY(d) - GetRectCenterY(tempRect)
if dx*dx + dy*dy <= tempInstance.mainNode.collision*tempInstance.mainNode.collision then
set tempInstance.distance = 0.00
endif
set d = null
return false
endmethod
private method onSpellStart takes nothing returns thistype
local real casterX = GetUnitX(Spell.triggerUnit)
local real casterY = GetUnitY(Spell.triggerUnit)
local real angle = Atan2(Spell.targetY - casterY, Spell.targetX - casterX)
local real cos = Cos(angle - bj_PI*0.50)
local real sin = Sin(angle - bj_PI*0.50)
local real ox
local real oy
local real x
local real y
local real d
local integer count
local thistype node
set this = allocate()
call this.initSpellConfiguration(Spell.abilityId)
set ox = casterX + this.teethOffset*Cos(angle)
set oy = casterY + this.teethOffset*Sin(angle)
set count = this.count
loop
exitwhen count == 0
set node = allocate()
set node.mainNode = this
set node.next = 0
set node.prev = thistype(0).prev
set thistype(0).prev.next = node
set thistype(0).prev = node
set d = this.width*0.50 - ((this.width/I2R(this.count + 1)))*I2R(count)
set x = Spell.targetX + d*cos
set y = Spell.targetY + d*sin
set node.facing = Atan2(y - casterY, x - casterX)
set node.distance = this.distance
set node.critical = GetRandomReal(0.00, 1) <= this.critChance
set node.dummy = CreateUnit(DUMMY_OWNER, DUMMY_UNIT_ID, ox, oy, node.facing*bj_RADTODEG)
set node.effect = AddSpecialEffectTarget(this.toothModel, node.dummy, SFX_ATTACH_POINT)
if UnitAddAbility(node.dummy, 'Arav') then
call UnitRemoveAbility(node.dummy, 'Arav')
endif
call SetUnitFlyHeight(node.dummy, this.toothHeight, 0.00)
set count = count - 1
endloop
set this.caster = Spell.triggerUnit
set this.group = CreateGroup()
set this.mainNode = 0
return this
endmethod
private method onSpellPeriodic takes nothing returns boolean
// -
// Find Projectile & move it
// -
local thistype mainNode = this.mainNode
local real x1 = GetUnitX(this.dummy)
local real y1 = GetUnitY(this.dummy)
local real x2 = x1 + mainNode.speed*Cos(this.facing)
local real y2 = y1 + mainNode.speed*Sin(this.facing)
local real z2
local unit u
local unit temp
local effect e
if mainNode == 0 then
return this.count == 0
endif
// Blizzard doesn't have an option to check the terrain height of a point so we must resort to the dark magic that is "custom script", note that the variables written in the custom script must reflect EXACTLY how the variable is, else it will crash
call MoveLocation(loc, x2, y2)
set z2 = GetLocationZ(loc)
call SetUnitX(this.dummy, x2)
call SetUnitY(this.dummy, y2)
call SetUnitFlyHeight(this.dummy, z2 + mainNode.toothHeight, 0.00)
// -
// Collision check
// -
if this.distance > 0.00 and not IsTerrainPathable(x1, y1, PATHING_TYPE_WALKABILITY) then
// -
// Unit & Destructable Check
// -
set this.distance = this.distance - mainNode.speed
call GroupEnumUnitsInRange(enumGroup, x1, y1, mainNode.collision, null)
loop
set u = FirstOfGroup(enumGroup)
exitwhen u == null
call GroupRemoveUnit(enumGroup, u)
if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
// We don't hurt structures, denied
// -
// This denotes our previous condition check that the projectile is dead and to clean up
set this.distance = 0.00
// -
elseif not IsUnitInGroup(u, mainNode.group) and /*
*/ this.distance > 0.00 and /*
*/ TargetFilter(u, mainNode.caster) then
// Normally special effetcts will sit on the ground
// But with these few additions I can make it appear where it hits the unit
set temp = CreateUnit(DUMMY_OWNER, DUMMY_UNIT_ID, x2, y2, this.facing)
if UnitAddAbility(this.dummy, 'Arav') then
call UnitRemoveAbility(this.dummy, 'Arav')
endif
call SetUnitFlyHeight(temp, z2 + mainNode.toothHeight, 0.00)
call UnitApplyTimedLife(temp, 'BTLF', SFX_DEATH_TIME)
if mainNode.critical then
set e = AddSpecialEffectTarget(mainNode.critHitModel, temp, SFX_ATTACH_POINT)
call UnitDamageTarget(mainNode.caster, u, mainNode.damage*mainNode.critMultiplier, true, false, mainNode.attackType, mainNode.damageType, mainNode.weaponType)
else
set e = AddSpecialEffectTarget(mainNode.normalHitModel, temp, SFX_ATTACH_POINT)
call UnitDamageTarget(mainNode.caster, u, mainNode.damage, true, false, mainNode.attackType, mainNode.damageType, mainNode.weaponType)
endif
call DestroyEffect(e)
set e = null
set temp = null
if not mainNode.shotgunBoogey then
// This option halts units from being put in the naughty corner and exlcuded from more than one bolt
// This is in line with diablo 2 orginal teeth spell
call GroupAddUnit(mainNode.group, u)
endif
// Again, this tells the loop that the projectile should be dead and cleaned up
set this.distance = 0.00
endif
endloop
// Watch out for that tree!
// Note this does not apply to props or doodads, the previous conditional check "Check Walkability" works around this by seeing if you can walk on it
set tempInstance = this
set tempRect = Rect(x1 - mainNode.collision, y1 - mainNode.collision, x1 + mainNode.collision, y1 + mainNode.collision)
call EnumDestructablesInRect(tempRect, Filter(function thistype.onDestCollide), null)
return false
endif
return true // end periodic
endmethod
private method onSpellEnd takes nothing returns nothing
if this.mainNode > 0 then
set this.mainNode.count = this.mainNode.count - 1
call DestroyEffect(this.effect)
call UnitApplyTimedLife(this.dummy, 'BTLF', SFX_DEATH_TIME)
set this.dummy = null
set this.caster = null
set this.effect = null
else
call DestroyGroup(this.group)
set this.group = null
endif
call this.deallocate()
endmethod
implement SpellEvent
implement SpellClonerFooter
endstruct
module TeethConfiguration
private static method configHandler takes nothing returns nothing
local Teeth node = SpellCloner.configuredInstance
set node.toothModel = PROJECTILE_MODEL
set node.critHitModel = CRITICAL_HIT_MODEL
set node.normalHitModel = REGULAR_HIT_MODEL
set node.shotgunBoogey = SHOTGUN_BOOGEY
set node.attackType = ATTACK_TYPE
set node.damageType = DAMAGE_TYPE
set node.weaponType = WEAPON_TYPE
set node.toothHeight = TOOTH_HEIGHT
set node.teethOffset = TEETH_OFFSET
set node.damage = toothDamage(Spell.level)
set node.collision = toothCollision(Spell.level)
set node.count = teethCount(Spell.level)
set node.critChance = critChance(Spell.level)
set node.critMultiplier = critPercentage(Spell.level)
set node.distance = travelDistance(Spell.level)
set node.speed = toothSpeed(Spell.level)*Teeth.SPELL_PERIOD
set node.width = clusterWidth(Spell.level)
endmethod
private static method onInit takes nothing returns nothing
call Teeth.create(thistype.typeid, SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.configHandler)
call PreloadEffect(PROJECTILE_MODEL)
call PreloadEffect(CRITICAL_HIT_MODEL)
call PreloadEffect(REGULAR_HIT_MODEL)
endmethod
endmodule
endlibrary
library ReflectionConfiguration /*
*/uses /*
*/SpellFramework /*
*/Reflection /*
*/
public struct Default extends array
static constant integer SPELL_ABILITY_ID = 'Refl'
static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
static constant boolean GHASTLY_SUMMONS = true
static constant string DEATH_EFFECT_MODEL = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl"
static constant string SUMMON_EFFECT_MODEL = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl"
static constant string SUMMON_EFFECT_ATTACHPOINT = "origin"
static constant method summonCount takes integer level returns integer
return 2 + 0*level
endmethod
static constant method duration takes integer level returns real
return 10.00 + 0.00*level
endmethod
static constant method damageDealtFactor takes integer level returns real
return 0.20 + 0.00*level
endmethod
static constant method damageTakenFactor takes integer level returns real
return 1.00 + 0.00*level
endmethod
static constant method summonOffsetMin takes integer level returns real
return 200.00 + 0.00*level
endmethod
static constant method summonOffsetMax takes integer level returns real
return summonOffsetMin(level)
endmethod
static constant method summonOffsetDeltaMin takes integer level returns real
return 0.00 + 0.00*level
endmethod
static constant method summonOffsetDeltaMax takes integer level returns real
return summonOffsetDeltaMin(level)
endmethod
static constant method summonAngleOffsetMin takes integer level returns real
return 0.00 + 0.00*level
endmethod
static constant method summonAngleOffsetMax takes integer level returns real
return summonAngleOffsetMin(level)
endmethod
static constant method summonAngleDeltaFactorMin takes integer level returns real
return 1.00 + 0.00*level
endmethod
static constant method summonAngleDeltaFactorMax takes integer level returns real
return summonAngleDeltaFactorMin(level)
endmethod
implement ReflectionConfiguration
endstruct
endlibrary
library Reflection /*
*/uses /*
*/SpellFramework /* https://www.hiveworkshop.com/threads/325448/
*/IllusionEditor /* https://www.hiveworkshop.com/threads/286441/
*/UnitDex /* https://www.hiveworkshop.com/threads/248209/
*/ResourcePreloader /* https://www.hiveworkshop.com/threads/287358/
*/RegisterPlayerUnitEvent /* https://www.hiveworkshop.com/threads/250266/
*/OrderIds /* https://www.hiveworkshop.com/threads/290882/
*/
private module Configuration
static constant integer LOCUST_ABILITY_ID = 'Aloc'
static constant real SPELL_PERIOD = 1./32.
endmodule
private keyword Reflection
private keyword IllusionList
globals
private IllusionList array iNode
endglobals
private struct IllusionList extends array
readonly Illusion illusion
implement Alloc
private static method onRemove takes thistype node returns nothing
set iNode[node.illusion] = 0
call node.deallocate()
endmethod
implement InstantiatedList
method add takes Illusion illusion, boolean isGhost returns thistype
local thistype node = allocate()
set node.illusion = illusion
set iNode[illusion] = node
if isGhost then
call UnitAddAbility(illusion.unit, Reflection.LOCUST_ABILITY_ID)
endif
call this.pushBack(node)
return node
endmethod
endstruct
private struct Reflection extends array
implement Configuration
static integer count
static real duration
static real damageDealtFactor
static real damageTakenFactor
static real summonOffsetMin
static real summonOffsetMax
static real summonOffsetDeltaMin
static real summonOffsetDeltaMax
static real summonAngleOffsetMin
static real summonAngleOffsetMax
static real summonAngleDeltaFactorMin
static real summonAngleDeltaFactorMax
static boolean ghastlySummons
static string deathEffectModel
static string summonEffectModel
static string summonEffectAttachPoint
private static IllusionList array illusionList
readonly real dx
readonly real dy
implement SpellClonerHeader
private method onSpellStart takes nothing returns thistype
local real ox
local real oy
local real x
local real y
local real facing
local real dr
local real a
local real dA
local thistype node
call this.initSpellConfiguration(Spell.abilityId)
if count < 1 then
return 0
endif
set ox = GetUnitX(Spell.triggerUnit)
set oy = GetUnitY(Spell.triggerUnit)
set facing = GetUnitFacing(Spell.triggerUnit)
set a = facing*bj_DEGTORAD - 0.5*bj_PI + GetRandomReal(summonAngleOffsetMin, summonAngleOffsetMax)
set dA = 2.00*bj_PI/count
loop
exitwhen count == 0
set dr = GetRandomReal(summonOffsetMin, summonOffsetMax)
set x = ox + dr*Cos(a)
set y = oy + dr*Sin(a)
set node = illusionList[GetUnitId(Spell.triggerUnit)].add(Illusion.createCopy(Spell.triggerPlayer, Spell.triggerUnit, duration, damageDealtFactor, damageTakenFactor, x, y, facing), ghastlySummons)
set node.next = 0
set node.prev = thistype(0).prev
set thistype(0).prev.next = node
set thistype(0).prev = node
set node.dx = x - ox
set node.dy = y - oy
set IllusionList(node).illusion.deathEffect = deathEffectModel
call SetUnitPathing(IllusionList(node).illusion.unit, false)
call DestroyEffect(AddSpecialEffectTarget(summonEffectModel, IllusionList(node).illusion.unit, summonEffectAttachPoint))
set a = a + dA*GetRandomReal(summonAngleDeltaFactorMin, summonAngleDeltaFactorMax)
set count = count - 1
endloop
return 0
endmethod
private method onSpellPeriodic takes nothing returns boolean
call SetUnitX(IllusionList(this).illusion.unit, GetUnitX(IllusionList(this).illusion.baseUnit) + this.dx)
call SetUnitY(IllusionList(this).illusion.unit, GetUnitY(IllusionList(this).illusion.baseUnit) + this.dy)
return not (UnitAlive(IllusionList(this).illusion.unit) and UnitAlive(IllusionList(this).illusion.baseUnit))
endmethod
private method onSpellEnd takes nothing returns nothing
call PauseUnit(IllusionList(this).illusion.unit, true)
endmethod
implement SpellEvent
implement SpellClonerFooter
private static method onSpellEvent takes nothing returns nothing
local integer unitId = GetUnitId(Spell.triggerUnit)
local IllusionList illusionNode
local Spell spell
if illusionList[unitId] > 0 and not hasActivationAbility(Spell.abilityId) and not illusionList[unitId].empty then
set spell = Spell[Spell.abilityId]
set illusionNode = illusionList[unitId].next
loop
exitwhen illusionNode == illusionList[unitId]
if Spell.orderType == SPELL_ORDER_TYPE_NO_TARGET then
call spell.invokeNoTargetEvent(Spell.eventType, Spell.level, illusionNode.illusion.unit)
elseif Spell.orderType == SPELL_ORDER_TYPE_POINT_TARGET then
call spell.invokePointTargetEvent(Spell.eventType, Spell.level, illusionNode.illusion.unit, Spell.targetX, Spell.targetY)
elseif Spell.orderType == SPELL_ORDER_TYPE_SINGLE_TARGET then
call spell.invokeSingleTargetEvent(Spell.eventType, Spell.level, illusionNode.illusion.unit, Spell.target)
endif
set illusionNode = illusionNode.next
endloop
endif
endmethod
implement SpellEventGeneric
private static method onNoTargetOrder takes nothing returns nothing
local integer unitId = GetUnitId(GetTriggerUnit())
local integer eventOrderId = GetIssuedOrderId()
local IllusionList illusionNode
if illusionList[unitId] > 0 and not illusionList[unitId].empty then
set illusionNode = illusionList[unitId].next
loop
exitwhen illusionNode == illusionList[unitId]
call IssueImmediateOrderById(illusionNode.illusion.unit, eventOrderId)
set illusionNode = illusionNode.next
endloop
endif
endmethod
private static method onPointTargetOrder takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer unitId = GetUnitId(u)
local integer eventOrderId
local real targetX
local real targetY
local real dx
local real dy
local IllusionList illusionNode
if illusionList[unitId] > 0 and not illusionList[unitId].empty then
set eventOrderId = GetIssuedOrderId()
set illusionNode = illusionList[unitId].next
if eventOrderId == ORDER_move or/*
*/ eventOrderId == ORDER_stop or/*
*/ eventOrderId == ORDER_smart or/*
*/ eventOrderId == ORDER_attack then
set dx = GetOrderPointX() - GetUnitX(u)
set dy = GetOrderPointY() - GetUnitY(u)
loop
exitwhen illusionNode == illusionList[unitId]
call IssuePointOrderById(illusionNode.illusion.unit, eventOrderId, GetUnitX(illusionNode.illusion.unit) + dx, GetUnitY(illusionNode.illusion.unit) + dy)
set illusionNode = illusionNode.next
endloop
else
set targetX = GetOrderPointX()
set targetY = GetOrderPointY()
loop
exitwhen illusionNode == illusionList[unitId]
if not IssuePointOrderById(illusionNode.illusion.unit, eventOrderId, targetX, targetY) then
call IssueImmediateOrderById(illusionNode.illusion.unit, ORDER_stop)
endif
set illusionNode = illusionNode.next
endloop
endif
endif
set u = null
endmethod
private static method onSingleTargetOrder takes nothing returns nothing
local integer unitId = GetUnitId(GetTriggerUnit())
local integer eventOrderId
local widget target
local IllusionList illusionNode
if illusionList[unitId] > 0 and not illusionList[unitId].empty then
set eventOrderId = GetIssuedOrderId()
set target = GetOrderTarget()
set illusionNode = illusionList[unitId].next
loop
exitwhen illusionNode == illusionList[unitId]
if not IssueTargetOrderById(illusionNode.illusion.unit, eventOrderId, target) then
call IssueImmediateOrderById(illusionNode.illusion.unit, ORDER_stop)
endif
set illusionNode = illusionNode.next
endloop
set target = null
endif
endmethod
private static method onIllusionDeath takes nothing returns nothing
call IllusionList.remove(iNode[Illusion[Illusion.triggerUnit]])
endmethod
private static method onUnitIndex takes nothing returns boolean
if not IsUnitIllusion(GetIndexedUnit()) then
set illusionList[GetIndexedUnitId()] = IllusionList.create()
endif
return false
endmethod
private static method onUnitDeindex takes nothing returns boolean
if illusionList[GetIndexedUnitId()] > 0 then
call illusionList[GetIndexedUnitId()].destroy()
set illusionList[GetIndexedUnitId()] = 0
endif
return false
endmethod
private static method onInit takes nothing returns nothing
call OnUnitIndex(function thistype.onUnitIndex)
call OnUnitDeindex(function thistype.onUnitDeindex)
call RegisterIllusionDeathHandler(function thistype.onIllusionDeath)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onNoTargetOrder)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onPointTargetOrder)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onSingleTargetOrder)
endmethod
endstruct
module ReflectionConfiguration
private static method configHandler takes nothing returns nothing
local Reflection node = SpellCloner.configuredInstance
set node.ghastlySummons = GHASTLY_SUMMONS
set node.deathEffectModel = DEATH_EFFECT_MODEL
set node.summonEffectModel = SUMMON_EFFECT_MODEL
set node.summonEffectAttachPoint = SUMMON_EFFECT_ATTACHPOINT
set node.count = summonCount(Spell.level)
set node.duration = duration(Spell.level)
set node.damageDealtFactor = damageDealtFactor(Spell.level)
set node.damageTakenFactor = damageTakenFactor(Spell.level)
set node.summonOffsetMin = summonOffsetMin(Spell.level)
set node.summonOffsetMax = summonOffsetMax(Spell.level)
set node.summonOffsetDeltaMin = summonOffsetDeltaMin(Spell.level)
set node.summonOffsetDeltaMax = summonOffsetDeltaMax(Spell.level)
set node.summonAngleOffsetMin = summonAngleOffsetMin(Spell.level)
set node.summonAngleOffsetMax = summonAngleOffsetMax(Spell.level)
set node.summonAngleDeltaFactorMin = summonAngleDeltaFactorMin(Spell.level)
set node.summonAngleDeltaFactorMax = summonAngleDeltaFactorMax(Spell.level)
endmethod
private static method onInit takes nothing returns nothing
call Reflection.create(thistype.typeid, SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.configHandler)
call PreloadEffect(DEATH_EFFECT_MODEL)
call PreloadEffect(SUMMON_EFFECT_MODEL)
endmethod
endmodule
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 TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//* > function IsTerrainDeepWater takes real x, real y returns boolean
//* > function IsTerrainShallowWater takes real x, real y returns boolean
//* > function IsTerrainLand takes real x, real y returns boolean
//* > function IsTerrainPlatform takes real x, real y returns boolean
//* > function IsTerrainWalkable takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//* [real] TerrainPathability_X
//* [real] TerrainPathability_Y
//*
globals
private constant real MAX_RANGE = 10.
private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals
globals
private item Item = null
private rect Find = null
private item array Hid
private integer HidMax = 0
public real X = 0.
public real Y = 0.
endglobals
function IsTerrainDeepWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
private function HideItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hid[HidMax] = GetEnumItem()
call SetItemVisible(Hid[HidMax], false)
set HidMax = HidMax + 1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
//Hide any items in the area to avoid conflicts with our item
call MoveRectTo(Find, x, y)
call EnumItemsInRect(Find ,null, function HideItem)
//Try to move the test item and get its coords
call SetItemPosition(Item, x, y) //Unhides the item
set X = GetItemX(Item)
set Y = GetItemY(Item)
static if LIBRARY_IsTerrainWalkable then
//This is for compatibility with the IsTerrainWalkable library
set IsTerrainWalkable_X = X
set IsTerrainWalkable_Y = Y
endif
call SetItemVisible(Item, false)//Hide it again
//Unhide any items hidden at the start
loop
exitwhen HidMax <= 0
set HidMax = HidMax - 1
call SetItemVisible(Hid[HidMax], true)
set Hid[HidMax] = null
endloop
//Return walkability
return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
private function Init takes nothing returns nothing
set Find = Rect(0., 0., 128., 128.)
set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary
library IsDestructableTree uses optional UnitIndexer /* v1.3.1
*************************************************************************************
*
* Detect whether a destructable is a tree or not.
*
***************************************************************************
*
* Credits
*
* To PitzerMike
* -----------------------
*
* for IsDestructableTree
*
*************************************************************************************
*
* Functions
*
* function IsDestructableTree takes destructable d returns boolean
*
* function IsDestructableAlive takes destructable d returns boolean
*
* function IsDestructableDead takes destructable d returns boolean
*
* function IsTreeAlive takes destructable tree returns boolean
* - May only return true for trees.
*
* function KillTree takes destructable tree returns boolean
* - May only kill trees.
*
*/
globals
private constant integer HARVESTER_UNIT_ID = 'hpea'//* human peasant
private constant integer HARVEST_ABILITY = 'Ahrl'//* ghoul harvest
private constant integer HARVEST_ORDER_ID = 0xD0032//* harvest order ( 852018 )
private constant player NEUTRAL_PLAYER = Player(PLAYER_NEUTRAL_PASSIVE)
private unit harvester = null
endglobals
function IsDestructableTree takes destructable d returns boolean
//* 851973 is the order id for stunned, it will interrupt the preceding harvest order.
return (IssueTargetOrderById(harvester, HARVEST_ORDER_ID, d)) and (IssueImmediateOrderById(harvester, 851973))
endfunction
function IsDestructableDead takes destructable d returns boolean
return (GetWidgetLife(d) <= 0.405)
endfunction
function IsDestructableAlive takes destructable d returns boolean
return (GetWidgetLife(d) > .405)
endfunction
function IsTreeAlive takes destructable tree returns boolean
return IsDestructableAlive(tree) and IsDestructableTree(tree)
endfunction
function KillTree takes destructable tree returns boolean
if (IsTreeAlive(tree)) then
call KillDestructable(tree)
return true
endif
return false
endfunction
private function Init takes nothing returns nothing
static if LIBRARY_UnitIndexer then//* You may adapt this to your own indexer.
set UnitIndexer.enabled = false
endif
set harvester = CreateUnit(NEUTRAL_PLAYER, HARVESTER_UNIT_ID, 0, 0, 0)
static if LIBRARY_UnitIndexer then
set UnitIndexer.enabled = true
endif
call UnitAddAbility(harvester, HARVEST_ABILITY)
call UnitAddAbility(harvester, 'Aloc')
call ShowUnit(harvester, false)
endfunction
//* Seriously?
private module Inits
private static method onInit takes nothing returns nothing
call Init()
endmethod
endmodule
private struct I extends array
implement Inits
endstruct
endlibrary
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
* v1.2.2.0, 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 global filter for units being indexed
/*static method onFilter takes unit u returns boolean
return true
endmethod*/
endmodule
/* _________________________________________________________________________
* 3. Function API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Every function inlines.
*
* 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.
*
* -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
/****************************************************************/
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
function UnitDexRemove takes unit u, boolean runEvents returns boolean
return UnitDex.Remove(u, runEvents)
endfunction
/****************************************************************/
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 = 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
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
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
// Index preplaced units
set i = 0
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
set LastIndex = Counter
// run init triggers
call TimerStart(CreateTimer(), 0.00, false, function thistype.onGameStart)
endmethod
endmodule
endlibrary
library GetUnitTypeCollisionSize uses Table
globals
private key t
endglobals
/*
* Credits to Nestharus
* https://github.com/nestharus/JASS/blob/master/jass/Systems/GetUnitCollision/script.j
*/
private function C takes integer unitId returns real
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), unitId, 0.00, 0.00, 0.00)
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real l = 0
local real h = 300
local real m = 150
loop
if (IsUnitInRangeXY(u, x+m, y, 0)) then
set l = m
else
set h = m
endif
set m = (l+h)/2
exitwhen l+.01 > h
endloop
set m = R2I((m + .005)*100)/100.
set Table(t).real[unitId] = m
call RemoveUnit(u)
set u = null
return m
endfunction
function GetUnitTypeCollisionSize takes integer unitId returns real
if (Table(t).real.has(unitId)) then
return Table(t).real[unitId]
endif
return C(unitId)
endfunction
endlibrary
library IPool requires Table, Alloc
/*
IPool 3.0.0.0 by Bribe
Special thanks to Pyrogasm on wc3c.net for the original Pools resource, and to
Rising_Dusk for popularizing it.
Quick Intro of IPool:
Do you want a random integer from a multiple-choice list instead of a number
between x and y? Do you want one of those choices to have a lower or higher chance
to be picked? How about each item has its own odds of being picked?
IPool is similar to the native types itempool and unitpool, however returns
integers instead of handles. Integer-based pools can be used for all sorts of
things, ranging from randomized creep drops, spawning systems, random abilities
being cast (a la traps), random instances of a atruct, etc.
Object-Types of IPool:
IPool - Fast .getItem() method, slow add/remove. Uses a Table index formatted
as an array to get a random item without searching.
SubPool - Fast add/remove, slower .getItem method. Uses an O(n) search to
get a random item.
Main IPool API:
IPool.create()->IPool
returns a new IPool for your needs
iPoolInstance.flush()
reset all values of the IPool to default settings
iPoolInstance.destroy()
use this if you are totally done with that IPool
iPoolInstance.add(integer value, integer weight)
add any integer to the pool. The weight must be greater than 0 to have a
chance of being picked.
iPoolInstance.remove(integer value)
that value will no longer have a chance if you use this method on it!
iPoolInstance.getItem()->integer
returns one of the integers you added. Has higher chance to pick an item
with higher weight
Main SubPool API:
SubPool.create(integer totalWeight)->SubPool
The totalWeight must be equal to or greater than the sum of all items you
will add to the SubPool. Each added item has a chance of itsWeight/
totalWeight.
The rest of the main API is the same as IPool. Only keep in mind that
subPoolInstance.getItem() will return 0 in many cases due to the improbability
of an integer being picked.
Secondary API for both structs is included as follows:
poolInstance.contains(integer value)->boolean
Was the value added to the pool?
poolInstance.copy()->pool
Returns a new pool with the same properties as the pool you want copied.
debug poolInstance.print()
Useful for identifying what objects are in the pool during tests.
poolInstance.lock/unlock()
SubPools can associate with another subPool and/or an IPool. If you
have a single nested pool you wish to not get auto-destroyed, simply
call pool.lock(). pool.unlock() reverses it. If you do not use these, all
nested pools will auto-destruct when the containing SubPool is destroyed.
API for getting/setting the weight of an already-added value to a pool:
iPoolInstance.add(integer value, integer additionalWeight)
You can add more weight to a value in IPool, but the only way to reduce
it is to completely null it using the .remove(integer value) method. This
is to avoid the cumbersome giant that was the original IPool.
subPoolInstance[integer value].weight = integer newWeight
Setting a SubPool value's weight can be done arbitrarily.
iPoolInstance.weightOf(integer value)->integer
&
subPoolInstance[integer value].weight
Just in case you lost track of how much weight you assigned to a value.
API for nesting pools:
subPoolInstance.subPool = SubPool.create()
The SubPool member can be get and set arbitrarily. It cannot be the same
SubPool as the SubPool that's trying to nest it, as that would cause
recursion errors.
If the SubPool was not able to pick an integer, it will default to the
next nested SubPool within itself.
subPoolInstance.pool = IPool.create()
The .pool member can be get and set arbitrarily. If the SubPool and any
nested SubPools could not pick an integer, this pool will be the last resort.
*/
private module Init
private static method onInit takes nothing returns nothing
set .tar = TableArray[JASS_MAX_ARRAY_SIZE]
endmethod
endmodule
private struct data extends array
static TableArray tar
integer int
integer locks
integer weight
implement Alloc
implement Init
endstruct
private function Create takes nothing returns data
local data this = data.allocate()
set this.locks = 0
return this
endfunction
private function Destroy takes data this returns boolean
if this.locks == -1 or this == 0 then
debug call BJDebugMsg("IPool Error: Attempt to double-free instance!")
return false
endif
set this.locks = -1
call this.deallocate()
return true
endfunction
private function Lock takes data i returns nothing
if i.locks == -1 then
debug call BJDebugMsg("IPool Error: Attempt to lock a destroyed instance!")
return
endif
set i.locks = i.locks + 1
endfunction
private function Unlock takes data i returns boolean
if i.locks == -1 then
debug call BJDebugMsg("IPool Error: Attempt to unlock destroyed instance!")
return false
endif
set i.locks = i.locks - 1
return i.locks == 0
endfunction
struct IPool extends array
method operator weight takes nothing returns integer
return data(this).weight
endmethod
private method operator weight= takes integer lbs returns nothing
set data(this).weight = lbs
endmethod
private method operator table takes nothing returns Table
return data(this).int
endmethod
private method operator table= takes Table t returns nothing
set data(this).int = t
endmethod
static method create takes nothing returns thistype
local thistype this = Create()
set this.table = Table.create()
return this
endmethod
method flush takes nothing returns nothing
call this.table.flush()
call data.tar[this].flush()
set this.weight = 0
endmethod
method destroy takes nothing returns nothing
if Destroy(this) then
call this.table.destroy()
call data.tar[this].flush()
set this.weight = 0
endif
endmethod
method lock takes nothing returns nothing
call Lock(this)
endmethod
method unlock takes nothing returns nothing
if Unlock(this) then
call this.destroy()
endif
endmethod
//One-liner method to get a random item from the pool based on weight
method getItem takes nothing returns integer
return this.table[GetRandomInt(0, this.weight -1)]
endmethod
method weightOf takes integer value returns integer
return data.tar[this][value]
endmethod
method chanceOf takes integer value returns real //returns between 0. and 1.
return this.weightOf(value) / (this.weight + 0.) //don't divide by 0 here or else!
endmethod
method contains takes integer value returns boolean
return data.tar[this].has(value)
endmethod
method add takes integer value, integer lbs returns nothing
local Table tb = this.table
local integer i = this.weight
if lbs < 1 then
debug call BJDebugMsg("IPool Error: Tried to add value with invalid weight!")
return
endif
set data.tar[this][value] = data.tar[this][value] + lbs
set lbs = i + lbs
set this.weight = lbs //Important
loop
exitwhen i == lbs
set tb[i] = value //treat this.table as an array
set i = i + 1
endloop
endmethod
method remove takes integer value returns nothing
local Table tb = this.table
local Table new
local integer i = this.weight
local integer n = 0
local integer val
if not this.contains(value) then
debug call BJDebugMsg("IPool Error: Attempt to remove un-added instance!")
return
endif
set new = Table.create()
set this.table = new
loop
set i = i - 1
set val = tb[i]
if val != value then
set new[n] = val //write to the new Table without gaps
set n = n + 1
endif
exitwhen i == 0
endloop
set this.weight = n //lower pool weight
call tb.destroy() //abandon old Table instance
call data.tar[this].remove(value) //clear the value's weight now that it's gone
endmethod
method copy takes nothing returns thistype
local thistype new = .create()
local integer i = this.weight
local Table tt = this.table
local Table nt = new.table
local Table dt = data.tar[new]
local integer val
if i == 0 then
debug call BJDebugMsg("IPool Error: Attempt to copy invalid instance!")
call new.destroy()
return 0
endif
set new.weight = i
loop
set i = i - 1
exitwhen i == 0
set val = tt[i]
set nt[i] = val
set dt[val] = dt[val] + 1
endloop
return new
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing //print the array of the pool
local string s = "IPool: |cffffcc33Weight: "
local integer i = this.weight
local Table t = this.table
set s = s + I2S(i) + "; Indices: "
loop
set i = i - 1
exitwhen i <= 0
set s = s + "[" + I2S(t[i]) + "]"
endloop
call BJDebugMsg(s + "|r")
endmethod
endif
endstruct
//New struct to handle deliberately-rare chances
struct SubPool extends array
private IPool iPool //for association if you want it
private thistype nest //you can nest IPoolMinis for poolception
//you can change a value's weight via subpoolinstance[value].weight = blah
//you can also change the entire pool's weight via subpoolinstance.weight = blah.
method operator weight takes nothing returns integer
return data(this).weight
endmethod
method operator weight= takes integer lbs returns nothing
set data(this).weight = lbs
endmethod
private method operator value takes nothing returns integer
return data(this).int
endmethod
private method operator value= takes integer val returns nothing
set data(this).int = val
endmethod
private thistype next
private thistype prev
method operator pool takes nothing returns IPool
return this.iPool
endmethod
method operator pool= takes IPool ip returns nothing
if ip != 0 then
call ip.lock()
endif
if this.iPool != 0 then
call this.iPool.unlock()
endif
set this.iPool = ip
endmethod
static method create takes integer totalWeight returns thistype
local thistype this = Create()
set this.next = this
set this.prev = this //I'm my own best friend
set this.weight = totalWeight
return this
endmethod
method destroy takes nothing returns nothing
local thistype curr = this
if this.next == -1 then
debug call BJDebugMsg("SubPool Error: Attempt to double-free!")
return
endif
loop
set curr = curr.next
call Destroy(curr) //destroy all the things
exitwhen curr == this
endloop
set this.next = -1
set this.pool = 0
if this.nest != 0 then
if data(this.nest).locks == 1 then
call this.nest.destroy()
else
call Unlock(this.nest)
endif
set this.nest = 0
endif
call data.tar[this].flush()
endmethod
method lock takes nothing returns nothing
call Lock(this)
endmethod
method unlock takes nothing returns nothing
if Unlock(this) then
call this.destroy()
endif
endmethod
method operator subPool takes nothing returns thistype //need to return thistype and not IPool :P
return this.nest
endmethod
method operator subPool= takes thistype ip returns nothing
if this == ip then
debug call BJDebugMsg("SubPool Error: Don't set a subPool within itself. Use the .copy() method, instead.")
return
endif
if ip != 0 then
call ip.lock()
endif
if this.nest != 0 then
call this.nest.unlock()
endif
set this.nest = ip
endmethod
method add takes integer val, integer lbs returns nothing
local thistype new
if lbs <= 0 then
debug call BJDebugMsg("SubPool Error: Don't add a value without weight")
return
endif
if data.tar[this].has(val) then
set new = data.tar[this][val]
set new.weight = new.weight + lbs
return
endif
set new = Create()
set new.prev = this.prev
set this.prev.next = new
set this.prev = new
set new.next = this
set new.value = val
set new.weight = lbs
set data.tar[this][val] = new
endmethod
method contains takes integer val returns boolean
return data.tar[this].has(val)
endmethod
method operator [] takes integer val returns thistype
return data.tar[this][val]
endmethod
method operator []= takes integer val, integer newWeight returns nothing
set this[val].weight = newWeight
endmethod
method remove takes integer val returns nothing
local thistype node = this[val]
if Destroy(node) then
set node.prev.next = node.next
set node.next.prev = node.prev
call data.tar[this].remove(val)
else
debug call BJDebugMsg("SubPool Error: Attempt to remove non-added value")
endif
endmethod
method getItem takes nothing returns integer
local thistype curr = this
local integer i = GetRandomInt(1, this.weight)
loop
set curr = curr.next
set i = i - curr.weight
exitwhen i <= 0
endloop
if curr == this then
if this.nest != 0 then
set i = this.nest.getItem()
else
set i = 0
endif
if i == 0 and this.pool != 0 then //if no low-probability item could be found...
set i = this.pool.getItem() //pick a random int from main pool
endif
else
set i = curr.value
endif
return i
endmethod
method copy takes nothing returns thistype
local thistype new = .create(this.weight)
local thistype curr = this
set new.pool = this.iPool
set new.subPool = this.nest
loop
set curr = curr.next
exitwhen curr == this
call new.add(curr.value, curr.weight)
endloop
return new
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing
local thistype curr = this
local string s = "SubPool: |cffffcc33Instance: " + I2S(this) + ", TotalWeight: " + I2S(this.weight) + ", Indices: "
if curr.next == this then
call BJDebugMsg("SubPool is empty!")
//return
endif
loop
set curr = curr.next
exitwhen curr == this
set s = s + "[" + I2S(curr.value) + "]<" + I2S(curr.weight) + ">"
endloop
call BJDebugMsg(s + "|r")
if this.nest != 0 then
call this.nest.print()
endif
if this.iPool != 0 then
call this.iPool.print()
endif
endmethod
endif
endstruct
endlibrary
library DamageEvent/* v 1.3.0.0
**********************************************************************************
*
* Physical Damage Detection Engine
* --------------------------------
* By looking_for_help aka eey
*
* This system is able to detect, modify and deal damage. It can differentiate
* between physical and spell damage, as well as block, reduce or increase the
* applied damage to a desired value. You can also allocate damage events from
* running damage events.
*
**********************************************************************************
*
* Implementation
* --------------
* 1. Copy this trigger to your map. With the AddDamageHandler function you can
* add as many handlers as you like (compare the OnDamage scope).
* 2. Copy the two custom abilities to your map and make sure they have the
* correct ID in the globals variable block.
* 3. Go to the locust swarm ability and invert its damage return portion
* from (default) 0.75 to -0.75.
* 4. Remove the spell damage reduction ability from the spell damage reduction
* items you use (runed bracers). You can configure the resistance of this
* item in the globals block, modifying BRACERS_SPELL_DAMAGE_REDUCTION.
*
**********************************************************************************
*
* Important Notes
* ---------------
* 1. Life Drain does not work with this system, so you should use a triggered
* version of this spell if you want to use it.
* 2. Same for Finger of Death, if you want to use this spell bug free with this
* system, you should use a triggered version of it.
* 3. If you use damage modifiers by setting the damage amount variable, you have
* to use GetUnitLife, GetUnitMaxLife and SetUnitLife instead of GetWidgetLife,
* GetUnitState for UNIT_STATE_MAX_LIFE and SetWidgetLife in your whole map to
* ensure there occure no bugs.
* 4. The boolean USE_SPELL_RESISTANCE_AUTO_DETECT is only neccessary set to true
* if you want to use a customized damage table with spell damage resistance
* above 100%. If this is not the case, it should be set to false, as the
* system is faster then and still works correct.
* 5. As already mentioned you can't use the spell reduction ability when using this
* system (runed bracers and elunes grace). If you want to use them, you can
* trigger them by using the damage modifiers. Runed bracers is already considered
* in this system, so you don't have to code it.
*
**********************************************************************************
*
* System API
* ----------
* unit PDDS.target
* - In this global unit variable, the damaged unit is saved. Don't write any-
* thing into this variable, use it as readonly.
*
* unit PDDS.source
* - In this global unit variable, the damage source is saved. Don't write any-
* thing into this variable, use it as readonly.
*
* real PDDS.amount
* - In this global real variable, the amount of damage is saved. This amount
* can be modified by simply setting it to the desired amount that should be
* applied to the target. Set the amount to 0.0 to block the damage completly.
* Negative values will result in heal.
*
* integer PDDS.damageType
* - In this global integer variable, the damage type of the current damage is
* saved. Use it to differentiate between physical, spell and code damage. The
* variable can takes the values PHYSICAL == 0, SPELL == 1 and CODE == 2. Don't
* write anything into this variable, use it as readonly.
*
* function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount,
* boolean attack, boolean ranged, attacktype localAttackType,
* damagetype localDamageType, weapontype localWeaponType
* returns boolean
* - Use this function to allocate attacks from a running damage event. You can use
* it in exactly the same way as the standard native UnitDamageTarget. The function
* is designed recursive, so you can allocate as many attacks as you want. Do not
* use this function outside of a damage event.
*
* function GetUnitLife takes unit u returns real
* - Use this function instead of the GetWidgetLife native. It ensures that you
* get the correct health value, even when damage modifiers are applied. If
* you don't use damage modifiers at all, you don't need this function.
*
* function GetUnitMaxLife takes unit u returns real
* - Use this function instead of the GetUnitState(u, UNIT_STATE_MAX_LIFE) native.
* It will return the correct value, even when damage modifiers are applied. If
* you don't use damage modifiers at all, you don't need this function.
*
* function SetUnitLife takes unit u, real newLife returns nothing
* - Use this function instead of the SetWidgetLife(u, newLife) native if you use
* damage modifiers in your map. Same rules as for the GetUnitMaxLife and the
* GetUnitMaxLife functions.
*
* function AddDamageHandler takes code damageHandler returns nothing
* - Allows you to add a damage handler function to the system. Compare the
* OnDamage scope for an example usage.
*
* function RemoveDamageHandler takes code damageHandler returns nothing
* - Allows you to remove a damage handler function from the system dynamic-
* ally. If you added the same handler function multiple times to the sys-
* tem, this function will remove all of these equal functions.
*
*********************************************************************************/
globals
/*************************************************************************
* Customizable globals
*************************************************************************/
private constant integer DAMAGE_TYPE_DETECTOR = 'A000'
private constant integer SET_MAX_LIFE = 'A001'
private constant integer SPELL_DAMAGE_REDUCTION_ITEM = 'brac'
private constant boolean USE_SPELL_RESISTANCE_AUTO_DETECT = false
private constant real ETHEREAL_DAMAGE_FACTOR = 1.66
private constant real BRACERS_SPELL_DAMAGE_REDUCTION = 0.33
private constant real TRIGGER_CLEANUP_PERIOD = 10.0
/*************************************************************************
* End of Customizable globals
*************************************************************************/
constant integer PHYSICAL = 0
constant integer SPELL = 1
constant integer CODE = 2
constant real UNIT_MIN_LIFE = 0.406
private constant attacktype ATTACK_TYPE_UNIVERSAL = ConvertAttackType(7)
private hashtable h
private real pureAmount
private trigger damageEvent
private trigger damageHandler
private trigger runAllocatedAttacks
private integer allocatedAttacks
private integer totalAllocs
private integer allocCounter
private real damageEventTrigger
endglobals
struct PDDS extends array
static unit source
static unit target
static real amount
static integer damageType
endstruct
/******************************************************************************
* User functions
******************************************************************************/
function AddDamageHandler takes code func returns nothing
local integer id = GetHandleId(Condition(func))
local integer index = 0
// Loop to manage equal damage handlers
loop
exitwhen ( LoadTriggerConditionHandle(h, id, index) == null )
set index = index + 1
endloop
// Store the desired damage handler function
call SaveTriggerConditionHandle(h, id, index, TriggerAddCondition(damageHandler, Filter(func)))
endfunction
function RemoveDamageHandler takes code func returns nothing
local integer id = GetHandleId(Condition(func))
local integer index = 0
// Loop through all equal damage handlers
loop
exitwhen ( LoadTriggerConditionHandle(h, id, index) == null )
call TriggerRemoveCondition(damageHandler, LoadTriggerConditionHandle(h, id, index))
set index = index + 1
endloop
// Clean things up
call FlushChildHashtable(h, id)
endfunction
function GetUnitLife takes unit u returns real
local boolean duringModification
local integer uId = GetHandleId(u)
local real health
local real storedHealth = LoadReal(h, uId, 2)
local real storedDamage = LoadReal(h, uId, 1)
// Check if the unit is being rescued from damage
set duringModification = GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0
if duringModification then
call UnitRemoveAbility(u, SET_MAX_LIFE)
endif
// Get the correct health value of the unit
if storedHealth != 0.0 then
set health = storedHealth - storedDamage
else
set health = GetWidgetLife(u) - storedDamage
endif
// Restore the rescue ability and return
if duringModification then
call UnitAddAbility(u, SET_MAX_LIFE)
endif
return health
endfunction
function GetUnitMaxLife takes unit u returns real
local real maxHealth
// Check if the unit is being rescued from damage
if GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0 then
call UnitRemoveAbility(u, SET_MAX_LIFE)
set maxHealth = GetUnitState(u, UNIT_STATE_MAX_LIFE)
call UnitAddAbility(u, SET_MAX_LIFE)
return maxHealth
endif
// If the unit isn't being rescued, use the standard native
return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
function SetUnitLife takes unit u, real newLife returns nothing
local integer targetId
local integer oldTimerId
local real oldHealth
// Check if the unit is being rescued from damage
if GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0 then
call UnitRemoveAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, newLife)
call UnitAddAbility(u, SET_MAX_LIFE)
// Get the unit specific timer information
set targetId = GetHandleId(u)
set oldHealth = LoadReal(h, targetId, 0)
// Update the unit specific timer information
if oldHealth != 0.0 then
set oldTimerId = LoadInteger(h, targetId, 3)
call SaveReal(h, targetId, 2, newLife)
call SaveReal(h, targetId, 0, newLife)
call SaveReal(h, oldTimerId, 4, newLife)
endif
return
endif
// If the unit isn't being rescued, use the standard native
call SetWidgetLife(u, newLife)
endfunction
function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, boolean attack, /*
*/boolean ranged, attacktype localAttackType, damagetype localDamageType, /*
*/weapontype localWeaponType returns boolean
// Avoid infinite loop due to recursion
if PDDS.damageType == CODE then
return false
endif
// Avoid allocating attacks on units that are about to be killed
if ( localTarget == PDDS.target and GetUnitLife(localTarget) - PDDS.amount < UNIT_MIN_LIFE ) then
return false
endif
// Store all damage parameters determined by the user
set allocatedAttacks = allocatedAttacks + 1
call SaveUnitHandle(h, allocatedAttacks, 0, localSource)
call SaveUnitHandle(h, allocatedAttacks, 1, localTarget)
call SaveReal(h, allocatedAttacks, 2, localAmount)
call SaveBoolean(h, allocatedAttacks, 3, attack)
call SaveBoolean(h, allocatedAttacks, 4, ranged)
call SaveInteger(h, allocatedAttacks, 5, GetHandleId(localAttackType))
call SaveInteger(h, allocatedAttacks, 6, GetHandleId(localDamageType))
call SaveInteger(h, allocatedAttacks, 7, GetHandleId(localWeaponType))
// Return true if the damage was allocated
return true
endfunction
/******************************************************************************
* Sub functions
******************************************************************************/
private function DealFixDamage takes unit source, unit target, real pureAmount returns nothing
local real MAX_DAMAGE = 1000000.0
local real beforeHitpoints
local real newHitpoints
// Ensure the amount is positive
if pureAmount < 0 then
set pureAmount = -pureAmount
endif
// Save the targets hitpoints
set beforeHitpoints = GetWidgetLife(target)
set newHitpoints = beforeHitpoints - pureAmount
// Apply the desired, fixed amount
if newHitpoints >= UNIT_MIN_LIFE then
call SetUnitState(target, UNIT_STATE_LIFE, newHitpoints)
else
if ( IsUnitType(target, UNIT_TYPE_ETHEREAL) == false ) then
call SetWidgetLife(target, 1.0)
call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, ATTACK_TYPE_UNIVERSAL, DAMAGE_TYPE_UNIVERSAL, null)
else
call UnitRemoveAbility(target, DAMAGE_TYPE_DETECTOR)
call SetWidgetLife(target, 1.0)
call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
endif
endif
endfunction
private function GetUnitSpellResistance takes unit u returns real
local real originalHP
local real beforeHP
local real afterHP
local real resistance
local real DUMMY_DAMAGE = 100
local real DUMMY_FACTOR = 0.01
// Deal spell damage in order to get the units resistance
call UnitRemoveAbility(u, DAMAGE_TYPE_DETECTOR)
set originalHP = GetWidgetLife(u)
call UnitAddAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, 20000.0)
set beforeHP = GetWidgetLife(u)
call DisableTrigger(damageEvent)
call UnitDamageTarget(PDDS.source, u, DUMMY_DAMAGE, true, false, null, DAMAGE_TYPE_UNIVERSAL, null)
call EnableTrigger(damageEvent)
set afterHP = GetWidgetLife(u)
call UnitRemoveAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, originalHP)
call UnitAddAbility(u, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(u, true, DAMAGE_TYPE_DETECTOR)
// Calculate this resistance
set resistance = DUMMY_FACTOR*(beforeHP - afterHP)
// If the resistance was greater than 100%, return it
if resistance > 1.0 then
return resistance
else
return 1.0
endif
endfunction
private function UnitHasItemOfType takes unit u, integer itemId returns integer
local integer index = 0
local item indexItem
// Check if the target has a spell damage reducing item
loop
set indexItem = UnitItemInSlot(u, index)
if ( indexItem != null ) and ( GetItemTypeId(indexItem) == itemId ) then
set indexItem = null
return index + 1
endif
set index = index + 1
exitwhen index >= bj_MAX_INVENTORY
endloop
set indexItem = null
return 0
endfunction
/******************************************************************************
* Damage Engine
******************************************************************************/
private function AfterDamage takes nothing returns nothing
local timer time = GetExpiredTimer()
local integer id = GetHandleId(time)
local unit localSource = LoadUnitHandle(h, id, 0)
local unit localTarget = LoadUnitHandle(h, id, 1)
local real amount = LoadReal(h, id, 3)
local real originalHealth = LoadReal(h, id, 4)
// If the damage was modified, restore units health
if originalHealth != 0.0 then
call UnitRemoveAbility(localTarget, SET_MAX_LIFE)
call SetWidgetLife(localTarget, originalHealth)
endif
// Apply the desired amount of damage
if amount > 0.0 then
call DisableTrigger(damageEvent)
call DealFixDamage(localSource, localTarget, amount)
call EnableTrigger(damageEvent)
else
call SetWidgetLife(localTarget, originalHealth - amount)
endif
// Clean things up
call FlushChildHashtable(h, id)
call FlushChildHashtable(h, GetHandleId(localTarget))
call DestroyTimer(time)
set time = null
set localSource = null
set localTarget = null
endfunction
private function DamageEngine takes nothing returns nothing
local timer time
local integer id
local real rawAmount
local real originalHealth
local integer targetId
local integer oldTimerId
local real oldHealth
local real oldOriginalHealth
local real oldAmount
// Set damage variables
set rawAmount = GetEventDamage()
if rawAmount == 0.0 then
return
endif
set PDDS.source = GetEventDamageSource()
set PDDS.target = GetTriggerUnit()
// Determine the damage type
if rawAmount > 0.0 then
if PDDS.damageType != CODE then
set PDDS.damageType = PHYSICAL
endif
set PDDS.amount = rawAmount
else
if PDDS.damageType != CODE then
set PDDS.damageType = SPELL
endif
set PDDS.amount = -rawAmount
endif
// Register spell reduction above 100%
static if USE_SPELL_RESISTANCE_AUTO_DETECT then
set PDDS.amount = GetUnitSpellResistance(PDDS.target)*PDDS.amount
else
if ( IsUnitType(PDDS.target, UNIT_TYPE_ETHEREAL) and IsUnitEnemy(PDDS.target, GetOwningPlayer(PDDS.source)) and rawAmount < 0.0 ) then
set PDDS.amount = ETHEREAL_DAMAGE_FACTOR*PDDS.amount
endif
endif
// Register spell damage reducing items like runed bracers
if ( IsUnitType(PDDS.target, UNIT_TYPE_HERO) and UnitHasItemOfType(PDDS.target, SPELL_DAMAGE_REDUCTION_ITEM) > 0 ) and rawAmount < 0.0 then
set PDDS.amount = (1 - BRACERS_SPELL_DAMAGE_REDUCTION)*PDDS.amount
endif
// Save health and damage variables
if PDDS.damageType != CODE then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
endif
set pureAmount = PDDS.amount
set originalHealth = GetWidgetLife(PDDS.target)
set oldTimerId = 0
// Call damage handlers
set damageEventTrigger = 0.0
set damageEventTrigger = 1.0
set damageEventTrigger = 0.0
// If the damage was modified, apply the rescue ability
if PDDS.amount != pureAmount then
call UnitAddAbility(PDDS.target, SET_MAX_LIFE)
call SetWidgetLife(PDDS.target, GetWidgetLife(PDDS.target) + pureAmount)
endif
// Check if a previous timer didn't yet expire
set targetId = GetHandleId(PDDS.target)
set oldHealth = LoadReal(h, targetId, 0)
// If this is the case, update the timer information
if oldHealth != 0.0 then
set oldTimerId = LoadInteger(h, targetId, 3)
set oldOriginalHealth = LoadReal(h, targetId, 2)
set oldAmount = LoadReal(h, targetId, 1)
set originalHealth = oldOriginalHealth - oldAmount
call SaveReal(h, oldTimerId, 4, oldOriginalHealth)
endif
// Call after damage event if damage was spell, modified, code or parallel
if ( rawAmount < 0.0 or pureAmount != PDDS.amount or oldTimerId != 0 or allocatedAttacks > 0 ) then
set time = CreateTimer()
set id = GetHandleId(time)
// Save handles for after damage event
call SaveUnitHandle(h, id, 0, PDDS.source)
call SaveUnitHandle(h, id, 1, PDDS.target)
call SaveReal(h, id, 2, pureAmount)
call SaveReal(h, id, 3, PDDS.amount)
call SaveReal(h, id, 4, originalHealth)
// Save this extra to manage parallel damage instances
call SaveReal(h, targetId, 0, GetWidgetLife(PDDS.target))
call SaveReal(h, targetId, 1, PDDS.amount)
call SaveReal(h, targetId, 2, originalHealth)
call SaveInteger(h, targetId, 3, id)
// Avoid healing of negative spell damage
if rawAmount < 0.0 then
call DisableTrigger(damageEvent)
call DealFixDamage(PDDS.source, PDDS.target, -rawAmount)
if ( originalHealth - PDDS.amount < UNIT_MIN_LIFE ) then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
call DealFixDamage(PDDS.source, PDDS.target, PDDS.amount)
endif
call EnableTrigger(damageEvent)
endif
// Guarantee unit exploding
if originalHealth - PDDS.amount < UNIT_MIN_LIFE then
if rawAmount > 0.0 then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
call SetWidgetLife(PDDS.target, UNIT_MIN_LIFE)
endif
endif
// Start the after damage event
call TimerStart(time, 0.0, false, function AfterDamage)
endif
// Handle allocated attacks from UnitDamageTargetEx
if totalAllocs == 0 then
set totalAllocs = allocatedAttacks
endif
if allocatedAttacks > 0 then
set allocatedAttacks = allocatedAttacks - 1
set allocCounter = allocCounter + 1
call TriggerEvaluate(runAllocatedAttacks)
endif
// Reset all required variables
set totalAllocs = 0
set allocCounter = -1
set PDDS.damageType = -1
endfunction
/******************************************************************************
* Initialization
******************************************************************************/
private function RestoreTriggers takes nothing returns nothing
local unit enumUnit = GetEnumUnit()
// Re-register units that are alive
if GetUnitTypeId(enumUnit) != 0 then
call TriggerRegisterUnitEvent(damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
endif
set enumUnit = null
endfunction
private function ClearMemory_Actions takes nothing returns nothing
local group g = CreateGroup()
local code c = function DamageEngine
// Reset the damage event
call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
call ResetTrigger(damageEvent)
call DestroyTrigger(damageEvent)
set damageEvent = null
// Rebuild it then
set damageEvent = CreateTrigger()
call TriggerAddCondition(damageEvent, Filter(c))
call ForGroup(g, function RestoreTriggers)
// Clean things up
call DestroyGroup(g)
set g = null
set c = null
endfunction
private function MapInit takes nothing returns nothing
local unit enumUnit = GetEnumUnit()
// Register units on map initialization
call UnitAddAbility(enumUnit, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(enumUnit, true, DAMAGE_TYPE_DETECTOR)
call TriggerRegisterUnitEvent(damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
set enumUnit = null
endfunction
private function UnitEntersMap takes nothing returns nothing
local unit triggerUnit = GetTriggerUnit()
// Register units that enter the map
if ( GetUnitAbilityLevel(triggerUnit, DAMAGE_TYPE_DETECTOR) < 1 ) then
call UnitAddAbility(triggerUnit, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(triggerUnit, true, DAMAGE_TYPE_DETECTOR)
call TriggerRegisterUnitEvent(damageEvent, triggerUnit, EVENT_UNIT_DAMAGED)
endif
set triggerUnit = null
endfunction
private function RunAllocatedAttacks takes nothing returns nothing
local integer localAllocAttacks = allocatedAttacks + 1
// Calculate the correct sequence of allocated attacks
set localAllocAttacks = localAllocAttacks - totalAllocs + 1 + 2*allocCounter
// Deal code damage if the unit isn't exploding
set PDDS.damageType = CODE
if GetUnitLife(LoadUnitHandle(h, localAllocAttacks, 1)) >= UNIT_MIN_LIFE then
call UnitDamageTarget(LoadUnitHandle(h, localAllocAttacks, 0), LoadUnitHandle(h, localAllocAttacks, 1), /*
*/LoadReal(h, localAllocAttacks, 2), LoadBoolean(h, localAllocAttacks, 3), /*
*/LoadBoolean(h, localAllocAttacks, 4), ConvertAttackType(LoadInteger(h, /*
*/localAllocAttacks, 5)), ConvertDamageType(LoadInteger(h, localAllocAttacks, 6)), /*
*/ConvertWeaponType(LoadInteger(h, localAllocAttacks, 7)))
else
call FlushChildHashtable(h, localAllocAttacks - 1)
endif
// Clean things up
call FlushChildHashtable(h, localAllocAttacks)
endfunction
private module Inits
private static method onInit takes nothing returns nothing
local group g = CreateGroup()
local region r = CreateRegion()
local trigger UnitEnters = CreateTrigger()
local trigger ClearMemory = CreateTrigger()
local code cDamageEngine = function DamageEngine
local code cUnitEnters = function UnitEntersMap
local code cClearMemory = function ClearMemory_Actions
local code cRunAllocatedAttacks = function RunAllocatedAttacks
// Initialize variables
set h = InitHashtable()
set damageEvent = CreateTrigger()
set damageHandler = CreateTrigger()
set PDDS.damageType = -1
set allocatedAttacks = 0
set runAllocatedAttacks = CreateTrigger()
set totalAllocs = 0
set allocCounter = -1
// Register units on map initialization
call TriggerRegisterVariableEvent(damageHandler, SCOPE_PRIVATE + "damageEventTrigger", EQUAL, 1.0)
call TriggerAddCondition(damageEvent, Filter(cDamageEngine))
call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
call ForGroup(g, function MapInit)
// Register units that enter the map
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(UnitEnters, r, null)
call TriggerAddCondition(UnitEnters, Filter(cUnitEnters))
// Register trigger for allocated attacks
call TriggerAddCondition(runAllocatedAttacks, Filter(cRunAllocatedAttacks))
// Clear memory leaks
call TriggerRegisterTimerEvent(ClearMemory, TRIGGER_CLEANUP_PERIOD, true)
call TriggerAddCondition(ClearMemory, Filter(cClearMemory))
// Clean things up
call DestroyGroup(g)
set UnitEnters = null
set ClearMemory = null
set cDamageEngine = null
set cUnitEnters = null
set cClearMemory = null
set cRunAllocatedAttacks = null
set g = null
set r = null
endmethod
endmodule
private struct Init extends array
implement Inits
endstruct
endlibrary
library UnitEnumUtils uses UnitDex
globals
integer array ENUMED_UNIT_ID
private group enumGroup = CreateGroup()
private integer array enumedUnitId
private integer enumedUnitCount = 0
private integer node
private integer temp
private boolexpr enumExpr
private unit enumUnit
endglobals
private function EnumUnitsExpr takes nothing returns boolean
set node = GetUnitId(GetFilterUnit())
set ENUMED_UNIT_ID[temp] = node
set temp = node
return true
endfunction
private function EnumCounterFilter takes nothing returns boolean
if enumedUnitCount > 0 then
set enumedUnitCount = enumedUnitCount - 1
set node = GetUnitId(GetFilterUnit())
set ENUMED_UNIT_ID[temp] = node
set temp = node
return true
endif
return false
endfunction
private function RandomEnumCounterFilter takes nothing returns boolean
set enumedUnitCount = enumedUnitCount + 1
set enumedUnitId[enumedUnitCount] = GetUnitId(GetFilterUnit())
return true
endfunction
function EnumUnitsInRange takes real x, real y, real radius, boolexpr filter returns nothing
set node = 0
set temp = 0
if filter == null then
call GroupEnumUnitsInRange(enumGroup, x, y, radius, null)
loop
set enumUnit = FirstOfGroup(enumGroup)
exitwhen enumUnit == null
call GroupRemoveUnit(enumGroup, enumUnit)
set node = GetUnitId(enumUnit)
set ENUMED_UNIT_ID[temp] = node
set temp = node
endloop
else
set enumExpr = And(filter, Filter(function EnumUnitsExpr))
call GroupEnumUnitsInRange(enumGroup, x, y, radius, filter)
call DestroyBoolExpr(enumExpr)
endif
set ENUMED_UNIT_ID[node] = 0
endfunction
function EnumUnitsInRangeCounted takes real x, real y, real radius, integer maxCount, boolexpr filter returns nothing
set node = 0
set temp = 0
if filter == null then
call GroupEnumUnitsInRange(enumGroup, x, y, radius, null)
loop
exitwhen maxCount == 0
set enumUnit = FirstOfGroup(enumGroup)
exitwhen enumUnit == null
call GroupRemoveUnit(enumGroup, enumUnit)
set node = GetUnitId(enumUnit)
set ENUMED_UNIT_ID[temp] = node
set temp = node
set maxCount = maxCount - 1
endloop
else
set enumedUnitCount = maxCount
set enumExpr = And(filter, Filter(function EnumCounterFilter))
call GroupEnumUnitsInRange(enumGroup, x, y, radius, enumExpr)
call DestroyBoolExpr(enumExpr)
endif
set ENUMED_UNIT_ID[node] = 0
endfunction
function EnumRandomUnitsInRangeCounted takes real x, real y, real radius, integer maxCount, boolexpr filter returns nothing
set enumedUnitCount = 0
set enumExpr = And(filter, Filter(function RandomEnumCounterFilter))
call GroupEnumUnitsInRange(enumGroup, x, y, radius, filter)
call DestroyBoolExpr(enumExpr)
set node = 0
if maxCount > enumedUnitCount then
set maxCount = enumedUnitCount
endif
loop
exitwhen maxCount == 0
set temp = GetRandomInt(1, enumedUnitCount)
set ENUMED_UNIT_ID[node] = enumedUnitId[temp]
set node = ENUMED_UNIT_ID[node]
set enumedUnitId[temp] = enumedUnitId[enumedUnitCount]
set enumedUnitCount = enumedUnitCount - 1
set maxCount = maxCount - 1
endloop
set ENUMED_UNIT_ID[node] = 0
endfunction
endlibrary
library MapBounds /* v1.1.0
Based on Nestharus's WorldBounds
*///! novjass
|=====|
| API |
|=====|
struct MapBounds extends array // Refers to initial playable map bounds
struct WorldBounds extends array // Refers to world bounds
readonly static real centerX
readonly static real centerY
readonly static real minX
readonly static real minY
readonly static real maxX
readonly static real maxY
readonly static rect rect
readonly static region region
static method getArea takes nothing returns real
static method getRandomX takes nothing returns real
static method getRandomY takes nothing returns real/*
- Returns a random coordinate inside the rect
*/static method getBoundedXWithMargin takes real x, real margin returns real
static method getBoundedYWithMargin takes real y, real margin returns real/*
*/static method getBoundedX takes real x returns real
static method getBoundedY takes real y returns real/*
- Returns a coordinate that is inside the bounds
*/static method containsX takes real x returns boolean
static method containsY takes real y returns boolean/*
- Checks if the bound contains the input coordinate
*/
//! endnovjass
private module CommonMembers
readonly static real centerX
readonly static real centerY
readonly static real minX
readonly static real minY
readonly static real maxX
readonly static real maxY
readonly static rect rect
readonly static region region
static method getArea takes nothing returns real
return (maxX - minX)*(maxY - minY)
endmethod
static method getRandomX takes nothing returns real
return GetRandomReal(minX, maxX)
endmethod
static method getRandomY takes nothing returns real
return GetRandomReal(minY, maxY)
endmethod
static method getBoundedXWithMargin takes real x, real margin returns real
if x < (minX + margin) then
return minX + margin
elseif x > (maxX - margin) then
return maxX - margin
endif
return x
endmethod
static method getBoundedYWithMargin takes real y, real margin returns real
if y < (minY + margin) then
return minY + margin
elseif y > (maxY - margin) then
return maxY - margin
endif
return y
endmethod
static method getBoundedX takes real x returns real
return getBoundedXWithMargin(x, 0.00)
endmethod
static method getBoundedY takes real y returns real
return getBoundedYWithMargin(y, 0.00)
endmethod
static method containsX takes real x returns boolean
return getBoundedX(x) == x
endmethod
static method containsY takes real y returns boolean
return getBoundedY(y) == y
endmethod
private static method onInit takes nothing returns nothing
set region = CreateRegion()
set rect = getRect()
set minX = GetRectMinX(rect)
set minY = GetRectMinY(rect)
set maxX = GetRectMaxX(rect)
set maxY = GetRectMaxY(rect)
set centerX = (minX + maxX)/2.00
set centerY = (minY + maxY)/2.00
call RegionAddRect(region, rect)
endmethod
endmodule
struct MapBounds extends array
private static method getRect takes nothing returns rect
return bj_mapInitialPlayableArea
endmethod
implement CommonMembers
endstruct
struct WorldBounds extends array
private static method getRect takes nothing returns rect
return GetWorldBounds()
endmethod
implement CommonMembers
endstruct
endlibrary
library IllusionEditor /* https://www.hiveworkshop.com/threads/illusion-editor-v1-5d-vjass-gui-support.286441/
*/requires /*
*/DamageEvent /* https://www.hiveworkshop.com/threads/system-physical-damage-detection.228456/#post-2273240/
*/optional Alloc /* https://www.hiveworkshop.com/threads/unique-id-allocation.260897/
*/optional Table /* https://www.hiveworkshop.com/threads/snippet-new-table.188084/#post-1826447/
*/optional TimerUtils /* http://www.wc3c.net/showthread.php?p=1020244
*/optional UnitRecycler /* https://www.hiveworkshop.com/threads/snippet-unit-recycler.286701/#post-3078532/
*/optional DummyRecycler /* https://www.hiveworkshop.com/threads/dummy-recycler-v1-25.277659/
*///! novjass
_______________________
| |
| Illusion Editor v1.5d |
| Written by AGD |
|_______________________|
/*
This system gives you freedom and ease in creating illusions. With this, you
can now create illusions of any type as easily as creating units or make an
illusion copy from an already existing unit, with the ability to change many
of their aspects dynamically.
Hint:
- Use zero or any negative value for duration upon creation to make a permanent illusion
- Upon updating an illusion's duration, a zero value would make it permanent while a
negative value would kill it instantly
To Import:
- Copy all the neccessary Object Editor data or alternatively use the object merger
- Copy this trigger/trigger category together with the requirements
- Configure the configurable object rawcodes
*/
|============|
| Struct API |
|============|
struct Illusion/*
*/readonly static unit lastCreated /* The last created illusion unit
*/readonly static unit triggerUnit /* The dying illusion unit (Event response)
*/readonly unit unit /* The illusion unit
*/readonly unit baseUnit /* The unit source from where the illusion was created
*/readonly real elapsed /* The elapsed time of the illusion
*/readonly real remaining /* The remaining time of the illusion
*/real duration /* The duration of the illusion's timed life
*/real damageFactor /* The damage dealt factor
*/real receiveFactor /* The damage received factor
*/string deathEffect /* The special effect played when the illusion dies
*/static method create takes player whichPlayer, integer unitId, real duration, real damageFactor, real receiveFactor, real x, real y, real angle returns Illusion/*
- Creates a new illusion unit of a specific type
*/static method createCopy takes player whichPlayer, unit baseUnit, real duration, real damageFactor, real receiveFactor, real x, real y, real angle returns Illusion/*
- Creates a new illusion copy of a given unit
*/static method operator [] takes unit u returns Illusion/*
- Returns the Illusion instance of the illusion unit
*/method destroy takes nothing returns nothing/*
- Destroys an Illusion instance
*/method addDuration takes real duration returns nothing/*
- Adds extra time to the illusion's duration
*/static method registerDeathHandler takes code c returns nothing/*
*/static method unregisterDeathHandler takes code c returns nothing/*
- Registers/Unregisters an illusion death handler code
*/static method clearDeathHandlers takes nothing returns nothing/*
- Clears all illusion death handlers
*/
|==============|
| Function API |
|==============|
function CreateIllusion takes player whichPlayer, integer unitId, real duration, real damageFactor, real receiveFactor, real x, real y, real angle returns unit/*
- Creates an illusion of a specific unit type
*/function CreateIllusionCopy takes player whichPlayer, unit u, real duration, real damageFactor, real receiveFactor, real x, real y, real angle returns unit/*
- Creates an illusion copy of a unit which possesses the same abilities and items as the original unit
*/function AddIllusionDuration takes unit u, real extraTime returns nothing/*
- Adds an extra duration to an illusion's timed life
(This only works for illusions created by this system)
*/function SetIllusionDuration takes unit u, real time returns nothing/*
- Sets the duration of the illusion
(This only works for illusions created by this system)
*/function SetIllusionDamageFactor takes unit u, real factor returns nothing/*
- Sets the damage dealt factor of the illusion
(This only works for illusions created by this system)
*/function SetIllusionReceiveFactor takes unit u, real factor returns nothing/*
- Sets the damage receive factor of the illusion
(This only works for illusions created by this system)
*/function SetIllusionDeathEffect takes unit u, string model returns nothing/*
- Sets the death animation of the illusion
(This only works for illusions created by this system)
*/function GetIllusionElapsedTime takes unit u returns real/*
- Returns the elapsed time of the illusion
(This will return 0 for illusions not created by this system)
*/function GetIllusionRemainingTime takes unit u returns real/*
- Returns the remaining duration of the illusion
(This will return 0 for illusions not created by this system)
*/function GetIllusionDuration takes unit u returns real/*
- Returns the duration of the illusion
(This will return 0 for illusions not created by this system)
*/function GetIllusionDamageFactor takes unit u returns real/*
- Returns the damage dealt factor of the illusion
(This will return 0 for illusions not created by this system)
*/function GetIllusionReceiveFactor takes unit u returns real/*
- Returns the damage received factor of the illusion
(This will return 0 for illusions not created by this system)
*/function GetIllusionBaseUnit takes unit u returns unit/*
- Returns the unit that the illusion is copied from
(Only works for illusion products of CopyUnit())
*/function GetIllusionFlag takes unit u returns boolean/*
- Checks if the illusion is created by this system or not
*/constant function GetLastCreatedIllusion takes nothing returns unit/*
- Returns the last illusion created using this system
*/constant function GetTriggerIllusion takes nothing returns unit/*
- Refers to the dying illusion (Event Response)
*/function RegisterIllusionDeathHandler takes code c returns nothing/*
- Regsiters a code from the illusion death handler
*/function UnregisterIllusionDeathHandler takes code c returns nothing/*
- Unregsiters a code from the illusion death handler
*/function ClearIllusionDeathHandlers takes nothing returns nothing/*
- Clears all illusion death handlers
*///! endnovjass
/*=============================== System Configuration ==============================*/
globals
/*
Rawcode of the item used for creating illusions */
private constant integer ABIL_ID = 'Illu'
/*
Rawcode of the dummy caster */
private constant integer CASTER_ID = 'ictr'
/*
Rawcode of the dummy unit used for attaching the illusion's death special effect */
private constant integer DUMMY_ID = 'idum'
/*
The default death animation of the illusions */
private constant string DEFAULT_DEATH_SFX = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl"
/*
The owning player of the dummy unit */
private constant player DUMMY_OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
/*
Order id of the illusion ability */
private constant integer ORDER_ID = 852274
/*
The prefix of the operation error message when debug mode is enabled */
private constant string ERROR_PREFIX = "|CFFFF0000ERROR: "
endglobals
/*=========================== End of System Configuartion ===========================*/
/* Do not change anything below this line if you're not so sure on what you're doing */
/*===================================================================================*/
native UnitAlive takes unit u returns boolean
private module IllusionStructCore
readonly static unit lastCreated = null
readonly static unit triggerUnit = null
readonly unit unit
readonly unit baseUnit
real damageFactor
real receiveFactor
string deathEffect
private thistype prev
private thistype next
private boolean permanent
private timer lifeTimer
private real prevElapsed
private static integer deathHandlerCount = 0
private static integer unusedDeathEvent = 0
private static unit dummyCaster
private static boolexpr onDeathExpr
private static trigger onSummonTrigger = CreateTrigger()
private static trigger onDeathTrigger = CreateTrigger()
private static trigger onDeathHandler = null
static if LIBRARY_Table then
private static Table table
private static Table handlerTable
else
private static hashtable table = InitHashtable()
endif
implement optional Alloc
debug private static method debug takes string msg returns nothing
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "|CFFFFCC00[IllusionEditor]|R " + ERROR_PREFIX + msg)
debug endmethod
private static method onExpire takes nothing returns nothing
static if LIBRARY_TimerUtils then
local thistype this = GetTimerData(GetExpiredTimer())
elseif LIBRARY_Table then
local thistype this = table[GetHandleId(GetExpiredTimer())]
else
local thistype this = LoadInteger(table, 0, GetHandleId(GetExpiredTimer()))
endif
call KillUnit(this.unit)
if this.unit == lastCreated then
set lastCreated = this.prev.unit
endif
endmethod
static method createCopy takes player whichPlayer, unit source, real duration, real damageDealt, real damageReceived, real x, real y, real facing returns thistype
local thistype this
local thistype last
if not IssueTargetOrderById(dummyCaster, ORDER_ID, source) or lastCreated == null then
debug call debug("Unable to create illusion unit")
return 0
endif
set this = allocate()
set last = thistype(0).prev
set this.next = 0
set this.prev = last
set last.next = this
set thistype(0).prev = this
set this.damageFactor = damageDealt
set this.receiveFactor = damageReceived
set this.deathEffect = DEFAULT_DEATH_SFX
set this.baseUnit = source
set this.unit = lastCreated
static if LIBRARY_Table then
set table[GetHandleId(lastCreated)] = this
else
call SaveInteger(table, 0, GetHandleId(lastCreated), this)
endif
static if LIBRARY_TimerUtils then
set this.lifeTimer = NewTimerEx(this)
else
set this.lifeTimer = CreateTimer()
static if LIBRARY_Table then
set table[GetHandleId(this.lifeTimer)] = this
else
call SaveInteger(table, 0, GetHandleId(this.lifeTimer), this)
endif
endif
if duration > 0.00 then
call TimerStart(this.lifeTimer, duration, false, function thistype.onExpire)
else
set this.permanent = true
call TimerStart(this.lifeTimer, 1000000000.00, false, function thistype.onExpire)
endif
call SetUnitOwner(lastCreated, whichPlayer, true)
call SetUnitFacingTimed(lastCreated, facing, 0.00)
if IsUnitType(lastCreated, UNIT_TYPE_STRUCTURE) then
call SetUnitPosition(lastCreated, x, y)
call SetUnitAnimation(lastCreated, "stand")
else
call SetUnitX(lastCreated, x)
call SetUnitY(lastCreated, y)
endif
call TriggerRegisterUnitStateEvent(onDeathTrigger, lastCreated, UNIT_STATE_LIFE, LESS_THAN, UNIT_MIN_LIFE)
return this
endmethod
static method create takes player whichPlayer, integer rawCode, real duration, real damageDealt, real damageReceived, real x, real y, real angle returns thistype
static if LIBRARY_UnitRecycler then
local unit tempUnit = GetRecycledUnitEx(DUMMY_OWNER, rawCode, x, y, angle)
local thistype this = createCopy(whichPlayer, tempUnit, duration, damageDealt, damageReceived, x, y, angle)
call RecycleUnitEx(tempUnit)
else
local unit tempUnit = CreateUnit(DUMMY_OWNER, rawCode, x, y, angle)
local thistype this = createCopy(whichPlayer, tempUnit, duration, damageDealt, damageReceived, x, y, angle)
call RemoveUnit(tempUnit)
endif
set tempUnit = null
return this
endmethod
method destroy takes nothing returns nothing
local unit u = this.unit
local unit dummy
local unit prevDying
debug if u == null then
debug call debug("Attempted to destroy an already destroyed instance")
debug return
debug endif
call ShowUnit(u, false)
set prevDying = triggerUnit
set triggerUnit = u
call TriggerEvaluate(onDeathHandler)
set triggerUnit = prevDying
set prevDying = null
if this.deathEffect != "" then
static if LIBRARY_DummyRecycler then
set dummy = GetRecycledDummy(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), GetUnitFacing(u))
call DummyAddRecycleTimer(dummy, 2)
elseif LIBRARY_UnitRecycler then
set dummy = GetRecycledUnit(DUMMY_OWNER, DUMMY_ID, GetUnitX(u), GetUnitY(u), GetUnitFacing(u))
call RecycleUnitDelayed(dummy, 2)
call SetUnitFlyHeight(dummy, GetUnitFlyHeight(u), 0)
else
set dummy = CreateUnit(DUMMY_OWNER, DUMMY_ID, GetUnitX(u), GetUnitY(u), GetUnitFacing(u))
call UnitApplyTimedLife(dummy, 'BTLF', 2)
call SetUnitFlyHeight(dummy, GetUnitFlyHeight(u), 0)
endif
call DestroyEffect(AddSpecialEffectTarget(this.deathEffect, dummy, "origin"))
set dummy = null
endif
static if LIBRARY_TimerUtils then
call ReleaseTimer(this.lifeTimer)
else
call DestroyTimer(this.lifeTimer)
static if LIBRARY_Table then
call table.remove(GetHandleId(this.lifeTimer))
else
call RemoveSavedInteger(table, 0, GetHandleId(this.lifeTimer))
endif
endif
static if LIBRARY_Table then
call table.remove(GetHandleId(u))
else
call RemoveSavedInteger(table, 0, GetHandleId(u))
endif
call RemoveUnit(u)
call this.deallocate()
set this.next.prev = this.prev
set this.prev.next = this.next
set this.permanent = false
set this.lifeTimer = null
set this.unit = null
set this.baseUnit = null
set this.damageFactor = 0
set this.receiveFactor = 0
set this.deathEffect = null
set this.prevElapsed = 0.00
set u = null
set unusedDeathEvent = unusedDeathEvent + 1
if unusedDeathEvent == 50 then
set unusedDeathEvent = 0
call DestroyTrigger(onDeathTrigger)
set onDeathTrigger = CreateTrigger()
call TriggerAddCondition(onDeathTrigger, onDeathExpr)
set this = thistype(0).next
loop
exitwhen this == 0
call TriggerRegisterUnitStateEvent(onDeathTrigger, this.unit, UNIT_STATE_LIFE, LESS_THAN, UNIT_MIN_LIFE)
set this = this.next
endloop
endif
endmethod
static method operator [] takes unit u returns thistype
static if LIBRARY_Table then
return table[GetHandleId(u)]
else
return LoadInteger(table, 0, GetHandleId(u))
endif
endmethod
method operator duration= takes real duration returns nothing
local real elapsed = TimerGetElapsed(this.lifeTimer) + this.prevElapsed
if duration > 0.00 then
/* If the input duration < elapsed time, kill the illusion instantly */
if duration < elapsed then
call TimerStart(this.lifeTimer, 0.00, false, function thistype.onExpire)
/* Else, update the illusion's duration */
else
call TimerStart(this.lifeTimer, duration - elapsed, false, function thistype.onExpire)
endif
/* If the given duration <= 0, make the illusion permanent
The timer is not paused so that the elapsed time can
still be retrieved */
else
set this.permanent = true
call TimerStart(this.lifeTimer, 1000000000.00, false, function thistype.onExpire)
endif
set this.prevElapsed = elapsed
endmethod
method operator duration takes nothing returns real
if this.permanent then
return 0.00
endif
return TimerGetTimeout(this.lifeTimer) + this.prevElapsed
endmethod
method operator remaining takes nothing returns real
if this.permanent then
return 0.00
endif
return TimerGetRemaining(this.lifeTimer)
endmethod
method operator elapsed takes nothing returns real
return TimerGetElapsed(this.lifeTimer) + this.prevElapsed
endmethod
method addDuration takes real duration returns nothing
if not this.permanent then
set this.duration = this.duration + duration
endif
endmethod
static method registerDeathHandler takes code c returns nothing
local boolexpr expr = Filter(c)
static if LIBRARY_Table then
debug if handlerTable.handle.has(GetHandleId(expr)) then
debug call debug("Attempted to register an already registered handler code")
debug return
debug endif
else
debug if HaveSavedHandle(table, 0, GetHandleId(expr)) then
debug call debug("Attempted to register an already registered handler code")
debug return
debug endif
endif
if deathHandlerCount == 0 then
set onDeathHandler = CreateTrigger()
endif
set deathHandlerCount = deathHandlerCount + 1
static if LIBRARY_Table then
set handlerTable.triggercondition[GetHandleId(expr)] = TriggerAddCondition(onDeathHandler, expr)
else
call SaveTriggerConditionHandle(table, 0, GetHandleId(expr), TriggerAddCondition(onDeathHandler, expr))
endif
set expr = null
endmethod
static method unregisterDeathHandler takes code c returns nothing
local integer handleId = GetHandleId(Filter(c))
static if LIBRARY_Table then
debug if not handlerTable.handle.has(handleId) then
debug call debug("Attempted to unregister an unregistered handler code")
debug return
debug endif
call TriggerRemoveCondition(onDeathHandler, handlerTable.triggercondition[handleId])
call handlerTable.handle.remove(handleId)
else
debug if not HaveSavedHandle(table, 0, handleId) then
debug call debug("Attempted to unregister an unregistered handler code")
debug return
debug endif
call TriggerRemoveCondition(onDeathHandler, LoadTriggerConditionHandle(table, 0, handleId))
call RemoveSavedHandle(table, 0, handleId)
endif
set deathHandlerCount = deathHandlerCount - 1
if deathHandlerCount == 0 then
call DestroyTrigger(onDeathHandler)
set onDeathHandler = null
endif
endmethod
static method clearDeathHandlers takes nothing returns nothing
call handlerTable.flush()
call DestroyTrigger(onDeathHandler)
set onDeathHandler = null
endmethod
private static method onSummon takes nothing returns nothing
if GetSummoningUnit() == dummyCaster then
set lastCreated = GetSummonedUnit()
endif
endmethod
private static method adjustDamage takes nothing returns nothing
local thistype this = thistype[PDDS.source]
local thistype targ = thistype[PDDS.target]
if this != 0 then
set PDDS.amount = PDDS.amount*this.damageFactor
endif
if targ != 0 then
set PDDS.amount = PDDS.amount*targ.receiveFactor
endif
endmethod
private static method onDeath takes nothing returns nothing
local thistype this = thistype[GetTriggerUnit()]
if this != 0 then
call this.destroy()
endif
endmethod
private static method onInit takes nothing returns nothing
local code onSummon = function thistype.onSummon
local code onDeath = function thistype.onDeath
static if LIBRARY_Table then
set table = Table.create()
set handlerTable = Table.create()
endif
call Preload(DEFAULT_DEATH_SFX)
set onDeathExpr = Filter(onDeath)
set dummyCaster = CreateUnit(DUMMY_OWNER, CASTER_ID, 0.00, 0.00, 0.00)
call UnitAddAbility(dummyCaster, ABIL_ID)
call AddDamageHandler(function thistype.adjustDamage)
call TriggerAddCondition(onDeathTrigger, onDeathExpr)
call TriggerRegisterPlayerUnitEvent(onSummonTrigger, DUMMY_OWNER, EVENT_PLAYER_UNIT_SUMMON, null)
call TriggerAddCondition(onSummonTrigger, Filter(onSummon))
endmethod
endmodule
static if LIBRARY_Alloc then
struct Illusion extends array
implement IllusionStructCore
endstruct
else
struct Illusion
implement IllusionStructCore
endstruct
endif
/*===============================================================================================================*/
function CreateIllusion takes player whichPlayer, integer unitId, real duration, real damageDealt, real damageReceived, real x, real y, real angle returns unit
return Illusion.create(whichPlayer, unitId, duration, damageDealt, damageReceived, x, y, angle).unit
endfunction
function CreateIllusionCopy takes player whichPlayer, unit baseUnit, real duration, real damageDealt, real damageReceived, real x, real y, real angle returns unit
return Illusion.createCopy(whichPlayer, baseUnit, duration, damageDealt, damageReceived, x, y, angle).unit
endfunction
function SetIllusionDuration takes unit u, real duration returns nothing
set Illusion[u].duration = duration
endfunction
function AddIllusionDuration takes unit u, real duration returns nothing
call Illusion[u].addDuration(duration)
endfunction
function SetIllusionDamageFactor takes unit u, real factor returns nothing
set Illusion[u].damageFactor = factor
endfunction
function SetIllusionReceiveFactor takes unit u, real factor returns nothing
set Illusion[u].receiveFactor = factor
endfunction
function SetIllusionDeathEffect takes unit u, string model returns nothing
set Illusion[u].deathEffect = model
endfunction
function GetIllusionElapsedTime takes unit u returns real
return Illusion[u].elapsed
endfunction
function GetIllusionRemainingTime takes unit u returns real
return Illusion[u].remaining
endfunction
function GetIllusionDuration takes unit u returns real
return Illusion[u].duration
endfunction
function GetIllusionDamageFactor takes unit u returns real
return Illusion[u].damageFactor
endfunction
function GetIllusionReceiveFactor takes unit u returns real
return Illusion[u].receiveFactor
endfunction
function GetIllusionBaseUnit takes unit u returns unit
return Illusion[u].baseUnit
endfunction
function GetIllusionFlag takes unit u returns boolean
return Illusion[u] != 0
endfunction
constant function GetLastCreatedIllusion takes nothing returns unit
return Illusion.lastCreated
endfunction
constant function GetTriggerIllusion takes nothing returns unit
return Illusion.triggerUnit
endfunction
function RegisterIllusionDeathHandler takes code c returns nothing
call Illusion.registerDeathHandler(c)
endfunction
function UnregisterIllusionDeathHandler takes code c returns nothing
call Illusion.unregisterDeathHandler(c)
endfunction
function ClearIllusionDeathHandlers takes nothing returns nothing
call Illusion.clearDeathHandlers()
endfunction
endlibrary
library IllusionEditorGUI initializer Init /*
*/uses /*
*/IllusionEditor /* https://www.hiveworkshop.com/bundles/illusion-editor-v1-5d-vjass-gui-support.286441
*/optional ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
*/
private function Operation0 takes nothing returns nothing
set udg_Illusion_Unit = GetLastCreatedIllusion()
endfunction
private function Operation1 takes nothing returns nothing
set udg_Illusion_Unit = CreateIllusion(udg_Illusion_Owner, udg_Illusion_UnitType, udg_Illusion_Duration, udg_Illusion_DamageDealtFactor, udg_Illusion_DamageReceiveFactor, GetLocationX(udg_Illusion_Position), GetLocationY(udg_Illusion_Position), udg_Illusion_Facing)
set udg_Illusion_Operation = 0
set udg_Illusion_Owner = null
set udg_Illusion_UnitType = 0
set udg_Illusion_Duration = 0.00
set udg_Illusion_DamageDealtFactor = 0.00
set udg_Illusion_DamageReceiveFactor = 0.00
set udg_Illusion_Facing = 0.00
endfunction
private function Operation2 takes nothing returns nothing
set udg_Illusion_Unit = CreateIllusionCopy(udg_Illusion_Owner, udg_Illusion_BaseUnit, udg_Illusion_Duration, udg_Illusion_DamageDealtFactor, udg_Illusion_DamageReceiveFactor, GetLocationX(udg_Illusion_Position), GetLocationY(udg_Illusion_Position), udg_Illusion_Facing)
set udg_Illusion_Operation = 0
set udg_Illusion_Owner = null
set udg_Illusion_BaseUnit = null
set udg_Illusion_Duration = 0.00
set udg_Illusion_DamageDealtFactor = 0.00
set udg_Illusion_DamageReceiveFactor = 0.00
set udg_Illusion_Facing = 0.00
endfunction
private function Operation3 takes nothing returns nothing
call SetIllusionDuration(udg_Illusion_Unit, udg_Illusion_Duration)
set udg_Illusion_Operation = 0
endfunction
private function Operation4 takes nothing returns nothing
call AddIllusionDuration(udg_Illusion_Unit, udg_Illusion_Duration)
set udg_Illusion_Operation = 0
endfunction
private function Operation5 takes nothing returns nothing
call SetIllusionDamageFactor(udg_Illusion_Unit, udg_Illusion_DamageDealtFactor)
set udg_Illusion_Operation = 0
endfunction
private function Operation6 takes nothing returns nothing
call SetIllusionReceiveFactor(udg_Illusion_Unit, udg_Illusion_DamageReceiveFactor)
set udg_Illusion_Operation = 0
endfunction
private function Operation7 takes nothing returns nothing
call SetIllusionDeathEffect(udg_Illusion_Unit, udg_Illusion_DeathEffect)
set udg_Illusion_Operation = 0
endfunction
private function Operation8 takes nothing returns nothing
set udg_Illusion_Duration = GetIllusionElapsedTime(udg_Illusion_Unit)
set udg_Illusion_Operation = 0
endfunction
private function Operation9 takes nothing returns nothing
set udg_Illusion_Duration = GetIllusionRemainingTime(udg_Illusion_Unit)
set udg_Illusion_Operation = 0
endfunction
private function Operation10 takes nothing returns nothing
set udg_Illusion_Duration = GetIllusionDuration(udg_Illusion_Unit)
set udg_Illusion_Operation = 0
endfunction
private function Operation11 takes nothing returns nothing
set udg_Illusion_DamageDealtFactor = GetIllusionDamageFactor(udg_Illusion_Unit)
set udg_Illusion_Operation = 0
endfunction
private function Operation12 takes nothing returns nothing
set udg_Illusion_DamageReceiveFactor = GetIllusionReceiveFactor(udg_Illusion_Unit)
set udg_Illusion_Operation = 0
endfunction
private function Operation13 takes nothing returns nothing
set udg_Illusion_BaseUnit = GetIllusionBaseUnit(udg_Illusion_Unit)
set udg_Illusion_Operation = 0
endfunction
private function Operation14 takes nothing returns nothing
set udg_Illusion_Flag = GetIllusionFlag(udg_Illusion_Unit)
set udg_Illusion_Operation = 0
endfunction
private function Operate takes nothing returns nothing
if udg_Illusion_Operation < 8 then
if udg_Illusion_Operation < 4 then
if udg_Illusion_Operation < 2 then
if udg_Illusion_Operation < 1 then
call Operation0()
else
call Operation1()
endif
else
if udg_Illusion_Operation < 3 then
call Operation2()
else
call Operation3()
endif
endif
else
if udg_Illusion_Operation < 6 then
if udg_Illusion_Operation < 5 then
call Operation4()
else
call Operation5()
endif
else
if udg_Illusion_Operation < 7 then
call Operation6()
else
call Operation7()
endif
endif
endif
else
if udg_Illusion_Operation < 12 then
if udg_Illusion_Operation < 10 then
if udg_Illusion_Operation < 9 then
call Operation8()
else
call Operation9()
endif
else
if udg_Illusion_Operation < 11 then
call Operation10()
else
call Operation11()
endif
endif
else
if udg_Illusion_Operation < 14 then
if udg_Illusion_Operation < 13 then
call Operation12()
else
call Operation13()
endif
else
call Operation14()
endif
endif
endif
endfunction
private function IllusionDeathEventGUI takes nothing returns nothing
local unit prevDying = udg_Illusion_TriggerUnit
set udg_Illusion_TriggerUnit = GetTriggerIllusion()
set udg_Illusion_DeathEvent = 0.00
set udg_Illusion_DeathEvent = 1.00
set udg_Illusion_DeathEvent = 0.00
set udg_Illusion_TriggerUnit = prevDying
set prevDying = null
endfunction
private function Init takes nothing returns nothing
set udg_Illusion_OperationTrigger = CreateTrigger()
call TriggerAddAction(udg_Illusion_OperationTrigger, function Operate)
call RegisterIllusionDeathHandler(function IllusionDeathEventGUI)
endfunction
endlibrary
function InitTrig_IllusionEditor_GUI_Support takes nothing returns nothing
/* We use an InitTrig function instead of a scope/library
initializer to initialize the values of our variables to
make sure that InitGlobals() runs first so that the values
of our variables would not be reset. */
static if LIBRARY_ErrorMessage then
debug set udg_Illusion_GetFlag = 0x12321
endif
call ExecuteFunc("Trig_GUI_IllusionEditor_Documentation_Actions")
static if LIBRARY_ErrorMessage then
debug call ThrowError(udg_Illusion_GetFlag == 0x12321, "IllusionEditorGUI", "", "", 0, "The trigger \"GUI IllusionEditor Documentation\" is missing!")
endif
endfunction
//!| [ ILLUSION EDITOR OBJECT MERGER ] |
/*
Paste this script to your map and it save it. Close your map, open it again and disable/delete this script.
Now you have the all the object data needed for the Illusion Editor in your map.
*/
//! external ObjectMerger w3u nzin ictr anam "DummyCaster" ansf "(IllusionEditor)" uabi "Ainv,Aloc" ucbs 0 ushr 0 umdl "dummy.mdl" upat "" unbm 0 usid 1000000000 usin 1000000000
//! external ObjectMerger w3u nzin idum anam "Dummy" ansf "(IllusionEditor)" uabi "Ainv,Aloc,Amrf" ucbs 0 ushr 0 umdl "dummy.mdl" umvs 522 umvr 3 upat "" unbm 0
//! external ObjectMerger w3a Alil Illu anam "illu" atat "" lild 1 1 lilw 1 1 aare 1 0 aran 1 99999 ahdu 1 1000000000.00 adur 1 1000000000.00 atar 1 "air,alive,allies,ancient,bridge,dead,debris,decoration,enemies,friend,ground,hero,invulnerable,item,mechanical,neutral,nonancient,nonhero,nonsapper,notself,organic,player,self,structure,sapper,terrain,tree,vulnerable,wall,ward"
library TerrainGenerator uses MapBounds
struct TerrainGenerator extends array
static integer background = 0
private static integer array layerId
private static real array probability
private static boolean array needsPreviousLayer
private static integer layerCount = 0
private static integer currentLayer = 0
static method addLayer takes integer terrainId, real prob, boolean needsPrevLayer returns nothing
set layerCount = layerCount + 1
set needsPreviousLayer[layerCount] = needsPrevLayer
set probability[layerCount] = prob
set layerId[layerCount] = terrainId
endmethod
private static method initBackground takes nothing returns nothing
if background != 0 then
call SetTerrainType(WorldBounds.centerX, WorldBounds.centerY, background, -1, 40, 1)
endif
endmethod
private static method initLayer takes integer i returns nothing
local boolean needsPrevLayer = needsPreviousLayer[i]
local real y = WorldBounds.minY + 64.00
local real x
loop
exitwhen y >= WorldBounds.maxY
set x = WorldBounds.minX + 64.00
loop
exitwhen x >= WorldBounds.maxX
if GetRandomReal(0.00, 1.00) < probability[i] and/*
*/ ((not needsPrevLayer) or i == 1 or GetTerrainType(x, y) == layerId[i - 1]) then
call SetTerrainType(x, y, layerId[i], -1, 1, 1)
endif
set x = x + 128.00
endloop
set y = y + 128.00
endloop
endmethod
private static method onLayerInit takes nothing returns nothing
call initLayer(currentLayer)
endmethod
static method generate takes nothing returns nothing
call initBackground()
set currentLayer = 0
loop
exitwhen currentLayer == layerCount
set currentLayer = currentLayer + 1
call ForForce(bj_FORCE_PLAYER[0], function thistype.onLayerInit)
endloop
endmethod
endstruct
endlibrary
library UnitCampsGenerator /*
*/uses /*
*/Alloc /*
*/LinkedList /*
*/IPool /*
*/Table /*
*/MapBounds /*
*/TerrainPathability /*
*/GetUnitTypeCollisionSize /*
*/optional ErrorMessage /*
*/
globals
public constant real CAMP_SPACING_MIN = 800.00
endglobals
private keyword UnitList
globals
UnitCamp array ENUMED_UNIT_CAMPS
private IPool array unitIdPool
private real array unitCampInstanceX
private real array unitCampInstanceY
private real array unitCampInstanceRadius
private real array unitCampInstanceFacing
private UnitList array unitCampInstanceList
private player array unitCampInstancePlayer
private TableArray weightTable
private TableArray limitTable
private trigger evaluator = CreateTrigger()
private rect globalRect = Rect(0.00, 0.00, 0.00, 0.00)
private real tempRadius
private UnitCamp tempCamp
private integer tempData
private boolexpr tempFilter
endglobals
static if DEBUG_MODE then
private function AssertError takes boolean condition, string methodName, string structName, integer node, string message returns nothing
static if LIBRARY_ErrorMessage then
call ThrowError(condition, SCOPE_PREFIX, methodName, structName, node, message)
endif
endfunction
endif
private module Init
private static method onInit takes nothing returns nothing
call onScopeInit()
endmethod
endmodule
private function GetRandomMapX takes nothing returns real
return MapBounds.getRandomX()
endfunction
private function GetRandomMapY takes nothing returns real
return MapBounds.getRandomY()
endfunction
function EnumUnitCampsInRange takes real x, real y, real radius returns nothing
local real dx
local real dy
local UnitCampInstance node
local UnitCampInstance next = 0
local integer i = UnitCampsGenerator.unitCampCount
loop
exitwhen i == 0
set node = UnitCampsGenerator.unitCamp[i]
set dx = unitCampInstanceX[node] - x
set dy = unitCampInstanceY[node] - y
if dx*dx + dy*dy <= (radius + unitCampInstanceRadius[node])*(radius + unitCampInstanceRadius[node]) then
set ENUMED_UNIT_CAMPS[next] = node
set next = ENUMED_UNIT_CAMPS[next]
endif
set i = i - 1
endloop
set ENUMED_UNIT_CAMPS[next] = 0
endfunction
private struct Node extends array
implement Alloc
endstruct
private struct UnitList extends array
unit unit
private static method onRemove takes thistype node returns nothing
call RemoveUnit(node.unit)
set node.unit = null
call Node(node).deallocate()
endmethod
implement List
endstruct
private struct UnitPoolList extends array
boolean removed
readonly integer unitId
private static key tk
implement StaticList
static method contains takes integer unitId returns boolean
return Table(tk).has(unitId)
endmethod
static method getNode takes integer unitId returns thistype
return Table(tk)[unitId]
endmethod
static method emplace takes integer unitId returns nothing
local thistype node = Node.allocate()
call pushBack(node)
set node.unitId = unitId
set Table(tk)[unitId] = node
endmethod
static method delete takes integer unitId returns nothing
call Table(tk).remove(unitId)
call remove(Table(tk)[unitId])
endmethod
endstruct
struct UnitCamp extends array
player player
integer sizeMin
integer sizeMax
real radius
real facingDeviationMax
real unitSpacingMin
static method operator [] takes thistype node returns thistype
local thistype this = Node.allocate()
set this.player = node.player
set this.sizeMin = node.sizeMin
set this.sizeMax = node.sizeMax
set this.radius = node.radius
set this.facingDeviationMax = node.facingDeviationMax
set this.unitSpacingMin = node.unitSpacingMin
set unitIdPool[this] = unitIdPool[node].copy()
return this
endmethod
method registerToUnitPool takes integer unitId, integer weight, integer limit returns thistype
debug call AssertError(weight < 1, "registerToUnitPool()", "thistype", this, "Invalid Pool Item Weight (" + I2S(weight) + ")")
if not weightTable[this].has(unitId) then
call unitIdPool[this].add(unitId, weight)
set weightTable[this][unitId] = weight
set limitTable[this][unitId] = limit
set limitTable[this][-unitId] = limit
endif
return this
endmethod
method unregisterFromUnitPool takes integer unitId returns thistype
if weightTable[this].has(unitId) then
call unitIdPool[this].remove(unitId)
call weightTable[this].remove(unitId)
call weightTable[this].remove(-unitId)
call UnitPoolList.delete(unitId)
endif
return this
endmethod
method clearPool takes nothing returns thistype
call unitIdPool[this].flush()
call weightTable[this].flush()
call limitTable[this].flush()
return this
endmethod
static method create takes nothing returns thistype
local thistype node = Node.allocate()
set unitIdPool[node] = IPool.create()
set node.player = Player(PLAYER_NEUTRAL_AGGRESSIVE)
return node
endmethod
method destroy takes nothing returns nothing
call Node(this.clearPool()).deallocate()
call unitIdPool[this].destroy()
endmethod
endstruct
struct UnitCampInstance extends array
method operator x takes nothing returns real
return unitCampInstanceX[this]
endmethod
method operator y takes nothing returns real
return unitCampInstanceY[this]
endmethod
method operator radius takes nothing returns real
return unitCampInstanceRadius[this]
endmethod
method operator facing takes nothing returns real
return unitCampInstanceFacing[this]
endmethod
method operator player takes nothing returns player
return unitCampInstancePlayer[this]
endmethod
method destroy takes nothing returns nothing
call UnitList(this).flush()
endmethod
endstruct
private struct CampPoolList extends array
boolean removed
implement StaticList
endstruct
struct UnitCampsGenerator extends array
readonly static UnitCampInstance array unitCamp
readonly static integer unitCampCount = 0
readonly static real filterX = 0.00
readonly static real filterY = 0.00
readonly static UnitCamp filterCamp = 0
private static IPool unitCampPool = 0
private static unit array mapCampUnit
private static integer mapCampUnitCount = 0
private static method getPool takes nothing returns IPool
if unitCampPool == 0 then
set unitCampPool = IPool.create()
endif
return unitCampPool
endmethod
static method registerToUnitCampPool takes UnitCamp camp, integer weight, integer limit returns thistype
debug call AssertError(weight < 1, "registerToUnitCampPool()", "thistype", 0, "Invalid Pool Item Weight (" + I2S(weight) + ")")
if not weightTable[0].has(camp) then
call getPool().add(camp, weight)
set weightTable[0][camp] = weight
set limitTable[0][camp] = limit
set limitTable[0][-camp] = limit
endif
return 0
endmethod
static method unregisterFromUnitCampPool takes UnitCamp camp returns thistype
if weightTable[0].has(camp) then
call getPool().remove(camp)
call weightTable[0].remove(camp)
call limitTable[0].remove(camp)
call limitTable[0].remove(-camp)
endif
return 0
endmethod
static method clearUnitCampPool takes nothing returns thistype
call getPool().flush()
call weightTable[0].flush()
call limitTable[0].flush()
return 0
endmethod
private static method clearDestInRadius takes nothing returns boolean
local destructable d = GetFilterDestructable()
local real dx = GetWidgetX(d) - GetRectCenterX(globalRect)
local real dy = GetWidgetY(d) - GetRectCenterY(globalRect)
if dx*dx + dy*dy < tempRadius*tempRadius then
call RemoveDestructable(d)
endif
set d = null
return false
endmethod
private static method generateCamp takes UnitCamp camp, real x, real y, real facing returns UnitCampInstance
local integer i = GetRandomInt(camp.sizeMin, camp.sizeMax)
local integer limit
local integer unitId
local real collisionSize
local real r
local real a
local UnitCampInstance node
local UnitList temp
local UnitList unitNode
local UnitPoolList poolNode
local boolean exit
if i == 0 then
return 0
endif
set node = Node.allocate()
call UnitList.insert(node, node) // Make a circular list
set unitCampInstanceX[node] = x
set unitCampInstanceY[node] = y
set unitCampInstanceRadius[node] = camp.radius
set unitCampInstanceFacing[node] = facing
set tempRadius = camp.radius
call SetRect(globalRect, x - camp.radius, y - camp.radius, x + camp.radius, y + camp.radius)
call EnumDestructablesInRect(globalRect, Filter(function thistype.clearDestInRadius), null)
loop
exitwhen i == 0
set mapCampUnitCount = mapCampUnitCount + 1
set unitId = unitIdPool[camp].getItem()
if not UnitPoolList.contains(unitId) then
call UnitPoolList.emplace(unitId)
endif
if limitTable[camp][unitId] > 0 then
set limit = limitTable[camp][-unitId] - 1
set limitTable[camp][-unitId] = limit
if limit == 0 then
call unitIdPool[camp].remove(unitId)
set UnitPoolList.getNode(unitId).removed = true
endif
endif
set collisionSize = GetUnitTypeCollisionSize(unitId)
loop
set a = GetRandomReal(0.00, 2.00*bj_PI)
set r = GetRandomReal(0.00, camp.radius)
call IsTerrainWalkable(x + r*Cos(a), y + r*Sin(a))
set exit = true
set temp = UnitList(node).next
loop
exitwhen temp == node
if IsUnitInRangeXY(temp.unit, TerrainPathability_X, TerrainPathability_Y, camp.unitSpacingMin + collisionSize) then
set exit = false
exitwhen true
endif
set temp = temp.next
endloop
exitwhen exit
endloop
set mapCampUnit[mapCampUnitCount] = CreateUnit(camp.player, unitId, TerrainPathability_X, TerrainPathability_Y, GetRandomReal(facing - camp.facingDeviationMax, facing + camp.facingDeviationMax)*bj_RADTODEG)
set unitNode = Node.allocate()
set unitNode.unit = mapCampUnit[mapCampUnitCount]
call UnitList(node).pushBack(unitNode)
set i = i - 1
endloop
set poolNode = UnitPoolList.head.next
loop
exitwhen poolNode == UnitPoolList.head
if poolNode.removed then
set poolNode.removed = false
call unitIdPool[camp].add(poolNode.unitId, weightTable[camp][poolNode.unitId])
endif
set limitTable[camp][-poolNode.unitId] = limitTable[camp][poolNode.unitId]
call UnitPoolList.remove(poolNode)
set poolNode = poolNode.next
endloop
return node
endmethod
private static method initCampGeneration takes nothing returns nothing
set tempData = generateCamp(tempCamp, TerrainPathability_X, TerrainPathability_Y, GetRandomReal(0.00, 2.00*bj_PI))
endmethod
private static method initIterativeGeneration takes nothing returns nothing
local IPool pool = getPool()
local UnitCamp camp = pool.getItem()
local UnitCampInstance node
local integer limit
local real x
local real y
local real r = camp.radius
if not CampPoolList.isLinked(camp) then
call CampPoolList.pushBack(camp)
endif
if limitTable[0][camp] > 0 then
set limit = limitTable[0][-camp] - 1
set limitTable[0][-camp] = limit
if limit == 0 then
call pool.remove(camp)
set CampPoolList(camp).removed = true
endif
endif
loop
set x = MapBounds.getBoundedXWithMargin(GetRandomMapX(), r)
set y = MapBounds.getBoundedYWithMargin(GetRandomMapY(), r)
call IsTerrainWalkable(x, y)
call EnumUnitCampsInRange(TerrainPathability_X, TerrainPathability_Y, r + CAMP_SPACING_MIN)
if ENUMED_UNIT_CAMPS[0] == 0 then
if tempFilter == null then
exitwhen true
else
set filterX = TerrainPathability_X
set filterY = TerrainPathability_Y
set filterCamp = camp
exitwhen TriggerEvaluate(evaluator)
endif
endif
endloop
set filterX = 0.00
set filterY = 0.00
set filterCamp = 0
set tempCamp = camp
call ForForce(bj_FORCE_PLAYER[0], function thistype.initCampGeneration)
set node = tempData
if node > 0 then
set unitCampCount = unitCampCount + 1
set unitCamp[unitCampCount] = node
endif
endmethod
static method generate takes integer count, boolexpr filter returns thistype
local IPool pool = getPool()
local CampPoolList poolNode
set tempFilter = filter
call TriggerClearConditions(evaluator)
call TriggerAddCondition(evaluator, filter)
loop
exitwhen count == 0
call ForForce(bj_FORCE_PLAYER[0], function thistype.initIterativeGeneration)
set count = count - 1
endloop
set poolNode = CampPoolList.head.next
loop
exitwhen poolNode == CampPoolList.head
if poolNode.removed then
set poolNode.removed = false
call pool.add(poolNode, weightTable[0][poolNode])
endif
set limitTable[0][-poolNode] = limitTable[0][poolNode]
call CampPoolList.remove(poolNode)
set poolNode = poolNode.next
endloop
return 0
endmethod
endstruct
private struct Initializer extends array
private static method onScopeInit takes nothing returns nothing
set weightTable = TableArray[JASS_MAX_ARRAY_SIZE]
set limitTable = TableArray[JASS_MAX_ARRAY_SIZE]
endmethod
implement Init
endstruct
endlibrary
library DestructablesGenerator /*
*/uses /*
*/MapBounds /*
*/UnitCampsGenerator /*
*/
struct DestructablesGenerator extends array
readonly static integer array clusterDestId
readonly static integer array clusterSizeMin
readonly static integer array clusterSizeMax
readonly static real array clusterRadiusBase
readonly static real array clusterRadiusPerSize
readonly static real array clusterClearance
readonly static integer array clusterCountMin
readonly static integer array clusterCountMax
readonly static boolexpr array clusterPlacementFilter
readonly static real filterX
readonly static real filterY
readonly static real filterRadius
readonly static integer filterSize
readonly static integer filterLayer
readonly static integer currentLayer = 0
private static integer layerCount = 0
private static group enumGroup = CreateGroup()
private static trigger evaluator = CreateTrigger()
static method addLayer takes integer destructableId, integer sizeMin, integer sizeMax, real radiusBase, real radiusPerSize, real clearance, integer countMin, integer countMax, boolexpr filter returns nothing
set layerCount = layerCount + 1
set clusterDestId[layerCount] = destructableId
set clusterSizeMin[layerCount] = sizeMin
set clusterSizeMax[layerCount] = sizeMax
set clusterRadiusBase[layerCount] = radiusBase
set clusterRadiusPerSize[layerCount] = radiusPerSize
set clusterClearance[layerCount] = clearance
set clusterCountMin[layerCount] = countMin
set clusterCountMax[layerCount] = countMax
set clusterPlacementFilter[layerCount] = filter
endmethod
private static method checkUnitProximity takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), filterX, filterY, filterRadius)
endmethod
private static method initLayer takes nothing returns nothing
local real r
local real a
local real x
local real y
local integer i = GetRandomInt(clusterSizeMin[currentLayer], clusterSizeMax[currentLayer])
local real radius = clusterRadiusBase[currentLayer] + i*clusterRadiusPerSize[currentLayer]
call TriggerClearConditions(evaluator)
call TriggerAddCondition(evaluator, clusterPlacementFilter[currentLayer])
loop
set x = WorldBounds.getBoundedXWithMargin(WorldBounds.getRandomX(), radius)
set y = WorldBounds.getBoundedYWithMargin(WorldBounds.getRandomY(), radius)
call EnumUnitCampsInRange(x, y, radius + clusterClearance[currentLayer])
if ENUMED_UNIT_CAMPS[0] == 0 then
set filterX = x
set filterY = y
set filterRadius = radius + clusterClearance[currentLayer]
call GroupEnumUnitsInRange(enumGroup, x, y, filterRadius + 200.00, Filter(function thistype.checkUnitProximity))
if FirstOfGroup(enumGroup) == null then
if clusterPlacementFilter[currentLayer] == null then
exitwhen true
else
set filterSize = i
set filterLayer = currentLayer
exitwhen TriggerEvaluate(evaluator)
endif
endif
endif
endloop
set filterX = 0.00
set filterY = 0.00
set filterRadius = 0.00
set filterSize = 0
set filterLayer = 0
loop
exitwhen i == 0
set a = GetRandomReal(0.00, 2.00*bj_PI)
set r = GetRandomReal(0.00, radius)
call CreateDestructable(clusterDestId[currentLayer], x + r*Cos(a), y + r*Sin(a), GetRandomReal(0, 360), 1.00, GetRandomInt(0, 3))
set i = i - 1
endloop
endmethod
private static method onLayerInit takes nothing returns nothing
local integer clusterCount = GetRandomInt(clusterCountMin[currentLayer], clusterCountMax[currentLayer])
loop
exitwhen clusterCount == 0
call ForForce(bj_FORCE_PLAYER[0], function thistype.initLayer)
set clusterCount = clusterCount - 1
endloop
endmethod
static method generate takes nothing returns nothing
set currentLayer = 0
loop
exitwhen currentLayer == layerCount
set currentLayer = currentLayer + 1
call onLayerInit()
endloop
endmethod
endstruct
endlibrary
library MapSettingGenerator /*
*/uses /*
*/TerrainGenerator /*
*/DestructablesGenerator /*
*/UnitCampsGenerator /*
Inspiration for creating my own terrain generator:
https://www.hiveworkshop.com/threads/terrain-generation.154168/
*/
struct MapSettingGenerator extends array
static method operator Terrain takes nothing returns TerrainGenerator
return 0
endmethod
static method operator Destructables takes nothing returns DestructablesGenerator
return 0
endmethod
static method operator UnitCamps takes nothing returns UnitCampsGenerator
return 0
endmethod
endstruct
endlibrary
scope MapInit initializer OnInit
globals
private constant integer UNIT_CAMP_COUNT_MIN = 12
private constant integer UNIT_CAMP_COUNT_MAX = 20
private constant real CENTER_STAGE_RADIUS = 1000.00
private string a = "A"
private string b = "B"
private string c = "C"
endglobals
private function InitTerrain takes nothing returns nothing
set MapSettingGenerator.Terrain.background = 'Adrd'
call MapSettingGenerator.Terrain.addLayer('Adrg', 0.6, true)
call MapSettingGenerator.Terrain.addLayer('Alvd', 0.3, true)
call MapSettingGenerator.Terrain.addLayer('Avin', 0.15, true)
/*
* Generate Terrain
*/
call MapSettingGenerator.Terrain.generate()
set a = null
endfunction
private function DestructablePlacementFilter takes nothing returns boolean
local real dx = MapSettingGenerator.Destructables.filterX - WorldBounds.centerX
local real dy = MapSettingGenerator.Destructables.filterY - WorldBounds.centerY
local real r = CENTER_STAGE_RADIUS + MapSettingGenerator.Destructables.filterRadius
return dx*dx + dy*dy > r*r
endfunction
private function InitDestructables takes nothing returns nothing
call MapSettingGenerator.Destructables.addLayer('FTtw', 2, 12, 0.00, 50.00, 35.00, 180, 240, Filter(function DestructablePlacementFilter))
/*
* Generate Destructables
*/
call MapSettingGenerator.Destructables.generate()
set b = null
endfunction
private function UnitCampsLocationFilter takes nothing returns boolean
local real dx = MapSettingGenerator.UnitCamps.filterX - MapBounds.centerX
local real dy = MapSettingGenerator.UnitCamps.filterY - MapBounds.centerY
local real r = CENTER_STAGE_RADIUS + MapSettingGenerator.UnitCamps.filterCamp.radius
return dx*dx + dy*dy > r*r
endfunction
private function InitUnitCamps takes nothing returns nothing
/*
* Human camps
*/
local UnitCamp camp = UnitCamp.create()
set camp.sizeMin = 4
set camp.sizeMax = 8
set camp.radius = 450.00
set camp.facingDeviationMax = bj_PI*0.125
set camp.unitSpacingMin = 50.00
call camp/*
*/.registerToUnitPool('hpea', 1, 0)/*
*/.registerToUnitPool('hfoo', 1, 0)/*
*/.registerToUnitPool('hrif', 1, 0)/*
*/.registerToUnitPool('hmpr', 1, 0)/*
*/.registerToUnitPool('hhou', 1, 2)/*
*/.registerToUnitPool('hwtw', 1, 1)
call MapSettingGenerator.UnitCamps.registerToUnitCampPool(camp, 5, 0)
/*
* Orc camps
*/
set camp = UnitCamp.create()
set camp.sizeMin = 3
set camp.sizeMax = 6
set camp.radius = 350.00
set camp.facingDeviationMax = bj_PI*0.125
set camp.unitSpacingMin = 65.00
call camp/*
*/.registerToUnitPool('oshm', 1, 0)/*
*/.registerToUnitPool('ogru', 1, 0)/*
*/.registerToUnitPool('ocat', 1, 2)/*
*/.registerToUnitPool('Obla', 1, 1)/*
*/.registerToUnitPool('otto', 1, 1)/*
*/.registerToUnitPool('ogre', 1, 1)
call MapSettingGenerator.UnitCamps.registerToUnitCampPool(camp, 3, 0)
/*
* Troll camps
*/
set camp = UnitCamp.create()
set camp.sizeMin = 3
set camp.sizeMax = 6
set camp.radius = 350.00
set camp.facingDeviationMax = bj_PI*0.125
set camp.unitSpacingMin = 50.00
call camp/*
*/.registerToUnitPool('ndtr', 1, 0)/*
*/.registerToUnitPool('ndtp', 1, 0)/*
*/.registerToUnitPool('ndtt', 1, 0)/*
*/.registerToUnitPool('ndtb', 1, 0)/*
*/.registerToUnitPool('ndth', 1, 0)/*
*/.registerToUnitPool('ndtw', 1, 0)
call MapSettingGenerator.UnitCamps.registerToUnitCampPool(camp, 2, 0)
/*
* Critters
*/
set camp = UnitCamp.create()
set camp.sizeMin = 1
set camp.sizeMax = 1
set camp.radius = 0.00
set camp.facingDeviationMax = 0.00
call camp/*
*/.registerToUnitPool('nalb', 1, 0)/*
*/.registerToUnitPool('nech', 1, 0)/*
*/.registerToUnitPool('nfro', 1, 0)/*
*/.registerToUnitPool('nsea', 1, 0)
call MapSettingGenerator.UnitCamps.registerToUnitCampPool(camp, 1, 4)
/*
* Neutral buildings
*/
set camp = UnitCamp.create()
set camp.sizeMin = 1
set camp.sizeMax = 1
set camp.radius = 125.00
set camp.facingDeviationMax = 0.00
call camp.registerToUnitPool('ntav', 1, 0)
call MapSettingGenerator.UnitCamps.registerToUnitCampPool(camp, 1, 2)
/*
* Generate Unit Camps
*/
call MapSettingGenerator.UnitCamps.generate(GetRandomInt(UNIT_CAMP_COUNT_MIN, UNIT_CAMP_COUNT_MAX), Filter(function UnitCampsLocationFilter))
set c = null
endfunction
private function OnTimerExpire takes nothing returns nothing
if a == "A" then
call BJDebugMsg("|cffff0000[ERROR]:|r Terrain generator thread crashed!")
endif
if b == "B" then
call BJDebugMsg("|cffff0000[ERROR]:|r Destructables generator thread crashed!")
endif
if c == "C" then
call BJDebugMsg("|cffff0000[ERROR]:|r Unit camps generator thread crashed!")
endif
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "|cffffcc00If you don't like the randomness of the generated map, simply restart the game.|r\n")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "Press [ESC] to cast a Fireball spell without actually using the ability.")
call DestroyTimer(GetExpiredTimer())
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(), 0, false, function OnTimerExpire)
set udg_DemoMap_Hero = CreateUnit(Player(0), 'Hero', 0.00, 0.00, 90.00)
call SelectUnit(udg_DemoMap_Hero, true)
call SetHeroLevel(udg_DemoMap_Hero, 10, false)
call FogEnable(false)
call FogMaskEnable(false)
call PanCameraToTimed(GetUnitX(udg_DemoMap_Hero), GetUnitY(udg_DemoMap_Hero), 0.00)
call ForForce(bj_FORCE_PLAYER[0], function InitTerrain)
call ForForce(bj_FORCE_PLAYER[0], function InitUnitCamps)
call ForForce(bj_FORCE_PLAYER[0], function InitDestructables)
endfunction
endscope