- Joined
- Mar 29, 2016
- Messages
- 688
ㅤ
Last edited:
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
/*
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.
*/
|=========|
| 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
*/
|=====|
| 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)
*/
/*
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/*
*/
/*
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/*
*/
|-------------------|
| 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)
*/
/*
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.
*/
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
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
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
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
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
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
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
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
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
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