System SpellStruct

Jesus4Lyf

Good Idea™
Reaction score
397
JASS:
private integer favouriteCallbackCount=0
private method myFavouriteCallback takes nothing returns nothing
    set this.favouriteCallbackCount = this.favouriteCallbackCount + 1
    call BJDebugMsg("Caster: " + GetUnitName(this.caster))
    if this.favouriteCallbackCount == 3 then
        call this.TU_stop(thistype.myFavouriteCallback)
        set this.autoDestroy = true
    endif
endmethod

private method onEffect takes nothing returns nothing
    set this.autoDestroy = false
    call this.TU_start(thistype.myFavouriteCallback, 1.0)
endmethod

-->
JASS:
private integer favouriteCallbackCount=0
private method myFavouriteCallback takes nothing returns nothing
    set this.favouriteCallbackCount = this.favouriteCallbackCount + 1
    call BJDebugMsg("Caster: " + GetUnitName(this.caster))
    if this.favouriteCallbackCount == 3 then
        call this.TimerStop(thistype.myFavouriteCallback)
    endif
endmethod

private method onEffect takes nothing returns nothing
    call this.timerStart(thistype.myFavouriteCallback, 1.0)
endmethod

Also, added .addLock and .removeLock. Timer calls will automatically lock the struct, meaning the struct cannot be destroyed while periodic functions are running on it. That is really nice. :)

Added .forGroup(g, method) (I forgot to do that :p).

Currently 620 lines, haven't updated the post until I can test my code however. :)
 

Jesus4Lyf

Good Idea™
Reaction score
397
>This is sort of like an ItemMenu but for spell abilities I kind of like it.
Yay!
I'll have to check that out.

I came up with another sensible addition. There is now a .createTrigger(method) method in the next version. It returns a trigger where the method will be called on the struct instance. This is cool for all kinds of reasons - it will not double free a trigger that uses sleep actions when destroyed because the sleep actions would not actually be in the callback of the real trigger. The method is fired through .execute. It also automatically handles data attachment and flushing, through a .destroyTrigger(method) method. :thup:

The idea is you call RegisterSomeEvent(this.createTrigger(method)), and you can destroy it later without having to store it by using call this.destroyTrigger(method). :)

Still haven't released the updates, hoping to test them later tonight.

Edit: Need to make it pause all timers and destroy all triggers if the struct is manually destroyed... hmm.

Edit: Done, released. :D
This is now awesome and do-everything-ish. :)
 

Romek

Super Moderator
Reaction score
963
>This is now awesome and do-everything-ish.
After your whole 'do everything systems suck' discussion with Nestharus, here you are making a system which tries to do everything.

> Does it try to pass those optional members as objects?
Perhaps there's a work-around for that. :p

To be honest, I don't like this. I can't pinpoint why, but it seems like something Nestharus would make. :(
 

Jesus4Lyf

Good Idea™
Reaction score
397
>but it seems like something Nestharus would make.
Yea I thought that. Except the interface is easy and intuitive... and for something that matters.

Now though, I'm not doing this with a "do everything" mindset actually. That was joking. I got sick of mapping in WC3 because everything to do with spells was inevitably a pain, and nothing was quick or easy. Spells were hundreds of lines of code for simple stuff, and mostly it was setting event responses in structs and crap and then attaching to timers and things. I'm over it. I can nearly guarantee that this resource will be at the core of all my maps now. Everything that was frustrating or tedious is reduced to one-liners.

Edit: Does it feel like I'm creating standard or framework and then trying to push and convert everyone into using it even though it has obvious downsides and is unnecessary? Or do I seem to be brushing aside all other systems and saying "this is all you need" like a quick fix...
I'd like to understand what discomforts you about it. But at the end of the day, like all my work, I made this because... I need it.

I'm sick of all the crap JASS makes us do.

Edit: Yay! 1.0.4, got methods to be optional. Thanks Romek for the tip-off about interfaces. I think I'm nearly ready to un-BETA this...
 

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
What I don't like is that it is a tad similar to a 'do-it-all' system, which is very unlike you :s
 

Kenny

Back for now.
Reaction score
202
>What I don't like is that it is a tad similar to a 'do-it-all' system, which is very unlike you :s

Personally, I don't see this as a 'do-it-all' system, but more of a 'do-everything-faster' system.

This makes spell develop so ridiculously easy. I made 4 pretty simplistic spells within about 5 minutes (They were all still more complicated than normal wc3 abilities).

Now that the event methods are optional, this is like 100 times better.

The thing that I still can't get over is the fact that after developing multiple systems that are all pre and post 1.23b compatible, you finally made something that isn't.

Also:
JASS:
private struct ChainAttach
    trigger trigger // <----
    timer timer // <----
    integer instance
    Method callback
    thistype next
    thistype prev
    endstruct


Ewww!! WHY?!? I find that really ugly, and I am a little surprised you did it. :p

>Currently 620 lines, haven't updated the post until I can test my code however.

Almost as long as my Meat Hook spell. :p Kinda sad when you think about how much functionality this provides compared to it.

Anywho, I think this is pretty awesome. You don't really understand how useful it is until you give it a try.
 

Nestharus

o-o
Reaction score
84
I think this is very well done for spells, but seems almost too cluttered, not as clean as it could be.

Maybe splitting it up into different API Layers? Different modules? Who knows ; ).

Er... it'd require a lot of thought on my part because you've already done such a great job with it, lol : D. I'm thinking of like generic spell objects -> more specific objects -> etc where people can override features or add in extra things and just have it all seem to mesh together. Then again, for me a deep hierarchy is pretty nifty as people can take parts and pieces of a spell and put them together to make a new spell (multi struct inheritance through delegates of dif layers or w/e and using the properties of the current struct)

that's actually how I'd do it, and as the represented data is handled completely on the user side, you wouldn't have to worry about accessing x and y coordinates, you could just let the user pass them in : ). I mean, you could worry about x and y etc and being able to pass in all of that crap, but then we're getting into a billion trigger evaluations and a horrible interface o-o.

It'd also allow people to more easily work on different layers and produce their own layered API without killing the interface, which may deal with more specific objects or w/e or who knows really : ).


So, how are those thoughts for making this uhm... more flexible?


Actually, looking at this wants to make me go back to Spawn and change around some of it with my current thoughts on the matter and your great innovations at forcing the user to handle their own data and algorithms and just plugging it into a Spell object so that the interface isn't horrible (simple enough), and so that the performance doesn't get raped, lol : D.


I'll really have to ponder more on this... but I really think all of this stuff could still somehow be linked together... the idea I think is to be able to do something that just allows people to work in different sections of a framework that are designed by a multitude of people. Each section would be more specific to certain things, and the further down you go into a given section, the more specific and typed it is, thus lowering overall development time ; o.


Hmmmmmmmmmmmmmmmmmmmmm....
 

Jesus4Lyf

Good Idea™
Reaction score
397
The thing that I still can't get over is the fact that after developing multiple systems that are all pre and post 1.23b compatible, you finally made something that isn't.
Yeah... I did.
I said when the new patch came out that I'd wait around 6 months before really using it. It's been about that. I'm pretty sure nearly everyone has upgraded to 1.24b, like the unofficial servers and such even... but really, hashtables are required for this. :D
Also:
JASS:
private struct ChainAttach
    trigger trigger // <----
    timer timer // <----
    integer instance
    Method callback
    thistype next
    thistype prev
    endstruct


Ewww!! WHY?!? I find that really ugly, and I am a little surprised you did it. :p
I couldn't think what else to call "timer". I originally had "trig", but "timer" didn't work as like... time or something. Any ideas? :p
Anywho, I think this is pretty awesome. You don't really understand how useful it is until you give it a try.
Yay, thanks for the feedback. :D

Edit: Nes, have you ever written a spell?
 

Nestharus

o-o
Reaction score
84
->Edit: Nes, have you ever written a spell?

Well, I've written passive spells if those count : ). The one I wrote required me to write Unit Decaying, rofl : P


Generally for spells, there's this whole UI design that i'm going to follow for every future spell I write, but I have to make the spell system first for it. It's actually rather innovative and I plan on using it for all of my games. Took me 1 year to think up the design : ). It makes spell casting actually require skill o-o.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Fair enough. Any feedback from anyone on the timer stuff I implemented? :)

Or the trigger stuff.
Edit: Just realised I should definitely allow multiple triggers per method per struct. <_<
Edit2: Just realised the above is probably untrue.

Edit: Example of timer/trigger use.
JASS:
struct BerserkersCall extends SpellStruct
    implement SpellStruct
    
    private static method DURATION takes integer level returns real
        return 1.0*level
    endmethod
    
    private static trigger reorder
    private static unit move
    
    private method onDuration takes nothing returns nothing
        call this.destroy() // actually has garbage collection
        // Alternatively:
        //call this.destroyTrigger(thistype.onOrder)
        //call this.stopTimer(thistype.onDuration) // actually will destroy the struct since it is no longer used
    endmethod
    private method afterZero takes nothing returns nothing
        call IssueTargetOrder(thistype.move,&quot;attack&quot;,this.caster)
        call this.stopTimer(thistype.afterZero)
    endmethod
    private method onOrder takes nothing returns nothing
        if GetIssuedOrderId()!=OrderId(&quot;attack&quot;) or GetOrderTarget()!=this.caster then
            set thistype.move=GetOrderedUnit()
            call this.startTimer(thistype.afterZero,0.0)
        endif
    endmethod
    private method forAoE takes unit u returns nothing
        if IsUnitEnemy(u,this.owner) and not IsUnitType(u,UNIT_TYPE_DEAD) then
            call IssueTargetOrder(u,&quot;attack&quot;,this.caster)
            call TriggerRegisterUnitEvent(thistype.reorder,u,EVENT_UNIT_ISSUED_ORDER)
            call TriggerRegisterUnitEvent(thistype.reorder,u,EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterUnitEvent(thistype.reorder,u,EVENT_UNIT_ISSUED_POINT_ORDER)
        endif
    endmethod
    private method onEffect takes nothing returns nothing
        set thistype.reorder=this.createTrigger(thistype.onOrder)
        call this.forUnitsInAoECaster(thistype.forAoE)
        call this.startTimer(thistype.onDuration,thistype.DURATION(this.level))
    endmethod
    
    private static method onInit takes nothing returns nothing
        set thistype.abil=&#039;xxxx&#039;
        set thistype.defaultAoE=300.0
    endmethod
endstruct

A classical annoying-to-implement spell whipped up in a few minutes.

Edit: Would it be better to have a true/false for periodicness in .startTimer? Else I could implement something like .waitExec(method, time)... I dunno. Seems people may forget to stop their timers. :p
 

Kenny

Back for now.
Reaction score
202
That Berserker's Call example is awesome. A once annoying spell reduced to about 40 lines.

>Edit: Would it be better to have a true/false for periodicness in .startTimer? Else I could implement something like .waitExec(method, time)... I dunno. Seems people may forget to stop their timers. :p

I don't think the true/false argument is necessary. If people are forgetting something like that, they probably shouldn't be using this.

What else are you expecting to add to this before it moves out of beta?
 

Jesus4Lyf

Good Idea™
Reaction score
397
>What else are you expecting to add to this before it moves out of beta?
More a case of being happy with the interface. I don't have to be backwards compatible in BETA, usually when I release something, that's it. (I have been fully backwards compatible so far, and try to be.)

Main issues are should timer have periodic boolean, and should you be able to make two triggers for one method for one struct... at the moment you can only have one trigger per method per spell instance. :) (Same with timers, but I don't find that to be perhaps troublesome really.)

Edit: I'm updating the documentation, finally.
So far:
JASS:
//
//
//      Spell Struct
//          By Jesus4Lyf.
//       Version 1.0.4 BETA.
//
//      What is SpellStruct?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          SpellStruct is a system designed for the sake of rapid spell development.
//          It grants features which can be entirely encapsulated in a struct type per
//          ability. It handles event response creation, timer attachment, trigger
//          attachment, area of effect (AoE) enumeration, unit attachment, and all
//          spells made using it should be automatically MUI/leakless and rather efficient.
//          
//      How to implement?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Simply create a new trigger object called SpellStruct, go to &#039;Edit -&gt; Convert
//          to Custom Text&#039;, and replace everything that&#039;s there with this script.
//
//    _______________________
//    ||                   ||
//    || SpellStruct Usage ||
//    ||                   ||
//    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//      Writing a Simple SpellStruct:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          - To use SpellStruct, write SpellStructs. These are structs which extend
//            SpellStruct, and implement SpellStruct.
//
//          - Everything is optional to implement/use, except setting thistype.abil=&#039;Axxx&#039;
//            in static method onInit.
//
//          - Example:
//
/*          struct MySpell extends SpellStruct
                implement SpellStruct
                
                private static method onInit takes nothing returns nothing
                    set thistype.abil=&#039;A000&#039;
                endmethod
            endstruct
*/
//      Event Responses:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          - The following is a list of event responses that come with SpellStructs.
//
//          - Event responses are stored on your struct as members.
/*
            // Members of your SpellStruct:
            unit caster             // The casting unit.
            integer level           // The level of the ability on the casting unit.
            integer order           // The order id of the caster when it began casting the spell.
            unit targetUnit         // The target unit of the ability being cast (null if none).
            item targetItem         // The target item of the ability being cast (null if none).
            destructable targetDest // The target destructable of the ability being cast (null if none).
            widget targetWidget     // The target unit, item or destructable (see above).
            real targetX            // The point targetted when channeling began (X/Y coordinates).
            real targetY            // If spell is targetted, this is the original position of the target.
            player owner            // The owner of the casting unit, when channeling began.
            integer abilId          // The ability id of the ability being cast.
            
            // Extra methods:
            real casterX                        // The caster&#039;s X coordinate (inlines to GetUnitX(this.caster))
            real casterY                        // The caster&#039;s Y coordinate (inlines to GetUnitY(this.caster))
            real getDistanceToTargetWidget()    // The distance between the caster and the target widget.
            real getDistanceToTargetPoint()     // The distance between the caster and the original point targetted.
*/
//          - The above event response list may be used anywhere in your SpellStruct,
//            and at any time.
//
//          - You may implement methods which are called when the normal Warcraft III
//            spell events would fire. Some of the Warcraft III event responses are
//            broken for certain events, sometimes intermittently, but these are fixed
//            when using SpellStruct.
//            Also, usually in Warcraft III, these are implemented in a way that cycles
//            through all triggered abilities to see if the spell cast is the spell the
//            trigger is for. In SpellStruct, this is changed so that Warcraft III will
//            jump straight to the method for the spell that was cast.
//
//          - These methods, which are called when events fire, are non-static. This means
//            any members you add in your SpellStruct can be accessed from within the method.
//            This is achieved with unit attachment (internally).
//
//          - Example:
//
/*          struct MySpell extends SpellStruct
                implement SpellStruct
                
                // Reserved method names for event methods, in order of firing:
                method onCreate takes nothing returns nothing // fires just before channel
                    // If this is an AoE spell...
                    set this.aoe=this.level*200
                    
                    // Maybe you want to control the lifetime of the struct yourself:
                    set this.autoDestroy=false // there is a thistype.autoDestroyDefault too,
                                               // which is true by default.
                                               // So you can conditionally control the lifetime.
                                               // Do not destroy structs before the spell ends.
                endmethod
                method onChannel takes nothing returns nothing // Unit starts channeling a spell.
                endmethod
                method onStartCast takes nothing returns nothing // Fires much as the same as channel.
                endmethod
                method onEffect takes nothing returns nothing // When the spell has successfully cast, mana deducted.
                endmethod                                     // Will only fire if channeling successfully completed.
                method onFinish takes nothing returns nothing // When the effect finishes (or is interrupted).
                endmethod                                     // This will only fire if the effect fired.
                method onStopCast takes nothing returns nothing // When the spell stops being cast, or is cancelled.
                endmethod
                
                private static method onInit takes nothing returns nothing
                    set thistype.abil=&#039;Axxx&#039;
                    set thistype.autoDestroyDefault=true // set to true by default.
                                                         // may be overridden for each
                                                         // instance by setting
                                                         // this.autoDestroy=true/false
                    // AutoDestroyed structs are destroyed after the spell stops casting.
                    
                    set thistype.defaultAoE=200.0 // optional.
                endmethod
            endstruct
*/
//      Disabling Auto-Destruction:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          - SpellStructs are created just before the .onChannel event method is
//            called.
//
//          - By default, SpellStructs are automatically destroyed, and this occurs
//            just after the method .onStopCast is called.
//
//          - You may extend this lifetime in two ways. If you wish to manually
//            manage the lifetime of a SpellStruct, you may do either of the following:
/*          struct MySpell extends SpellStruct
                implement SpellStruct
                
                method onStopCast takes nothing returns nothing
                    call this.destroy() // can call this at any time.
                endmethod               // no more event methods fire after destroying a SpellStruct.
                
                method onCreate takes nothing returns nothing
                    set this.autoDestroy=false // Either this
                endmethod
                private static method onInit takes nothing returns nothing
                    set thistype.abil=&#039;Axxx&#039;
                    set thistype.autoDestroyDefault=false // or this.
                endmethod
            endstruct
*/
//          - Setting thistype.autoDestroyDefault to false causes .autoDestroy to
//            be set to false just before onCreate is called, for all new instances.
//
//          - Setting this.autoDestroy to false stops just the current instance
//            from auto destruction. Setting it back to true will cause the struct
//            to be destroyed if it usually would have been by then, else continue
//            to monitor it for auto destruction.
//
//      Locking:
//     ¯¯¯¯¯¯¯¯¯¯
//          - You may also add locks to an instance. This is done using this.addLock(),
//            and this.removeLock(). Calling this.addLock increments a counter, and
//            calling this.removeLock() decrements it. While it is greater than 0,
//            a SpellStruct will not be autodestroyed. Decrementing it back to 0
//            after the .onStopCast method has fired while .autoDestroy is true will
//            destroy the struct automatically. This is a garbage collection mechanism.
//
//          - You may check if a struct has no locks on it by using .isNotLocked.
//
//          - Example:
//
/*          struct MySpell extends SpellStruct
                implement SpellStruct
                
                private integer ticks=57
                
                // T32x example.
                private method periodic takes nothing returns nothing
                    set this.ticks=this.ticks-1
                    if this.ticks==0 then
                        call BJDebugMsg(&quot;Fired 57 times&quot;)
                        call this.stopPeriodic()
                        call this.removeLock() // Remove lock called here.
                    endif                      // will only destroy the struct if casting finished,
                endmethod                      // so that event methods will continue to fire and
                implement T32x                 // all data is available.
                
                method onEffect takes nothing returns nothing
                    call this.startPeriodic()
                    call this.addLock() // We want the struct to exist until .stopPeriodic is called.
                endmethod               // Locks are useful because we could attach this instance to any
                                        // number of things, which can each unlock it when they are done with it.
                private static method onInit takes nothing returns nothing
                    set thistype.abil=&#039;Axxx&#039;
                endmethod
            endstruct
*/
//      Timers:
//     ¯¯¯¯¯¯¯¯¯
//          - If you have TimerUtils in your map, SpellStruct will operate using
//            TimerUtils data attachment for timers.
//          - If you don&#039;t have TimerUtils, but have Recycle, SpellStruct will
//            attach to Recycled timers using GetHandleId and a hashtable.
//          - If you have neither, SpellStruct will create timers dynamically,
//            and pause and destroy them when they are done with. Attachment
//            will be done with GetHandleId and a hashtable.
//
//          - .startTimer(method, period) will start a timer for the given method,
//            for the current spell instance. This means all spell event responses
//            will be available from within the callback. This timer will keep
//            firing until you stop it using .stopTimer(method).
//
//          - Starting a timer in this way automatically adds a lock to the struct,
//            and stopping a timer removes a lock. This is to guarantee that when
//            a timer method fires, all data is available and valid (while a struct
//            has locks, it will not be auto destroyed).
//
//          - Manually calling .destroy on a spell struct will stop all timers for
//            that struct automatically.
//
//          - Because it attaches both the method to call and the struct instance
//            to the timer, and then fires the method with .execute(), it is
//            recommended that you use this only for timers that are reasonably
//            infrequent. Using T32x with SpellStruct is recommended for high
//            frequency (low period) timers.
//
//          - Example:
//
/*          struct MySpell extends SpellStruct
                implement SpellStruct
                
                private method whenTimerExpires takes nothing returns nothing
                    call KillUnit(this.targetUnit) // Kill the target of the spell.
                    call this.stopTimer(thistype.whenTimerExpires)
                endmethod
                method onEffect takes nothing returns nothing
                    call this.startTimer(thistype.whenTimerExpires,5.0) // Run .whenTimerExpires in 5.0 seconds.
                endmethod
                
                private static method onInit takes nothing returns nothing
                    set thistype.abil=&#039;Axxx&#039;
                endmethod
            endstruct
*/
//      Triggers:
//     ¯¯¯¯¯¯¯¯¯¯¯
//          - Trigger attaching works using GetHandleId and a hashtable.
//
//          - .createTrigger(method) will create a trigger with the method as it&#039;s
//            action. Do not use DestroyTrigger to remove this trigger, you must
//            use .destroyTrigger(method) to destroy it instead. This saves having
//            to store the trigger in any sort of variable, generally. Destroying
//            a trigger using SpellStruct has protection against the double free
//            bug in Warcraft III, even if you use TriggerSleepAction.
//
//          - Creating a trigger in this way automatically adds a lock to the struct,
//            and destroying it removes this lock. This is to guarantee that when
//            a timer method fires, all data is available and valid (while a struct
//            has locks, it will not be auto destroyed).
//
//          - Manually calling .destroy on a spell struct will destroy all triggers
//            for that struct automatically.
//
//          - Example:
//
/*          struct MySpell extends SpellStruct
                implement SpellStruct
                
                // Gets a random real between -300.0 and 300.0.
                private static constant real targetOffset=300.0
                private static constant method getOffset takes nothing returns real // inlines
                    return GetRandomReal(-thistype.targetOffset, thistype.targetOffset)
                endmethod
                
                // For skipping an order, when reissuing it.
                private boolean skipOrder = false
                
                private method onTargetPointOrder takes nothing returns nothing
                    if this.skipOrder then
                        set this.skipOrder=false
                    else
                        set this.skipOrder=true
                        call IssuePointOrderById(this.targetUnit, GetIssuedOrderId(), GetOrderPointX() + thistype.getOffset(), GetOrderPointY() + thistype.getOffset())
                    endif
                endmethod
                private method onEffect takes nothing returns nothing
                    // Blurs the aim of the target for 5.0 seconds.
                    call TriggerRegisterUnitEvent(this.createTrigger(thistype.onTargetPointOrder), this.targetUnit, EVENT_UNIT_ISSUED_POINT_ORDER)
                    call TriggerSleepAction(5.0) // a lock from the trigger will stop the struct from being destroyed.
                    call this.destroyTrigger(thistype.onTargetPointOrder)
                endmethod
                
                private static method onInit takes nothing returns nothing
                    set thistype.abil=&#039;Axxx&#039;
                endmethod
            endstruct
*/
//      AoE (Area of Effect) enumeration:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          - You may set the AoE of a spell instance using set this.aoe = x, or you
//            may set the default value for the aoe of a spell using set
//            thistype.defaultAoE = x. This will set the value of .aoe to the value
//            specified before onCreate is called.
//
//          - You may enumerate the units in AoE of the target point, the current
//            position of the targetted object, or the caster. This requires a unit
//            filter to be applied.
//
//          - This enum does not clear the group before hand, like native enums.
//            Actually, it should even be safe to use with dynamic groups.
//
/*          struct MySpell extends SpellStruct
                implement SpellStruct
                
                private method myFilter takes unit u returns boolean
                    return not IsUnitType(u, UNIT_TYPE_DEAD)
                endmethod
                
                private method onEffect takes nothing returns nothing
                    local group g = CreateGroup()
                    call this.enumUnitsInAoE(g, thistype.myFilter) // Like a default WC3 ability, enum in range of the point targetted when channeling began.
                    call this.enumUnitsInAoETarget(g, thistype.myFilter) // Enums all units current within aoe of the targetted widget (unit/item/destructable).
                    call this.enumUnitsInAoECaster(g, thistype.myFilter) // Enums all units current within aoe of the caster.
                    //...
                endmethod
                
                private static method onInit takes nothing returns nothing
                    set thistype.abil=&#039;Axxx&#039;
                    set thistype.defaultAoE=500.0
                endmethod
            endstruct
*/
//          - You may also skip groups altogether and do actions for all units within
//            aoe of the target point/target/caster.
//
/*          struct MySpell extends SpellStruct
                implement SpellStruct
                
                private method myAction takes unit u returns nothing
                    if not IsUnitType(u, UNIT_TYPE_DEAD) then
                        call SetUnitX(u, this.casterX)
                        call SetUnitY(u, this.casterY)
                    endif
                endmethod
                
                private method onEffect takes nothing returns nothing
                    local group g = CreateGroup()
                    set this.aoe = this.level * 100.0 + 100.0
                    call this.forUnitsInAoE(thistype.myAction) // Like a default WC3 ability, units in range of the point targetted when channeling began.
                    call this.forUnitsInAoETarget(thistype.myAction) // For all units current within aoe of the targetted widget (unit/item/destructable).
                    call this.forUnitsInAoECaster(thistype.myAction) // For all units current within aoe of the caster.
                    //...
                endmethod
                
                private static method onInit takes nothing returns nothing
                    set thistype.abil=&#039;Axxx&#039;
                endmethod
            endstruct
*/
//          - You may also check, for a single unit, to see if it is within the AoE
//            of the target point, target or caster, using the following:
//            - this.isUnitInAoE(myUnit) // within aoe of target point.
//            - this.isUnitInAoETarget(myUnit) // within aoe of target.
//            - this.isUnitInAoECaster(myUnit) // within aoe of caster.
//          - The above return booleans.
//
//          - All AoE functionality takes into account the collision size of the
//            enumerated units. This matches better with Warcraft III AoE detection,
//            which highlights units the spell will hit in green for AoE abilities.
//
//      Miscellaneous:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          - call this.forGroup(g, callback method takes unit)
//            Example:
//
/*          method myCallback takes unit u returns nothing
                call SetWidgetLife(u, GetWidgetLife(this.caster))
            endmethod
            // in some method:
                call this.forGroup(g, thistype.myCallback) // set life for all units in group to the caster&#039;s life.
*/
//
//      Thanks:
//     ¯¯¯¯¯¯¯¯¯
//          - Romek, for helping me with the interface hint to make the methods
//            optional.
//
Edit2: Updated it again. It should be complete now.
I hope to test that examples compile before releasing a new version. :)

Edit: Hm. I want to remove:
JASS:
real getDistanceToTargetWidget()    // The distance between the caster and the target widget.
real getDistanceToTargetPoint()     // The distance between the caster and the original point targetted.

Any objections?
 

Kenny

Back for now.
Reaction score
202
The documentation is quite nice. It explains everything in good detail.

>Any objections?

I am a little surprised that I think this, but I kinda object. By kinda, I do think they should be removed, but I would find it pretty handy to have methods like that around. Stuff like distances and angles would be helpful, as it can make it a lot easier for those who get confused with how to do angles and stuff properly.

Maybe an optional module? :p Haha.
 

Viikuna

No Marlo no game.
Reaction score
265
The thing that mostly bugs me with this is that theres too much random stuff in there. SpellStruct is a nice idea, but it should probably contain only stuff that is common to all spells, but not needed elsewhere.

Stuff like event responses, those group helper methods, trigger and timer thingies, math stuff, all those are stuff that is needed in many systems. I just see no reason to rewrite that stuff for spells only, since you already probably have that stuff for your main systems too. These things should really go to their own libraries, at least IMO.
 

Romek

Super Moderator
Reaction score
963
> Any objections?
Yes. :p
They're useful functions used in many spells with unit or point targets.
 

Azlier

Old World Ghost
Reaction score
461
Yes, I would like to file a complaint.

One of your comments in the Life Drain spell example is incorrect (I'm so nitpicky).

JASS:
private method periodic takes nothing returns nothing // should inline, lol  &lt;- NONSENSE
    //if not this.isUnitInAoECaster(this.targetUnit) then
        call thistype.setDistanceToUnit(this.targetUnit,this.caster,this.aoe)
    //endif
endmethod


>Any objections?

None, your honor.
 

Narks

Vastly intelligent whale-like being from the stars
Reaction score
90
Could we have a way to assign multiple abilityId's to one SpellStruct? That would be awesome for my map.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top