1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Seek unity between the elements in the 22nd Terraining Contest Poll.
    Dismiss Notice
  3. Seize the moment! The 18th Mini Mapping Contest has commenced.
    Dismiss Notice
  4. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  5. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Spell Framework [vJASS + GUI] v2.1.0

Submitted by AGD
This bundle is marked as approved. It works and satisfies the submission rules.
Spell Framework
For fast & convenient custom spell development



Aside from easing spell creation, this framework also allows you to create spells mechanics and
spell modifications beyond what is previously doable (without messy workarounds). Examples of
these include multicast systems, single-target spell blocks, spell pool, random spell activation,
and much more!

The complete documentation can be found inside the map. Be sure to spare a time for reading.


How to Import
1. Copy the 'Spell Development Framework' trigger category into your map
2. Copy the 'Spell Development Template' trigger category into your map
so you can use the premade templates (OPTIONAL)

... and that's it!


Supported Patch
Any



Preview
[​IMG]
The preview above shows two illusions seemingly casting an ability



  • Code (vJASS):
    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
  • SpellEvent
    Code (vJASS):
    /*
        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


    */

    SpellCloner
    Code (vJASS):
    |=====|
    | 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)


    */

    SpellEventGUI
    • SpellEventGUI Documentation
      • Events
        • Map initialization
      • Conditions
      • Actions
        • -------- /*----------------------------------------------------------------------------------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- Spell Event GUI Documentation --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- All system functionalities are stated in this trigger --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- --------
        • -------- Features: --------
        • -------- - Easy to use event response variables --------
        • -------- - Spell-related events --------
        • -------- - Manual spell event triggering --------
        • -------- - Spell event response values overriding --------
        • -------- - Structured spell template --------
        • -------- */ --------
        • -------- --------
        • -------- System Configuration --------
        • Set SPELL__FRAME_RATE = 32.00
        • Set SPELL__DUMMY_PLAYER = Neutral Passive
        • -------- --------
        • Custom script: /*
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- --------
        • -------- Events: --------
        • -------- (Runs only for all spells registered to the system) --------
        • -------- --------
        • -------- To catch a spell cast begin event, use --------
        • -------- - Game - SPELL__Event becomes Equal to 1.00 --------
        • -------- --------
        • -------- To catch a spell channel begin event, use --------
        • -------- - Game - SPELL__Event becomes Equal to 2.00 --------
        • -------- --------
        • -------- To catch a spell effect start event, use --------
        • -------- - Game - SPELL__Event becomes Equal to 3.00 --------
        • -------- --------
        • -------- To catch a spell cast stop event, use --------
        • -------- - Game - SPELL__Event becomes Equal to 4.00 --------
        • -------- --------
        • -------- To catch a spell cast finish event, use --------
        • -------- - Game - SPELL__Event becomes Equal to 5.00 --------
        • -------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- --------
        • -------- Event Responses: --------
        • -------- These variables can be used in response to generic spell events as well as --------
        • -------- in your spell triggers (OnSpellStart, OnSpellPeriodic, and OnSpellEnd) --------
        • -------- - SPELL__Ability -> Ability being cast --------
        • -------- - SPELL__EventType -> Spell event triggering this trigger --------
        • -------- - SPELL__OrderType -> Order type of the ability being cast --------
        • -------- - SPELL__Level -> Level of the ability being cast (as integer) --------
        • -------- - SPELL__RealLevel -> Level of the ability being cast (as real) --------
        • -------- - SPELL__TriggerPlayer -> Triggering player --------
        • -------- - SPELL__TriggerUnit -> Triggering unit (caster) --------
        • -------- - SPELL__TargetPoint -> Target point of the ability being cast --------
        • -------- - SPELL__TargetUnit --------
        • -------- - SPELL__TargetItem --------
        • -------- - SPELL__TargetDest --------
        • -------- - SPELL__Node -> Unique integer to be used as index for your spell variables. Always 0 inside generic spell events --------
        • -------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- --------
        • -------- Constants: --------
        • -------- --------
        • -------- Event type constants - use in conjunction with SPELL__EventType --------
        • -------- - SPELL__EVENT_CAST --------
        • -------- - SPELL__EVENT_CHANNEL --------
        • -------- - SPELL__EVENT_EFFECT --------
        • -------- - SPELL__EVENT_ENDCAST --------
        • -------- - SPELL__EVENT_FINISH --------
        • -------- --------
        • -------- Order type constants - use in conjunction with SPELL__OrderType --------
        • -------- - SPELL__ORDER_NO_TARGET --------
        • -------- - SPELL__ORDER_POINT_TARGET --------
        • -------- - SPELL__ORDER_SINGLE_TARGET --------
        • -------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- --------
        • -------- Spell Registration: --------
        • -------- --------
        • -------- To register a spell, setup the neccesary data --------
        • -------- - SPELL__Ability -> Set to the activation ability --------
        • -------- - SPELL__EventType -> Set to the type of spell event --------
        • -------- - SPELL__OnSpellStart -> Set to the trigger that will run upon the spell event --------
        • -------- - SPELL__OnSpellPeriod -> Set to the spell's periodic trigger --------
        • -------- - SPELL__OnSpellEnd -> Set to the trigger that will run when the periodic trigger ends --------
        • -------- --------
        • -------- Then do --------
        • Trigger - Run SPELL__RegisterHandler (ignoring conditions)
        • -------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- --------
        • -------- Manual Event Firing: --------
        • -------- --------
        • -------- To manually fire a no-target spell event, setup the neccessary data --------
        • -------- - SPELL__Ability -> Set to the triggering ability --------
        • -------- - SPELL__EventType -> Set to the event type to be fired --------
        • -------- - SPELL__Level --------
        • -------- - SPELL__TriggerUnit --------
        • -------- --------
        • -------- To manually fire a point-target spell event, setup the neccessary data --------
        • -------- - SPELL__Ability -> Set to the triggering ability --------
        • -------- - SPELL__EventType -> Set to the event type to be fired --------
        • -------- - SPELL__Level --------
        • -------- - SPELL__TriggerUnit --------
        • -------- - SPELL__TargetPoint --------
        • -------- --------
        • -------- To manually fire a single-target spell event, setup the neccessary data --------
        • -------- - SPELL__Ability -> Set to the triggering ability --------
        • -------- - SPELL__EventType -> Set to the event type to be fired --------
        • -------- - SPELL__Level --------
        • -------- - SPELL__TriggerUnit --------
        • -------- - SPELL__TargetUnit or SPELL__TargetItem or SPELL__TargetDest --------
        • -------- --------
        • -------- Then do --------
        • Trigger - Run SPELL__InvokeEvent (ignoring conditions)
        • -------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- --------
        • -------- Event Parameters Overriding: --------
        • -------- --------
        • -------- You can only override event parameters in response to a generic spell event --------
        • -------- mentioned in the Events section above. --------
        • -------- There are three options in overriding event parameters. You can override --------
        • -------- the parameters of a No-Target, Point-Target, or a Single-Target event. --------
        • -------- --------
        • -------- (No-Target) --------
        • -------- --------
        • Set SPELL__OrderType = SPELL__ORDER_NO_TARGET
        • Set SPELL__Level = (SPELL__Level + 1)
        • Set SPELL__TriggerUnit = SPELL__TriggerUnit
        • Trigger - Run SPELL__OverrideParams (ignoring conditions)
        • -------- --------
        • -------- (Point-Target) --------
        • -------- --------
        • Set TempLoc = SPELL__TargetPoint
        • -------- --------
        • Set SPELL__OrderType = SPELL__ORDER_POINT_TARGET
        • Set SPELL__Level = (SPELL__Level + 1)
        • Set SPELL__TriggerUnit = SPELL__TriggerUnit
        • Set SPELL__TargetPoint = (SPELL__TargetPoint offset by 200.00 towards (Facing of SPELL__TriggerUnit) degrees)
        • Trigger - Run SPELL__OverrideParams (ignoring conditions)
        • -------- --------
        • -------- call RemoveLocation(udg_SPELL__TargetPoint) --------
        • Set SPELL__TargetPoint = TempLoc
        • -------- --------
        • -------- (Single-Target) --------
        • -------- --------
        • Set TempUnit = SPELL__TriggerUnit
        • -------- --------
        • Set SPELL__OrderType = SPELL__ORDER_NO_TARGET
        • Set SPELL__Level = (SPELL__Level + 1)
        • Set SPELL__TriggerUnit = SPELL__TargetUnit
        • Set SPELL__TargetUnit = TempUnit
        • -------- You could also use SPELL__TargetItem or SPELL__TargetDest ^ --------
        • Trigger - Run SPELL__OverrideParams (ignoring conditions)
        • -------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- End of Spell Event GUI Documentation --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- Below Are Just Variable Creators (Do Not Touch) --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- -------------------------------------------------------------------------------------- --------
        • -------- --------
        • Set SPELL__EVENT_CAST = 0
        • Set SPELL__EVENT_CHANNEL = 0
        • Set SPELL__EVENT_EFFECT = 0
        • Set SPELL__EVENT_ENDCAST = 0
        • Set SPELL__EVENT_FINISH = 0
        • Set SPELL__ORDER_NO_TARGET = 0
        • Set SPELL__ORDER_POINT_TARGET = 0
        • Set SPELL__ORDER_SINGLE_TARGET = 0
        • -------- --------
        • Set SPELL__Ability = SPELL__Ability
        • Set SPELL__EnumCount = 0
        • Set SPELL__EnumRange = 0.00
        • Set SPELL__EnumedTargets[0] = No unit
        • Set SPELL__EnumerateTargetsInRange = SPELL__EnumerateTargetsInRange
        • Set SPELL__Event = 0.00
        • Set SPELL__EventType = 0
        • Set SPELL__ExitPeriodic = False
        • Set SPELL__InitializationEvent = 0.00
        • Set SPELL__InvokeEvent = SPELL__InvokeEvent
        • Set SPELL__Level = 0
        • Set SPELL__Index = 0
        • Set SPELL__OnEndTrigger = SPELL__OnEndTrigger
        • Set SPELL__OnPeriodTrigger = SPELL__OnPeriodTrigger
        • Set SPELL__OnStartTrigger = SPELL__OnStartTrigger
        • Set SPELL__OverrideParams = SPELL__OverrideParams
        • Set SPELL__OrderType = 0
        • Set SPELL__PERIOD = 0.00
        • Set SPELL__RealLevel = 0.00
        • Set SPELL__RegisterHandler = SPELL__RegisterHandler
        • Set SPELL__TargetDest = No destructible
        • Set SPELL__TargetItem = No item
        • Set SPELL__TargetUnit = No unit
        • Set SPELL__TargetPoint = (Center of (Playable map area))
        • Set SPELL__TriggerPlayer = Player 1 (Red)
        • Set SPELL__TriggerUnit = No unit
        • Set SPELL__AllyFilterFlag = False
        • Set SPELL__DeadFilterFlag = False
        • Set SPELL__EnemyFilterFlag = False
        • Set SPELL__FlyingFilterFlag = False
        • Set SPELL__HeroFilterFlag = False
        • Set SPELL__LivingFilterFlag = False
        • Set SPELL__MagicImmuneFilterFlag = False
        • Set SPELL__MechanicalFilterFlag = False
        • Set SPELL__NonHeroFilterFlag = False
        • Set SPELL__StructureFilterFlag = False
        • Custom script: */
        • Custom script: call SpellEventGUI_Initialize()


    Alloc
    Code (vJASS):
    /*
        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/*

    */

    LinkedList
    API List
    Code (vJASS):
    /*
        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 Description
    Code (vJASS):
    |-------------------|
    | 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)


    */
  • Code (vJASS):
    /*
        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.

    */

    Spell Template A
    Code (vJASS):
    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

    Spell Template B
    Code (vJASS):
    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

    Spell Template C
    Code (vJASS):
    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

    Spell Template D
    Code (vJASS):
    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

    Spell Template E
    Code (vJASS):
    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

    Spell Template F
    Code (vJASS):
    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

    Cloneable Spell Template A
    Code (vJASS):
    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

    Cloneable Spell Template B
    Code (vJASS):
    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

    Cloneable Spell Template C
    Code (vJASS):
    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

    Cloneable Spell Configuration Template
    Code (vJASS):
    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
  • v2.1.0
    - Spell Template C changed from being specifically a channeling spell template into a flexible one that automatically detects whether the spell is channeling or non-channeling based on the value of <SPELL_EVENT_TYPE> in the configuration
    - Some fixes for Spell Template E, Spell Template F, & Cloneable Spell Template B related to their data structures
    - Fixed an instance leak in sample spell 'Teeth' along with minor improvements
    - Minor improvements for sample spell 'Reflection'
    - Added tooltips for the sample spells
    - Updated the libraries used
    - Other minor changes

    v2.0.0
    - Renamed the resource from 'Spell Development Framework' into 'Spell Framework'
    - Major update to SpellEvent resulting to backwards incompatibility in API
    - Significant updates to SpellCloner library
    - Significant updates to LinkedList library
    - Updated all the spell templates to match with the new Framework API
    - Other important updates

    v1.0.1
    - Updated documentations across the libraries
    - Updated the utility lib 'LinkedList'

    v1.0.0
    - Initial release




Spell Resources Using this Framework

Archimonde Spellpack
Jaina Spellpack
Kael'thas Spellpack
Torrent Array



CREDITS
JAKEZINC
(Beta tester, bug reports, & suggestions)

Bribe
(GUI SpellSystem, some features of which I adopted into SpellEventGUI)

Devalut
(Letting me modify his Teeth - Ver. Gangrenous Gillyweed to be used as a sample spell)

Contents

Spell Framework v2.1.0 (Map)

Reviews
MyPad
The sample spells used work seamlessly with the system, and the system appears to be robust and relatively error-free. Granted, the sample spells do cost a lot of memory when spammed (dummy unit creation), so a word of caution for those who would like...
  1. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    578
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
     
    Last edited: Sep 1, 2020
  2. Devalut

    Devalut

    Joined:
    Feb 9, 2009
    Messages:
    1,461
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    Cool!
     
  3. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    578
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    Updated thread description..
     
  4. hemmedo

    hemmedo

    Joined:
    Jun 13, 2008
    Messages:
    333
    Resources:
    0
    Resources:
    0
    Amazing work mate, good job.
     
  5. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    578
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    Thanks :)
     
  6. JAKEZINC

    JAKEZINC

    Joined:
    Aug 13, 2013
    Messages:
    1,615
    Resources:
    9
    Spells:
    9
    Resources:
    9
    Yeah, I'm advocating the use of this. Now it comes even with a GUI version.
    It should be highly recommended from those who'll incorporate their maps with a bunch of custom spells/abilities.

    The more spell/ability code registered to the SpellFramework the more it becomes powerful.
    Yeah this is obviously true, the system debunks most of the undoable things for the spell-makers in the past.
    (I just hoped you made it recognized earlier: where spell-makers aren't that gone, plus a lively spell section)

    Although, it shouldn't be a problem anymore as you have my back for that. ;)

    The only thing I'm waiting for is the moderator on this section to criticize this as It deserves to be proven very useful.
     
  7. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,545
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    Currently wrapping my head around this system. Merciful God, there are a lot of abstractions in this system.

    In the test map, if you create too many fireballs, the game crashes. I have yet to investigate the reason behind this, so I can only point this out.

    EDIT:
    Further testing revealed that the creation of fireballs wasn't a major cause of the game crash. The likelihood of the game crashing increased the more interactions the fireballs had with the environment (albeit destructive).
     
    Last edited: Aug 6, 2020
  8. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    578
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    Thanks for the feedback. I hope I can get back to this resource soon.
     
  9. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,545
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    The sample spells used work seamlessly with the system, and the system appears to be robust and relatively error-free. Granted, the sample spells do cost a lot of memory when spammed (dummy unit creation), so a word of caution for those who would like to import these sample spells-as-is.

    This system has a lot of dependencies, which makes it a bit more complex to import than most systems, but offers in exchange feature-rich, easy spellmaking templates that I would encourage a lot of vJASSers/GUIers to use in their own projects.

    The Tooth spell appears to leak a bit, with the index slowly growing bigger. (Debug message is printed onSpellStart, reading instance this).
    upload_2020-8-25_19-59-17.png

    Rating: 4.7/5.0
    Status: Approved
     
  10. Dat-C3

    Dat-C3

    Joined:
    Mar 15, 2012
    Messages:
    2,555
    Resources:
    10
    Models:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    1
    Resources:
    10
    Why not spawn 1,000 dummies at map init to solve the lag caused by unit making? If one isn't using them for unit capabilities then could use corpse units as well though make sure they'd be set to not raiseable and also not based on a tauren unit.
     
  11. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    578
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    I did not do it since I didn't experience any lags related to casting spells. It's rare to experience lag due to creating dummy units even in bunches. What affects performance more in such cases are probably the attached special effects.

    Thanks, I'm gonna check out that leak. Gonna update soon.
     
  12. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    578
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    Updated to v2.1.0
    I believe the leak is fixed now