• 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] Spell Struct

Level 7
Joined
Oct 11, 2008
Messages
304
+1 vote to remove this after the booleans :)
JASS:
    private method onFinish takes nothing returns nothing
    endmethod

/* VS */

    private method onEndCast takes nothing returns nothing
        if this.channeling/this.casting then
        endif
    endmethod
 
???

you want onFinish removed?


Also Mag, there was a reason Position was used ;p

edit
Oh yes, you can now see if a spell was stopped because it was finished, canceled, unit died, unit reincarnated, or unit was removed

IsUnitDeindexing means unit was removed
IsUnitDead means that unit died
IsUnitReincarnating means that unit reincarnated

If none of those are true, then the unit canceled

edit
What I can actually do is change the behavior of onFinish and onEndCast if you like? I can change onEndCast to onCancel (from removal, death, reincarnation, or actual cancel) and onFinish to actual spell finish : ). I think that I'll do that.
 
Updated behavior and added uses UnitEvent.


The behavior is now good. The only methods that won't be generated if they aren't declared by the user are onChannel and onEffect. The reason the others are generated is so that the state of the spell can be tracked. Furthermore, onFinish and onEndCast always have to be in there to get an accurate end cast reason and to perform destruction. The user can choose not to declare onFinish and onEndCast, but the behind the scenes methods will still be generated, they just won't call onFinish/onEndCast ;P.


I think that this thing is now perfect ^_^


edit
If you noticed, the spell uses the unit index as the instance of itself. One unit can only have 1 spell up at a time and a spell will only last during the casting period. The instant the casting is done, so is the spell. Well, how would you do damage over time effects then? They go beyond the casting period... how would you do anything with this?


This forces you to follow a different methodology when crafting spells. The spell is now the deployer of effects. The spell creates the effects, like a damage over time effects, and those effects run independent of the spell. When using this, you should have 1 struct for each type of effect. The effects are not related to each other at all and are connected in absolutely no way. 1 instance per effect, not connected to the spell.


This will make spell design easier to read and write (99% of currently written spells, possibly 100%, are total messes). If you think that the spell deals damage or has a timer on it, you are doing something wrong. Now, you can make instant spells deal damage, that will actually work, but if a spell does something like a projectile and you try to make it do damage, it is going to fail. The projectile is an effect that was generated by the spell.


Anyways, hopefully this spell struct will fix up how spells are made ^)^.


Also, don't forget to lock the target for each effect and unlock it when the effect is done!
 
Honestly for spells you could be running every spell from one sequence of arrays. Is there going to be a chance that 8190 spells are being cast at the same time? Absolutely rubbish, could you imagine the lag?

It is smarter to consolidate everything into one mainstream source of generic data, stuff you know will always need to exist, such as what you have in the module. However, 1) your module could be shortened and 2) the most glaring thing for me is that you don't check the targets in the order of the most common things, for example you'd want to check if the target is a unit, before checking if it's an item, and then finally checking if it's a destructable.

Why using Condition() instead of Filter()?

If it's not a widget, for you it's a location. What about stand-and-channel spells? It will return null for the location. Maybe you should be using position of caster in that case?
 
Hmm, I can change the order in if statement and fix location.

your module could be shortened

How so? All of those events actually run at different times >.>.

It is smarter to consolidate everything into one mainstream source of generic data, stuff you know will always need to exist, such as what you have in the module.

I disagree =). I think it's smarter to make the spell deploy effects like I did above =). By mashing various effects together with the spell, it creates a massive mess. Just look at all of the currently written spells >.>.
 
Well, the thing I've been really working at bettering is the removal of the fields generated in the module... I can't really go to method operators as that'd end up spamming triggers and in debug mode, trigger evaluations ;|.


I can't use a delegate as that'd end up doing 2 array reads >.>.


It really looks like, because of the suckiness of jh, I'm stuck spamming globals >.<.

edit
moved to operators >.>. Lack of private module operators on the part of jh just made the module code unreadable.
 
You could make SpellStruct implement modules SpellStructTop and SpellStructBase, so newbies can implement SpellStruct and pros can use the top/bottom approach to avoid the triggers.

I am not completely sold, mostly you'd use this for channeling spells is that right? Most of the spells are pretty much instant-cast. Is this the complete package or is there more to expect from it?
 
I am not completely sold, mostly you'd use this for channeling spells is that right? Most of the spells are pretty much instant-cast. Is this the complete package or is there more to expect from it?

Complete package. The alternative is j4l's, which does the same thing except with interfaces and extending structs and onDestroy. I wrote this because I hated the way j4l did it ; P.
 
Well this code contains the update:

JASS:
library SpellStruct /* v1.1.0.0
*************************************************************************************
*
*   An efficient and easy to use module for spell design.
*
*************************************************************************************
*
*   */uses/*
*   
*       */ Position /*              hiveworkshop.com/forums/submissions-414/snippet-position-184578/
*       */ SpellEffectEvent /*      hiveworkshop.com/forums/jass-functions-413/snippet-spelleffectevent-187193/
*       */ UnitEvent /*             hiveworkshop.com/forums/jass-functions-413/extension-unit-event-172365/
*
************************************************************************************
*
*   constant integer SPELL_FINISHED
*   constant integer SPELL_CANCELED
*   constant integer SPELL_CASTER_INVALID
*   constant integer SPELL_TARGET_INVALID
*
*   module SpellStruct
*
*       readonly unit caster                (always present)
*       readonly Position target            (always present)
*
*       readonly boolean casting            (always present)
*       readonly boolean channeling         (always present)
*       readonly boolean finished           (always present)
*       readonly integer endCastReason      (always present)
*
*       (Interface) - "this" refers to unit index of caster
*
*           private static constant integer ABILITY_ID                  (not optional)
*
*           private method onChannel takes nothing returns nothing      (optional)
*               runs when channeling beings
*           private method onCast takes nothing returns nothing         (optional)
*               runs when casting begins
*           private method onEffect takes nothing returns nothing       (optional)
*               runs when casting begins
*           private method onFinish takes nothing returns nothing       (optional)
*               runs when the casting is finished
*           private method onEndCast takes nothing returns nothing      (optional)
*               runs when the casting is finished or was canceled for some reason
*
************************************************************************************/
    //! textmacro SPELL_STRUCT_HANDLER takes SPELL1, SPELL2
        private function $SPELL2$ takes nothing returns boolean
            return TriggerEvaluate($SPELL1$.trigger[GetSpellAbilityId()])
        endfunction
    //! endtextmacro
    //! textmacro SPELL_STRUCT_ON_SPELL_METHOD takes SPELL
        private static method on$SPELL$P takes nothing returns boolean
            local thistype this=GetUnitUserData(GetTriggerUnit())
    //! endtextmacro
    //! textmacro SPELL_STRUCT_ON_SPELL_METHOD_2
            return false
        endmethod
    //! endtextmacro
    //! textmacro SPELL_STRUCT_ON_SPELL takes SPELL1, SPELL2
        if (not $SPELL1$.handle.has(ABILITY_ID)) then
            set $SPELL1$.trigger[ABILITY_ID]=CreateTrigger()
        endif
        call TriggerAddCondition($SPELL1$.trigger[ABILITY_ID],Condition(function thistype.on$SPELL2$P))
    //! endtextmacro
    //! textmacro SPELL_STRUCT_HANDLER_INIT takes SPELL1, SPELL2, EVENT
        set $SPELL1$ = Table.create()
        call RegisterPlayerUnitEvent($EVENT$,function $SPELL2$)
    //! endtextmacro
    globals
        private Table channel
        private Table cast
        private Table finish
        private Table endCast
        private location l = Location(0,0)
        constant integer SPELL_FINISHED=0
        constant integer SPELL_CANCELED=1
        constant integer SPELL_CASTER_INVALID=2
        constant integer SPELL_TARGET_INVALID=3
    endglobals
    
    //! runtextmacro SPELL_STRUCT_HANDLER("channel","Channel")
    //! runtextmacro SPELL_STRUCT_HANDLER("cast","Cast")
    //! runtextmacro SPELL_STRUCT_HANDLER("finish","Finish")
    //! runtextmacro SPELL_STRUCT_HANDLER("endCast","EndCast")
    
    private module In
        private static method onInit takes nothing returns nothing
            //! runtextmacro SPELL_STRUCT_HANDLER_INIT("channel","Channel","EVENT_PLAYER_UNIT_SPELL_CHANNEL")
            //! runtextmacro SPELL_STRUCT_HANDLER_INIT("cast","Cast","EVENT_PLAYER_UNIT_SPELL_CAST")
            //! runtextmacro SPELL_STRUCT_HANDLER_INIT("finish","Finish","EVENT_PLAYER_UNIT_SPELL_FINISH")
            //! runtextmacro SPELL_STRUCT_HANDLER_INIT("endCast","EndCast","EVENT_PLAYER_UNIT_SPELL_ENDCAST")
        endmethod
    endmodule
    private struct I extends array
        implement In
    endstruct
    
    private function GetSpellTarget takes unit caster returns Position
        local location z
        local Position target
        
        if (null!=GetSpellTargetUnit()) then
            set target=Position[GetSpellTargetUnit()]
        elseif (null!=GetSpellTargetItem()) then
            set target=Position[GetSpellTargetItem()]
        elseif (null!=GetSpellTargetDestructable()) then
            set target=Position[GetSpellTargetDestructable()]
        else
            set z=GetSpellTargetLoc()
            if (null!=z) then
                set target=Position[z]
                set z=null
            else
                call MoveLocation(l,GetWidgetX(caster),GetWidgetY(caster))
                set target=Position.create(GetWidgetX(caster),GetWidgetY(caster),GetLocationZ(l)+GetUnitFlyHeight(caster))
            endif
        endif
        
        return target
    endfunction
    
    private function GetFinishReason takes unit caster, UnitIndex casterId, Position target returns integer
        if (IsUnitDead(casterId) or IsUnitReincarnating(casterId) or null==GetUnitById(casterId)) then
            return SPELL_CASTER_INVALID
        elseif (not target.valid) then
            return SPELL_TARGET_INVALID
        endif
        return 0
    endfunction
    
    globals
        private Position array trg
        private unit array cst
        
        private boolean array cng
        private boolean array chn
        private boolean array fns
        
        private integer array ecr
    endglobals
    
    module SpellStructTop
        method operator target takes nothing returns Position
            return trg[this]
        endmethod
        method operator caster takes nothing returns unit
            return cst[this]
        endmethod
        method operator casting takes nothing returns boolean
            return cng[this]
        endmethod
        method operator channeling takes nothing returns boolean
            return chn[this]
        endmethod
        method operator finished takes nothing returns boolean
            return fns[this]
        endmethod
        method operator endCastReason takes nothing returns integer
            return ecr[this]
        endmethod
        
        private method allocate takes nothing returns nothing
            set cst[this]=GetUnitById(this)
            set trg[this] = GetSpellTarget(cst[this])
            call trg[this].lock()
            call UnitIndex(this).lock()
        endmethod
    endmodule
    
    module SpellStructBase
        //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD("Channel")
            set chn[this]=true
            call allocate()
            static if thistype.onChannel.exists then
                call onChannel()
            endif
        //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD_2()
        
        //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD("Cast")
            
            
            if (chn[this]) then
                set chn[this]=false
                set cng[this]=true
                
                static if thistype.onCast.exists then
                    call onCast()
                endif
            endif
        //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD_2()
        
        static if thistype.onEffect.exists then
            //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD("Effect")
                if (cng[this]) then
                    call onEffect()
                endif
            //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD_2()
        endif
        
        //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD("Finish")
            set ecr[this] = GetFinishReason(cst[this],this,trg[this])
            
            set cng[this] = false
            
            set fns[this]=0==ecr[this]
            
            static if thistype.onFinish.exists then
                if (fns[this]) then
                    call onFinish()
                else
                    set chn[this] = false
                endif
            else
                set chn[this] = false
            endif
        //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD_2()
        
        //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD("EndCast")
            if (not fns[this] and 0==ecr[this]) then
                set ecr[this]=SPELL_CANCELED
                set chn[this]=false
                set cng[this]=false
            else
                set fns[this]=false
            endif
            static if thistype.onEndCast.exists then
                call onEndCast()
            endif
            set cst[this]=null
            set ecr[this]=0
            call trg[this].unlock()
            call UnitIndex(this).unlock()
        //! runtextmacro SPELL_STRUCT_ON_SPELL_METHOD_2()
        
        private static method onInit takes nothing returns nothing
            //! runtextmacro SPELL_STRUCT_ON_SPELL("channel", "Channel")
            //! runtextmacro SPELL_STRUCT_ON_SPELL("cast", "Cast")
            //! runtextmacro SPELL_STRUCT_ON_SPELL("finish", "Finish")
            //! runtextmacro SPELL_STRUCT_ON_SPELL("endCast", "EndCast")
            
            static if thistype.onEffect.exists then
                call RegisterSpellEffectEvent(ABILITY_ID,function thistype.onEffectP)
            endif
        endmethod
    endmodule
    
    module SpellStruct
        implement SpellStructTop
        implement SpellStructBase
    endmodule
endlibrary
 
Top