• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[System] SpellObjs

Level 3
Joined
Aug 27, 2011
Messages
41

SpellObjs


Version: 0.41
LastUpdated: 09/30/2011 at 6:00pm (Central Time)

Introduction

This is my new system for holding all the data related to timed spells (Channeled spells, Dots/Hots, Channeled Dots, Channeled AoEs, Dots/Hots AoEs, passive spell proc). It completely removes the need for Hashtables. It recycles the timers that it uses, and has a built-in DamageTarget() function. Right now I've coded it for Channeled Spells, and Dots, AoEs are coming soon, then Passives. Also I might add any suggested features.

Requires: vJass

Credits:
  • The Creator/s of vJass
  • watermelon_1234, for help with some syntax issues
  • Magtheridon96, for constructive criticism and help with syntax for structs and increasing speed and system usability.
  • Bribe, for constructive criticism and help with general syntax issues, information on better common practices, and speed issues.
  • PurgeandFire111, for constructive criticism and a push in the right direction.
  • Troll-Brain, for being helpful

  • Instructions

    • Copy SpellObjs into the Map Header in the Trigger Editor.

  • To use

    • Copy and paste the given templates, into a new trigger. Edit it to your needs.

Testmap is at the bottom of post.

EDIT: [9-28-11] Updated code and test map to improve the system based on everyone's feedback.
EDIT: [9-30-11] Updated code based on Bribe's feedback.

Alright here is the Code:
JASS:
//--------------------------------------------------------------------------
//
// SpellObjs
//
// Made by: Fox536
// Date:    09/24/2011
// Version: 0.41
//
//==========================================================================
// This is a system to better hold the info, for MUI Spell, that 
// reduces the need to save all your spell's information into a Hashtable,
// down to one Integer save and load.
// Useful for channeled abilities, Dots, Hots, and any other spell that 
// might need a timed events.
//
//==========================================================================
// Features
//  - Recycling
//  - Double Free protection
//  - Holds CasterUnit, TargetUnit, Damage, Amount of times the spell Hits.
//  - Dots, Hots, and other multi-hit Spells now added in for easy use.
//  - What kind of Target the Spell has.
//  - Built-in Damage Target handles targetUnit, or targetDestructable 
//    right now.
//
//==========================================================================
// Planned Features
//  - Seperate Real for Allied targets' healing/dmg as well.
//  - Real for Range/AoE, for AoE Spells.
//  - Array for spell Effects for Enemy targets Spell Effects.
//  - Array for spell Effects for Allied targets Spell Effects.
//
//==========================================================================
// Globals
//  > group     Fox_SpellObjGroup   - This is temporarily holds the Group for enumeration.
//  > hashtable Fox_SpellObjHash     - This is the Hashtable for loading and saving the index of the spell.
//
//==========================================================================
// Struct Methods
//  - Private Static
//      > None
//
//  - Public Static
//      > create - Create a new object.
//        public static method create takes nothing returns thistype
//
//      > getSpellObj - Returns the SpellObj with the given timer.
//        public method getSpellObj takes timer objCastTimer return thistype
//
//  - Private
//      > setupNew - Sets up the Spell to default
//        private method setupNew takes nothing return nothing
//
//      > resetSpell - Clears the Spell to be reused
//        private method resetSpell takes integer spellTypeId, real duration, boolean repeating, real damage returns nothing
//
//      > booleanToBoolExpr - Converts the filter variable to boolexpr.
//        private static method booleanToBoolExpr takes nothing return boolean
//
//  - Public
//      > free - Recycle object.
//        public method free takes nothing returns nothing
//
//      > clear - Clears the SpellObj removing memory leak
//        public method clear takes boolean recycle returns nothing
//
//      > tick - Checks if the spell can be Ticked
//                  - If it can it increases tickCount then returns true.
//                  - Otherwise it clears and recycles itself, then returns false.
//        public method tick takes nothing returns boolean
//
//      > setupUnitTarget - Sets the timer to be used for Spell Targeting Unit
//        public method setupUnitTarget takes real duration, integer repeatTimes, code func, unit caster, unit target, real damage returns boolean
//
//      > setupCoorTarget - Sets the timer to be used for Spell Targeting Coors
//        public method setupCoorTarget takes real duration, integer repeatTimes, code func, unit caster, real x, real y, real damage returns boolean
//
//      > setupSelfTarget - Sets the timer to be used for Spell Targeting Self
//        public method setupSelfTarget takes real duration, integer repeatTimes, code func, unit caster, real damage returns boolean
//
//      > setupDestructableTarget - Sets the timer to be used for Spell Targeting Destructable
//        public method setupDestructableTarget takes real duration, integer repeatTimes, code func, unit caster, destructable target, real damage returns boolean
//
//      > damageTarget - Damages the Target
//        public method damageTarget takes nothing returns nothing
//
//      > damageAoETargets - Damages the Targets of the AoE, either at the targetX, and targetY or using the caster's position.
//        public method damageAoETargets takes code filterFunc returns nothing
//
//      > getTarget - returns the Target of the spell
//        public method getTarget takes nothing returns unit
//
//==========================================================================
// Struct Member's (Variable)
//  - Private Static
//      > integer  instanceCount            - The count of current Objects.
//      > integer  instanceToRecycle        - The current object to recycle.
//
//  - Public Static
//      > None
//
//  - Private
//    - Timer variables
//      > timer         castTimer           - The cast Timer for the spell.
//      > real          castTime            - The duration of the Timer.
//      > boolean       castTimerRepeats    - Whether or not the Timer Repeats.
//      > integer       tickCount           - Number of Ticks so far
//      > integer       tickMax             - Max number of Ticks
//
//    - Spell Target variables
//      > integer       spellTargetType     - The Target Type of the spell.
//                                              > 1 = Unit Targeted Spell
//                                              > 2 = Point Targeted Spell
//                                              > 3 = No Target (Self-Cast/Self-Location Cast)
//                                              > 4 = Targeted Destructable
//      > unit          casterUnit          - The Unit casting the spell.
//      > unit          targetUnit          - The Unit targeted by the spell, for spellType 1 (Unit targeted Spell).
//      > real          targetX             - The Real X targeted by the spell, for spellType 2 (Point Targeted Spell).
//      > real          targetY             - The Real Y targeted by the spell, for spellType 2 (Point Targeted Spell).
//      > destructable  targetDestructable  - The Destructable targeted by the spell, for spellType 4 (Destructable targeted Spell).
//
//    - Spell Data variables
//      > real          damageToWidget      - Damage to do to the widget
//      > real          spellRange          - The AoE of the Spell
//      > bool          filter              - Used to convert the boolean value to a boolexpr
//
//    - Recycling variables
//      > thistype      recycleNext         - Recycle Stack
//      > boolean       recycled            - The Index of the spellObj.
//
//    - Misc
//      > boolean       filter              - The variable used in booleanToBoolExpr
//
//  - Public
//      > None
//
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------
globals
    hashtable   Fox_SpellObjHash        // This holds the Timer handleID for quick lookup.
    group       Fox_SpellObjGroup       // This is the Global Group to keep from having to Create and Destroy a group constantly.
    unit        Fox_SpellObjEnumUnit    // This allows user to check from a function given no parameters.
endglobals

function interface FoxCodeFunction takes nothing returns boolean

private module FoxModSpellObjsInit
    private static method onInit takes nothing returns nothing
        // Initialize Hashtable
        set Fox_SpellObjHash = InitHashtable()
        // Create Group
        set Fox_SpellObjGroup = CreateGroup()
        // Set Unit to null
        set Fox_SpellObjEnumUnit = null
    endmethod
endmodule

library FoxSpellObjs    
    // booleanToBoolExpr - Converts the filter variable to boolexpr.
    function booleanToBoolExpr takes nothing returns boolean
            return SpellObjs.filter
    endfunction
    
    //----------------------------------------------------------------------
    // SpellObj Struct has all needed Code
    //----------------------------------------------------------------------
    struct SpellObjs extends array
        // Handle Initialization
        implement FoxModSpellObjsInit
        //------------------------------------------------------------------
        // Private Static Variables
        //------------------------------------------------------------------    
        // Added to improve the object recycling
        private static integer      instanceCount     = 0 // The count of current Objects.
        private static thistype     instanceToRecycle = 0 // The current object to recycle.
        
        //------------------------------------------------------------------
        // Public Static Variables
        //------------------------------------------------------------------    
        // Misc
        public static boolean       filter              // The variable used in booleanToBoolExpr
        
        //------------------------------------------------------------------
        // Private Struct Variables
        //------------------------------------------------------------------
        // Timer variables
        private timer               castTimer           // The cast Timer for the spell.
        private real                castTime            // The duration of the Timer.
        private boolean             castTimerRepeats    // Whether or not the Timer Repeats.
        private integer             tickCount           // Number of Ticks so far
        private integer             tickMax             // Max number of Ticks
        
        // Spell Target variables
        private integer             spellTargetType     // The Target Type of the spell. Example:
                                                        // 1 = Unit Targeted Spell
                                                        // 2 = Point Targeted Spell
                                                        // 3 = No Target (Self-Cast/Self-Location Cast)
                                                        // 4 = Targeted Destructable
        private unit                casterUnit          // The Unit casting the spell.
        private unit                targetUnit          // The Unit targeted by the spell, for spellType 1 (Unit targeted Spell).
        private real                targetX             // The Real X targeted by the spell, for spellType 2 (Point Targeted Spell).
        private real                targetY             // The Real Y targeted by the spell, for spellType 2 (Point Targeted Spell).
        private destructable        targetDestructable  // The Destructable targeted by the spell, for spellType 4 (Destructable targeted Spell).
        
        // Spell Data variables
        private real                damageToWidget      // Damage to do to the widget
        private real                spellRange          // The AoE of the Spell
        
        // Recycling variables
        private thistype            recycleNext         // Recycle Stack
        private boolean             recycled            // The Index of the spellObj.
        
        //------------------------------------------------------------------
        // Public Struct Variables
        //------------------------------------------------------------------
        // None
        
        //------------------------------------------------------------------
        // Private Static Methods
        //------------------------------------------------------------------
        // None
        
        //------------------------------------------------------------------
        // Public Static Methods
        //------------------------------------------------------------------
        // Create a new object.
        public static method create takes nothing returns thistype
            local thistype this
            // if we aren't going to recycle anything
            if (0 == instanceToRecycle) then
                set instanceCount = instanceCount + 1 // increase instance count
                set this = instanceCount
            else
                // we have an instance to recycle
                // we set the current instance to the recycled one
                set this = instanceToRecycle
                // we set the recycled instance to the one that proceeds it in the recycle stack
                set instanceToRecycle = instanceToRecycle.recycleNext
            endif
            // setup the object
            call setupNew()
            // Return new object
            return this
        endmethod
        
        // getSpellObj - Returns the SpellObj with the given timer.
        public static method getSpellObj takes timer objCastTimer returns thistype
            return thistype[LoadInteger(Fox_SpellObjHash, GetHandleId(objCastTimer), 0)]
        endmethod
        
        //------------------------------------------------------------------
        // Private Methods
        //------------------------------------------------------------------
        // setupNew - Sets up the Spell to default
        private method setupNew takes nothing returns nothing
            // Object Variables
            //--------------------------------------------------------------
            // Initializes all the settings for the new SpellObj
            // if castTimer doesn't exist
            if (.castTimer == null) then
                // then create new timer
                set .castTimer       = CreateTimer()
                // Save the handle of the new timer
                call SaveInteger(Fox_SpellObjHash, GetHandleId(.castTimer), 0, this)
            endif
            // Initialize all other variables
            set .castTime            = 1.0
            set .castTimerRepeats    = false
            set .casterUnit          = null
            set .targetUnit          = null
            set .targetX             = 0.0
            set .targetY             = 0.0
            set .targetDestructable  = null
            set .spellTargetType     = 0
            set .tickCount           = 0
            set .tickMax             = 1
            set .spellRange          = 0.0
            // Sets recycled to false, so it can later be recycled
            set .recycled            = false
        endmethod
        
        // resetSpell - Clears the Spell to be reused
        private method resetSpell takes integer spellTypeId, real duration, boolean repeating, real damage returns nothing
            // Set Everything to default
            //--------------------------------------------------------------
            set .casterUnit          = null
            set .targetUnit          = null
            set .targetX             = 0.0
            set .targetY             = 0.0
            set .targetDestructable  = null
            
            // Set given Variables
            //--------------------------------------------------------------
            set .spellTargetType     = spellTypeId
            set .castTime            = duration
            set .castTimerRepeats    = repeating
            set .damageToWidget      = damage
        endmethod
        
        //------------------------------------------------------------------
        // Public Methods
        //------------------------------------------------------------------
        // free - Recycle object.
        public method free takes nothing returns nothing
            if (.recycled == false) then
                // Add recycled instance to stack
                set .recycleNext = instanceToRecycle
                // Make next recycled instance the current one
                set .instanceToRecycle = this
            endif
        endmethod
        
        // clear - Clears the SpellObj removing memory leak
        public method clear takes boolean recycle returns nothing
            // Pause Timer to avoid possible repeat tick
            call PauseTimer(.castTimer)
            // Nulls Old Data
            set .casterUnit          = null
            set .targetUnit          = null
            set .targetX             = 0.0
            set .targetY             = 0.0
            set .targetDestructable  = null
            set .tickCount           = 0
            set .tickMax             = 1
            set .spellRange          = 0.0
            if (recycle) then
                call free()
            endif
        endmethod
        
        // tick - Checks if the spell can be Ticked
        //  - If it can it increases tickCount then returns true.
        //  - Otherwise it clears and recycles itself, then returns false.
        public method tick takes nothing returns boolean
            // If tickCount is lower then tickMax, Increase and return true.
            //--------------------------------------------------------------
            if (.tickCount < .tickMax) then
                // Increase tickCount
                set .tickCount = .tickCount + 1
                
                // Ticks have not reached max yet, so return true
                return true
                
            //--------------------------------------------------------------
            // Otherwise Clear and Recycle this, and return false.
            else
                // null this out and recycle it
                call clear(true)
                
                // Ticks have reached there max
                return false
            endif
        endmethod
        
        // setupUnitTarget - Sets the timer to be used for Spell Targeting Unit
        public method setupUnitTarget takes real duration, integer repeatTimes, code func, unit caster, unit target, real damage returns boolean
            // Setup if recycled is false, then return true
            //--------------------------------------------------------------
            if (.recycled == false) then
                // Clear Old Data
                call resetSpell(1, duration, (repeatTimes > 1), damage)
                // Set Variables
                set .casterUnit          = caster
                set .targetUnit          = target
                set .tickMax             = repeatTimes
                // Start Timer
                call TimerStart(.castTimer, .castTime, .castTimerRepeats, func)
                
                return true
            endif

            //--------------------------------------------------------------
            // Otherwise return false
            return false
        endmethod

        // setupCoorTarget - Sets the timer to be used for Spell Targeting Coors
        public method setupCoorTarget takes real duration, integer repeatTimes, code func, unit caster, real x, real y, real damage returns boolean
            // Setup if recycled is false, then return true
            //--------------------------------------------------------------
            if (recycled == false) then
                // Clear Old Data
                call resetSpell(2, duration, (repeatTimes > 1), damage)
                // Set Variables
                set .casterUnit          = caster
                set .targetX             = x
                set .targetY             = y
                set .tickMax             = repeatTimes
                // Start Timer
                call TimerStart(.castTimer, .castTime, .castTimerRepeats, func)
                
                return true
            endif
            
            //--------------------------------------------------------------
            // Otherwise return false
            return false
        endmethod
        
        // setupSelfTarget - Sets the timer to be used for Spell Targeting Self
        public method setupSelfTarget takes real duration, integer repeatTimes, code func, unit caster, real damage returns boolean
            // Setup if recycled is false, then return true
            //--------------------------------------------------------------
            if (recycled == false) then
                // Clear Old Data
                call resetSpell(3, duration, (repeatTimes > 1), damage)
                // Set Variables
                set .casterUnit          = caster
                set .tickMax             = repeatTimes
                // Start Timer
                call TimerStart(.castTimer, .castTime, .castTimerRepeats, func)
                
                return true
            endif
            
            //--------------------------------------------------------------
            // Otherwise return false
            return false
        endmethod
        
        // setupDestructableTarget - Sets the timer to be used for Spell Targeting Destructable
        public method setupDestructableTarget takes real duration, integer repeatTimes, code func, unit caster, destructable target, real damage returns boolean
            // Setup if recycled is false, then return true
            //--------------------------------------------------------------
            if (recycled == false) then
                // Clear Old Data
                call resetSpell(4, duration, (repeatTimes > 1), damage)
                // Set Variables
                set .casterUnit          = caster
                set .targetDestructable  = target
                set .tickMax             = repeatTimes
                // Start Timer
                call TimerStart(.castTimer, .castTime, .castTimerRepeats, func)
                
                return true
            endif
            
            //--------------------------------------------------------------
            // Otherwise return false
            return false
        endmethod
        
        // damageTarget - Damages the Target
        public method damageTarget takes nothing returns nothing
            // Locals
            local integer i = 0
            local unit tempUnit = null
            // if the spellTargetType is Unit or Destructable then
            if ((.spellTargetType == 1) or (.spellTargetType == 3) or (.spellTargetType == 4)) then
                // Damage Unit/Destructable
                call UnitDamageTarget(.casterUnit, .targetUnit, .damageToWidget, false, false, null, null, null)
            endif
        endmethod
        
        // damageAoETargets - Damages the Targets of the AoE, either at the 
        // targetX, and targetY or using the caster's position.
        public method damageAoETargets takes FoxCodeFunction filterFunc returns nothing
            local real damageTargetX
            local real damageTargetY
            // Damage AoE at Point
            if (.spellTargetType == 2) then
                set damageTargetX = targetX
                set damageTargetY = targetY
            elseif (.spellTargetType == 4) then
                set damageTargetX = GetUnitX(.casterUnit)
                set damageTargetY = GetUnitY(.casterUnit)
            elseif (.spellRange > 0.0) then
                set damageTargetX = GetUnitX(.targetUnit)
                set damageTargetY = GetUnitY(.targetUnit)
            else
                return
            endif
            // call AoE Coor Function here (use given function to filter through the units)
            call GroupEnumUnitsInRange(Fox_SpellObjGroup, damageTargetX, damageTargetY, .spellRange, null)
            // Loop through the units found
            loop
                // Set the Fox_SpellObjEnumUnit to the first in the Global Group
                set Fox_SpellObjEnumUnit = FirstOfGroup(Fox_SpellObjGroup)
                exitwhen Fox_SpellObjEnumUnit == null
                if (filterFunc.evaluate()) then
                    // Damage the Selected Unit.
                    call UnitDamageTarget(.casterUnit, Fox_SpellObjEnumUnit, .damageToWidget, false, false, null, null, null)
                    // Remove Unit from Group
                    call GroupRemoveUnit(Fox_SpellObjGroup, Fox_SpellObjEnumUnit)
                endif
            endloop
        endmethod
        
        // getTarget - returns the Target of the spell
        public method getTarget takes nothing returns unit
            return .targetUnit
        endmethod
        
    endstruct
    
endlibrary

JASS:
//--------------------------------------------------------------------------
//
// SpellUnitChanneled Template - [SpellObjs]
//
// Made by: Fox536
// Date:    09/24/2011
// Version: 0.6
//==========================================================================
// This spell template for channeled spells, and should be very easy to use 
// configure, and edit. It only requires, that you have 
// [SpellObjs by Fox536] in your map.
//==========================================================================
// Features
//  - Easy Customization
//  - Scope, so you can create as many spells using the template and 
//    never have to change the code except for the constants and the
//    Fox_SpellUnit_Tick() function.
//==========================================================================
// scope FoxSpellUnit1 - Change if you need to add another spell.
scope FoxSpellUnitChanneled1

    //======================================================================
    // Config
    //======================================================================
    // To create a spell that Heals, set Fox_SpellUnitDot_DamageAmount to a
    // negative value.

    //----------------------------------------------------------------------
    // Constant Functions
    //----------------------------------------------------------------------
    // Fox_SpellUnitChanneled_SpellId - Set this your the Spell's Id
    constant function Fox_SpellUnitChanneled_SpellId takes nothing returns integer
        return 'A000'
    endfunction
    
    // Fox_SpellUnitChanneled_DamageAmount - Set this your the Spell's Damage Amount
    constant function Fox_SpellUnitChanneled_DamageAmount takes nothing returns real
        return 200.0
    endfunction
    
    // Fox_SpellUnitChanneled_TickDelay - Set this your the Spell's Tick Delay
    constant function Fox_SpellUnitChanneled_TickDelay takes nothing returns real
        return 10.0
    endfunction
    
    // Fox_SpellUnitChanneled_TickAmount - Set this your the Spell's tick amount.
    // (The amount of times you want the timer function to happen.)
    constant function Fox_SpellUnitChanneled_TickAmount takes nothing returns integer
        return 1
    endfunction
    
    //----------------------------------------------------------------------
    // Main Timer Function
    //----------------------------------------------------------------------
    // Fox_SpellUnitChanneled_Tick - Is called whenever the timer expires.
    //  - edit for customizations if you need to.
    function Fox_SpellUnitChanneled_Tick takes nothing returns nothing
        local SpellObjs obj
        // Get the Obj.
        set obj = SpellObjs.getSpellObj(GetExpiredTimer())
        // if the Obj. doesn't equal null (Here just in case)
        if (obj != null) then
            // Damage the Spell's target
            call obj.damageTarget()
            // Clears and Recycles the Obj.
            call obj.clear(true)
        endif
    endfunction
    
    //======================================================================
    // End Config
    //======================================================================
    
    
    //----------------------------------------------------------------------
    // Conditions
    //----------------------------------------------------------------------
    // Fox_SpellUnitChanneled_Conditions - Checks if correct spell was cast.
    function Fox_SpellUnitChanneled_Conditions takes nothing returns boolean
        return (GetSpellAbilityId() == Fox_SpellUnitChanneled_SpellId())
    endfunction

    //----------------------------------------------------------------------
    // Actions
    //----------------------------------------------------------------------
    // Fox_SpellUnitChanneled_Actions - Starts the Channeling
    function Fox_SpellUnitChanneled_Actions takes nothing returns nothing
        local SpellObjs obj = SpellObjs.create()
        // Starts the Channel
        call obj.setupUnitTarget(Fox_SpellUnitChanneled_TickDelay(), Fox_SpellUnitChanneled_TickAmount(), function Fox_SpellUnitChanneled_Tick, GetTriggerUnit(), GetSpellTargetUnit(), Fox_SpellUnitChanneled_DamageAmount())
    endfunction

    //----------------------------------------------------------------------
    // Main Setup
    //----------------------------------------------------------------------
    // InitTrig_SpellUnit - Main Trigger Setup
    function InitTrig_FoxSpellUnitChanneled1 takes nothing returns nothing
        set gg_trg_FoxSpellUnitChanneled1 = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(gg_trg_FoxSpellUnitChanneled1, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
        call TriggerAddCondition(gg_trg_FoxSpellUnitChanneled1, Condition( function Fox_SpellUnitChanneled_Conditions))
        call TriggerAddAction(gg_trg_FoxSpellUnitChanneled1, function Fox_SpellUnitChanneled_Actions)
    endfunction
    
endscope

JASS:
//--------------------------------------------------------------------------
//
// SpellUnitDot Template - [SpellObjs]
//
// Made by: Fox536
// Date:    09/24/2011
// Version: 0.6
//==========================================================================
// This spell template for Dot or Hot spells, and should be very easy to 
// use configure, and edit. It only requires, that you have 
// [SpellObjs by Fox536] in your map.
//==========================================================================
// Features
//  - Easy Customization
//  - Scope, so you can create as many spells using the template and 
//    never have to change the code except for the constants and the
//    Fox_SpellUnitDot_Tick() function.
//==========================================================================
// scope FoxSpellUnitDot1 - Change if you need to add another spell.
scope FoxSpellUnitDot1

    //======================================================================
    // Config
    //======================================================================
    // To create a spell that Heals, set Fox_SpellUnitDot_DamageAmount to a
    // negative value.
    
    //----------------------------------------------------------------------
    // Constant Functions
    //----------------------------------------------------------------------
    // Fox_SpellUnitDot_SpellId - Set this your the Spell's Id
    constant function Fox_SpellUnitDot_SpellId takes nothing returns integer
        return 'A001'
    endfunction
    
    // Fox_SpellUnitDot_BuffId - Set this your the Buff's Id
    constant function Fox_SpellUnitDot_BuffId takes nothing returns integer
        return 'B000'
    endfunction

    // Fox_SpellUnitDot_DamageAmount - Set this your the Spell's Damage Amount
    constant function Fox_SpellUnitDot_DamageAmount takes nothing returns real
        return 25.0
    endfunction
    
    // Fox_SpellUnitDot_TickDelay - Set this your the Spell's Tick Delay
    constant function Fox_SpellUnitDot_TickDelay takes nothing returns real
        return 5.0
    endfunction

    // Fox_SpellUnitDot_TickAmount - Set this your the Spell's tick amount.
    // (The amount of times you want the timer function to happen.)
    constant function Fox_SpellUnitDot_TickAmount takes nothing returns integer
        return 10
    endfunction

    //----------------------------------------------------------------------
    // Main Timer Function
    //----------------------------------------------------------------------
    // Fox_SpellUnitDot_Tick - Is called whenever the timer expires.
    //  - edit for customizations if you need to.
    function Fox_SpellUnitDot_Tick takes nothing returns nothing
        local SpellObjs obj = SpellObjs.getSpellObj(GetExpiredTimer())
        if (obj != null) then
            if (obj.tick()) then
                if (GetUnitAbilityLevel(obj.getTarget(), Fox_SpellUnitDot_BuffId()) > 0) then
                    call obj.damageTarget()
                else
                    call obj.clear(true)
                endif
            else
                call UnitRemoveAbility(obj.getTarget(), Fox_SpellUnitDot_BuffId())
            endif
        endif
    endfunction
    
    //======================================================================
    // End Config
    //======================================================================

    //----------------------------------------------------------------------
    // Conditions
    //----------------------------------------------------------------------
    function Fox_SpellUnitDot_Conditions takes nothing returns boolean
        return (GetSpellAbilityId() == Fox_SpellUnitDot_SpellId())
    endfunction

    //----------------------------------------------------------------------
    // Actions
    //----------------------------------------------------------------------
    function Fox_SpellUnitDot_Actions takes nothing returns nothing
        local SpellObjs obj = SpellObjs.create()
        local unit target  = GetSpellTargetUnit()
        // if unit is not already under the effect of the spell.
        if not (GetUnitAbilityLevel(target, Fox_SpellUnitDot_BuffId()) > 0) then
            // Create new SpellObj
            set obj = SpellObjs.create()
            // Setup new SpellObj
            call obj.setupUnitTarget(Fox_SpellUnitDot_TickDelay(), Fox_SpellUnitDot_TickAmount(), function Fox_SpellUnitDot_Tick, GetTriggerUnit(), target, Fox_SpellUnitDot_DamageAmount())
        endif
        // Remove leaks
        set target = null
    endfunction

    //----------------------------------------------------------------------
    // Main Setup
    //----------------------------------------------------------------------
    function InitTrig_FoxSpellUnitDot1 takes nothing returns nothing
        set gg_trg_FoxSpellUnitDot1 = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(gg_trg_FoxSpellUnitDot1, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
        call TriggerAddCondition(gg_trg_FoxSpellUnitDot1, Condition( function Fox_SpellUnitDot_Conditions))
        call TriggerAddAction(gg_trg_FoxSpellUnitDot1, function Fox_SpellUnitDot_Actions)
    endfunction

endscope
 

Attachments

  • Fox SpellObjects Test Map.w3x
    36.8 KB · Views: 73
Last edited:
You should be using a library :p
Using a scope for a system is like creating a wrapper function that doesn't inline ;p (BAD)

JASS:
static method GetIndexFromTimerHandle takes integer handleId returns thistype
            local integer i = 0
            if (TotalSpellObjs >= 0) then
                loop
                    exitwhen i > TotalSpellObjs
                    if (SpellObjs[i] != null) then
                        // If HandleIds match
                        if (handleId == GetHandleId(SpellObjs[i].castTimer)) then
                            // Return Matched SpellObj
                            return SpellObjs[i]
                        endif
                    endif
                    set i = i + 1
                endloop
            endif
            // No Matched SpellObj
            //return null
            return -1
        endmethod

This is clearly an O(n) when it could be an O(1) with simple hashing :p
 
Level 3
Joined
Aug 27, 2011
Messages
41
This is clearly an O(n) when it could be an O(1) with simple hashing :p
What do you mean O(n) instead of O(1) are you talking about the fact that I start my arrays on 0 instead of Blizzard's strange 1? lol

So I should just change the scope to library? Is there any difference in the calls I need to do that?

Lol thanks again, learning a new language is always fun XD
 
What do you mean O(n) instead of O(1) are you talking about the fact that I start my arrays on 0 instead of Blizzard's strange 1? lol

So I should just change the scope to library? Is there any difference in the calls I need to do that?

Lol thanks again, learning a new language is always fun XD

It describes the complexity. In your case, it is doing an O(n) loop because it searches through an array until it finds the correct one. (known as a linear search)

O(1) would be a simple retrieval/lookup. For example, x[n] is O(1) because it is a constant. Hashtables have O(1) complexity. :)

You can check out this table:
http://en.wikipedia.org/wiki/Big_O_Notation#Orders_of_common_functions

-----

Anyway, as for the code, I recommend that you add to the top of the documentation a list of the function names. (and possibly what they do) so that it is easier to work with. (This way, people don't have to search through your code to find the names of the functions, they can just look at the documentation) For example:
JASS:
//    struct SpellObj
//
//        static method GetIndexFromTimerHandle takes integer handleId returns thistype
//        static method Create takes nothing returns thistype
//        method SetupUnitTarget takes real duration, integer repeatTimes, code func, unit caster, unit target, real damage returns boolean
// etc...
 
JASS:
               // Use one of the recycled objects
                // Increase Total Objects
                set TotalSpellObjs = TotalSpellObjs + 1
                // Move recycled obj to used array
                set SpellObjs[TotalSpellObjs] = RecycledObjs[TotalRecSpellObjs]
                // Remove moved obj from recycled array
                //set RecycledObjs[TotalRecSpellObjs] = null
                set RecycledObjs[TotalRecSpellObjs] = 0
                // Decrease Recycled Object Total
                set TotalRecSpellObjs = TotalRecSpellObjs - 1
                // For access set variable to obj
                set obj = SpellObjs[TotalSpellObjs]
                // Set recycled to false to allow it to be recycled again later
                set obj.recycled = false
                // Return the recycled obj
                return SpellObjs[TotalSpellObjs]

-_-'

You should just be using structs at this point.

The script itself needs such a large amount of work. I will try to take the time in the next days to give this some constructive feedback, but as it is now it's just unnacceptable.
 
JASS:
        private static method CreateNew takes nothing returns thistype
            // Locals
            //--------------------------------------------------------------
            // Local for new SpellObj
            local thistype newSpellObj
            
            // Struct Variables
            //--------------------------------------------------------------
            // Create New Object
            set newSpellObj                     = SpellObj.allocate()
            // Increase the Total
            set TotalSpellObjs                  = TotalSpellObjs + 1
            // Set Index to the Total of the objects
            set newSpellObj.index               = TotalSpellObjs
            // Set recycled to false
            set newSpellObj.recycled            = false
            // Add the new Object to the used Array
            set SpellObjs[TotalSpellObjs]       = newSpellObj
            
            // Object Variables
            //--------------------------------------------------------------
            // Initializes all the settings for the new SpellObj
            set newSpellObj.castTimer           = CreateTimer()
            set newSpellObj.castTime            = 1.0
            set newSpellObj.castTimerRepeats    = false
            set newSpellObj.casterUnit          = null
            set newSpellObj.targetUnit          = null
            set newSpellObj.targetX             = 0.0
            set newSpellObj.targetY             = 0.0
            set newSpellObj.targetDestructable  = null
            set newSpellObj.spellTargetType     = 0
            set newSpellObj.tickCount           = 0
            set newSpellObj.tickMax             = 1
            
            // Return the new object
            return SpellObjs[TotalSpellObjs]
        endmethod

->

JASS:
        private static integer ic = 0
        private static integer ir = 0
        private thistype rn
        private static method create takes nothing returns thistype
            local thistype this
            if 0==ir then
                set ic=ic+1
                set this=ic
            else
                set this=ir
                set ir=.rn
            endif
            // Using 'this' as the name allows you to omit using 'this' when referring to the members.
            set .castTimer           = CreateTimer()
            set .castTime            = 1.0
            set .castTimerRepeats    = false
            set .casterUnit          = null
            set .targetUnit          = null
            set .targetX             = 0.0
            set .targetY             = 0.0
            set .targetDestructable  = null
            set .spellTargetType     = 0
            set .tickCount           = 0
            set .tickMax             = 1
            return this
        endmethod

Also, if you use this, you have to make the struct extend an array:
struct YourStruct extends array
When you do this, JassHelper doesn't generate allocate and deallocate methods.
Normal structs in vJass generate a lot of shit-code.
If you make them extend arrays and allocate the struct instances yourself, you'd avoid all the shit-code.
So instead of calling .deallocate, you should do this:

set .rn=ir
set ir=this

And the reason you should be using a library instead of a scope is because scopes are moved to the end of the map script, so your system can't be used by anyone :/
 
If there is so much work needed to be put in this system to make it look properly (so yeah, you basically have to rewrite everything..), why haven't you post this in Triggers & Scripts asking for "Help me optimise this" or just pm one of experienced vJass users?

I'm pretty sure that there are many guys around who are willing to help you. Anyways, the conclusion is: why did you upload the resource without reviewing it first? ;/
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
The first rule of creating systems in vJASS - Always within library/endlibrary.

Do you need to use hashtables else wise? I don't see any problem with using Bribe's table.

Timer-recycling is something that is already been invented, ever heard of TimerUtils by Vexorian?

Custom UnitDamageTarget? Why? I don't see why this system should control this while we have damage detection engines out there.

The idea seem fairly new to me yet the execution is a mess. But keep it up, someone will guide you as I'm too tired right now.
 
Level 3
Joined
Aug 27, 2011
Messages
41

@Everyone


Wow that was alot of comments but I replied to all lol. I'll continue to work on it, but alot of the comments were stating something without pointing me in any direction lol. Anyways I'll start fixing the parts yall mentioned.

@Magtheridon96

- On library: I'll change it to be a library, but as far as it being unusable by anyone, it works fine as is cause the spells are in a scope as well so they are being put under it lol

- on create() method: your variables ic, ir, rn, they aren't easily figured out, after looking it over several times the best I can come up with is...

ic seems to have something to do with count like my total variable maybe...

ir seems to have something to do with the recycled count maybe....

rn well to be honest I have no clue what this is for lol

As far as removing everything before the .variableName you might think about posting that in the "Read before posting" or your "Structs for Dummies". Because believe it or not I've done research on systems, and didn't find any of the comments given here in any of my research lol.

Changing it to extending an Array... thought bout that but couldn't find any actual documentation on what the difference is and what the different syntax is, lol plus you put
Struct extends array
Here's a simple concept:
If a struct has no methods, just make it extend an array.
NEVER make a struct involving spell data extend an array!!!

@PurgeandFire111


On the Hashing: I read that link you posted, as far as making it hashing, unless there is another way besides using hashtable, adding hashing would kill the entire point of the library, but if there is a way to add hashing could you point me to a general explanation, or tutorial so I can find a way to code it in?

On the Method Comments: Thats a great idea, I just didn't want to over do the comments because last time I posted something I was told that there were too much comments and I commented these to about the same lol, I'll add the method comments at the top though :)


@Bribe


On Recycling: so your saying its pointless to recycle, that I should just throw out recycling or do I need to use/create another system that handles just the recycling of timer?
btw, any feedback would be great :)

@Spinnaker

I did review it and not to sound mean, or mad because I'm not, there really isn't anywhere that says "Here at the HiveWorkshop, we want our code like this, this and this" so as far as making it look like what I've seen posted on this thread it's all horribly nondecript, and hard to read lol. No offense, I know alot of that probably has to do with the fact that longer names are slower(not by much but still).

@baassee


On the Library vs Scope: again even with this site a my best friend Google I couldn't find any concrete documentation on Library so I used what I know and put it in a namespace lol.

ever heard of TimerUtils by Vexorian? - Yes I have I looked at it and I don't know whether it's because it's coded in Jass/vJass but that system didn't make much sense. I have a hard enough time reading peoples badly written code that has comments and full variable names much less a highly optimized code with neither lol, that just hurt my head. Trying to follow it was crazy.

Custom UnitDamageTarget? Not real sure what you're talking about on the Custom part it's just calling a native function, but it holds, and handles dealing the damage for you instead of having to define what you want to do in every spell, you just tell it what kind of spell you want and it has all the logic to find the targets for you. Rather then having to constantly keep adding code to get the targets for you and then damaging them yourself.
 
ic seems to have something to do with count like my total variable maybe...

ir seems to have something to do with the recycled count maybe....

rn well to be honest I have no clue what this is for lol

ic = instanceCount
ir = instanceRecycler
rn = RecycleNext

RecycleNext is used for a recycle stack.
When we deallocate something, we set the instance's 'rn' to the current instance that we're going to recycle, and then, we set the current instance we're going to recycle to the instance that we're deallocating.

And I'm glad you saw my tutorial :3
I said that because, well, when you have no methods, you can make the struct extend an array without having to use the allocate/deallocate methods.
You can make ANY struct extend an array.
Just add those 3 members (ic,ir,rn)
And allocate/deallocate the way I showed you :)
The reason: Efficiency.

ever heard of TimerUtils by Vexorian? - Yes I have I looked at it and I don't know whether it's because it's coded in Jass/vJass but that system didn't make much sense. I have a hard enough time reading peoples badly written code that has comments and full variable names much less a highly optimized code with neither lol, that just hurt my head. Trying to follow it was crazy.

TimerUtils:
- What it does:
You have an array of 256 timers (configurable)
NewTimer(), you get one of those timers from the list (it is now removed temporarily until you release it)
ReleaseTimer(), you release the timer, pausing it and putting it back into the list for future use

It's simply a stack :p
- Why:
Because DestroyTimer is buggy.
 
As has been said before this thing should be debugged before uploading. Repost this in Triggers / Scripts, if you want some advice, because this simply does not even come close to meeting the criteria of submissions.

No "work in progress" is "supposed" to be submitted. I am usually lax on this but... this is really poorly coded and shows a lack of understanding of fundamental vJass principles like common struct recycling, useage of O(N) complexity where hashtables are more than fine anyway. By the way what do you have against hashtables?

OnlyFunctionNamesAreCasedLikeThis, structMembers, structMethods and variables are casedLikeThis. CONSTANT_VARIABLES_ARE_CASED_LIKE_THIS.

What is the point in the locals here?

JASS:
        // DamageTarget - Damages the Target
        method DamageTarget takes nothing returns nothing
        // Locals
        //----
        local integer i = 0
        local unit tempUnit = null

Sorry to be harsh but really, you are looking too highly on this resource. I once wrote a library that turned a string into an integer, and was so impressed by it, and then realized that the whole thing could be replaced with a simple StringHash call.

Here's the resource I'm talking about:

http://www.hiveworkshop.com/forums/graveyard-418/string-handle-163741/
 
Level 3
Joined
Aug 27, 2011
Messages
41
By the way what do you have against hashtables?
They are from everyones posts slower then arrays(and arrays are slower then regular variables lol), plus they're one more global you have lol.

When starting this I asked myself serveral questions...

Q: Whats something that irritates me?
A: Not having a standard way to create spells?

Q: How do I make something that could create a standard way of handling this?
A: A spell Handling system.

Q: How do I make it simple?
A: Create a base for all spell handling.

Q: How do I make it innovative, but still simple?
A: Create it where it handles, everything but still make it customizable. (This is hard, because the more customizable you make somthing, the less simple it usually is lol.)

Q: How do I make it unique?
A: Remove the need for a global variable, in this case a hashtable.

lol, anyways as for the casing everyone's standards are different and again I didn't see that in the "Read Before Posting" thread so sorry lol.

As for the lack of fundamental vJass principles, I have yet to find a thread, tutorial or anything that states what everyone here sets as a standard... So I'm not a bad coder, I maybe very ignorant though lol.

And I'm not looking on this highly I just know that it works, and it not slow lol, but if it's coding is bad then I'm more then willing to have someone tell me what's wrong, and attempt to make it better.

Plus I don't think you're being harsh. You are simply stating what's wrong and then saying what I should do. If I'm incorrect about something as long as you say what it is and point me in the direction of fixing it, I consider it constructive criticism :). Now if you had posted something like "This is shit u NUB!!!" then it'd be harsh and I'd be mad lol.

Anyways though, I keep working on it and see where I can fix it better, I'll have to pm someone about the whole recycle part. Because I really didn't know how the standard way of doing was, I created that from scratch, and I think that part turned out good for not never creating any kind of recycling system in any language before lol.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
But it's not like we have a guide "(v)Jass standard rules".
It's common for us since we have followed its usage since years but that's not natural for new people (and yes there are still new "players" each day).
Keep it in mind.

Plus like stated we have all our own standard, personnaly i'm sick when i see these short names, and i don't really think inlining allocate/deallocate methods of structs is worth it in most cases (even if it generates moar "shit" jass code), it makes your code more confusing and if you care that much jasshelper is open source, i mean jasshelper should be edited instead of users' vJass code ...
 
Plus like stated we have all our own standard, personnaly i'm sick when i see these short names, and i don't really think inlining allocate/deallocate methods of structs is worth it in most cases (even if it generates moar "shit" jass code).

Agreed on both points, alloc/dealloc speed are incredibly overrated. So we have an extra if-check for allocating - oh my god! And compress names works fine with the optimizer as long as you don't use concatenation within VariableEvent/ExecuteFunc or custom natives. For everyone who uses such things there are easy workarounds (like using boolexprs instead of concatenated variable events, ExecuteFunc has at least 5 alternatives, and UnitAlive is easy enough to replace with UNIT_TYPE_DEAD).

Maybe cJass will start working again properly, then we can use their optimizer. Save for Nestharus who claims things like hooking RemoveUnit or an extra if-statement in allocate method bring his map to a grinding halt.

The only time to really avoid normal structs is by avoiding function interfaces/extended structs. But when are those really necessary? Modules are typically better.

I will work on standardization template that covers the basics, like how variables/functions/methods should be cased, when to use a struct or when to use a library, when to use a private prefix or when to make a struct extend array, things like that.
 
Last edited:
Level 3
Joined
Aug 27, 2011
Messages
41
@Bribe
Well hashtables are 4x slower than arrays.

Don't throw away useful programming concepts just because you
want to be different. That's like how stupid Apple is being by throwing
away USB support on iPad.

How is throwing away something slower, throwing it away because you want to be different? lol. But on the Apple thing heck ya it's retarded lol.


@Troll-Brain
jasshelper is open source, i mean jasshelper should be edited instead of users' vJass code ...

Where is the open-source version and what language is it wrote in I might be able to do something to help out if it's C++, or C# and I get feedback about what ya'll want :)

@Magtheridon96
Awesome man, I've read a bit of it so far and it's pretty explanatory, I have a few spots I have questions about, but that's not because I don't get programming fundamentals, I just not getting what's being used lol.
 
Level 3
Joined
Aug 27, 2011
Messages
41
@Troll-Brain Ok I'll look into it lol I'll get back to you bout it :)

@Everyone check the updated code, and testmap. I changed the system to fit what everyone told me. Let me know what you think about it...


PS: Linked lists for recycling, I always forget about linked lists. That would of saved me so much time lol, basic array replacement fundamentals lol. Thanks Magtheridon96 for posting the tutorial. Once I saw all of it working together I thought duh, I bet that's a linked list. To think, the first time I learned about them I thought I'll never use them. Now I've used them several times to solve a few problems that would be overly complicated otherwise lol. XD
 
Last edited:
Level 3
Joined
Aug 27, 2011
Messages
41
Well I figured it'd be a little slower then that, but still if you only had 1 or 2 spells going it'd still be faster lol just kidding, anyways I whipped that code up I'm sure one of you will see something that needs improvement, :) just let me know.
 
Instead of "Filter(function booleanToBoolExpr)" you should use "null" as the filter and put this if-check inside your FirstOfGroup loop.

This needs a module initiailzer otherwise it is quite useless.

When instances are destroyed you should be removing the saved integer attached to the timer data. So use 0 as the parent key and use the timer as the child key, then use RemoveSavedInteger(hash, 0, GetHandleId(timer))
 
Level 3
Joined
Aug 27, 2011
Messages
41
@Bribe

"Filter(function booleanToBoolExpr)" - I didn't even think of that, it irratated me that it wouldn't just let me use the function I had to use a function interface lol.

As for destroying instances I don't destroy them, I recycle them and the timer isn't ever destroyed either so the key is always the same?

Module Initializer - I would use it if I knew the syntax, I read your tutorial, but wasn't sure what all a module needed lol. Any advice on it would be great :)

@Magtheridon96
I read it the day he posted it, I changed/added alot based off of what he wrote as well, just wasn't sure about the module, plus since the spells were in scopes I didn't think it would affect them lol.

@Both
I'll do a couple more searches on them, but any help from either of you on Modules would be great lol :)
 
Level 3
Joined
Aug 27, 2011
Messages
41
JASS:
private module M
    private static method onInit takes nothing returns nothing
        //Your code goes here.
    endmethod
endmodule
private struct S extends array
    implement M
endstruct

Should the implement not be in the library, Im confused lol.

Edit: Lol I tried it and it seems to work not sure how but it does, plus updated code to deal with the "boolexpr" issue, plus fixed the fact that doing it the way I did wouldn't of given the end user a way to check the results lol. Didn't realize that until after re-reading my code and saying "What was I thinking, man I must of been tired!" lol but it should work fine now.

If no one has anything else that needs major fixing, then I'll start to improve and refine the system, adding features, more templates and such. Any feedback, comments, concerns, issues, or suggestions would be greatly appreciated XD.
 
Level 3
Joined
Aug 27, 2011
Messages
41
Lol ya thats pretty weird how he coded the vJass. Right now one of the things I'm working on is a vJass Compiler. It's been an absolute nightmare, as I hate any kind of string parsing lol. I just think it's a major pain, I'd rather be dealing with numbers. But it's been good and I've made some progress with it, So if you had any changes you think should be made let me know what they are I'll see if I can make it happen.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
He could still work on his vJass parser to fix bugs and change silly things (such as the module initializer priority), since cjass generates vJass code.

Also cJass has almost no debug at all (i mean detect and display code grammar error), and i don't think it will change.
 
Level 3
Joined
Aug 27, 2011
Messages
41
Ya I'm still looking at a replacement for vJass unfortunately if I succeed it'll be C#. So it'll require at least .Net and windows :( but I'm most capable of doin it in C#, so my chances of making work are so much better then if I was doing it in straight C, and I won't even attempt to try in Java. There isn't enough resources for Java, no decent IDE, nothing lol. My parser if finished is going to be bundled with my IDE I'm creating for sure, and a complier will probably be added so it can be used for New Gen.
 
To be honest, this is the kind of resource that nobody would use.
It just adds overhead to the spell and slows it down with extra functions,
memory allocations, and other things.

Spells are easy to code from scratch and managing spell data is really easy
to pull off. Assume that I only wanted to save the caster and the target.
This system would cache everything, even the things I don't need like the
level, the casterX, the casterY, etc.. which is obviously a lot of overhead
relative to just 2 array writes. This also uses lots of timers and hashtable
lookups, making it slower :/

This needs a lot of work to get approved.
 
You know, I have an idea for a SpellStruct.
You'd declare static constant integers like WANT_CASTER, the module would check if it exists, then it would write a unit array called caster and set caster[this] to the triggering unit.
The only problem is the clutter of constant globals.
But, at least they'll get deleted by an optimizer, so I say go for it :)

Trust me on this idea.
It adds no overhead at all!

edit
Actually, JassHelper might not support checking whether a variable exists :eek:
If that's the case, just use empty static methods :p

edit
Nevermind, that would look ugly ;_;
 
Last edited:
Top