Would anyone find this useful?

Kenny

Back for now.
Reaction score
202
I was sick and tired of writing the same things over and over again for spells that need to store a casting unit, its coordinates, a target unit, its coordinates, the distance between them, the angle between them and all that kind of stuff, so I wrote this:

JASS:
library EventResponses

    globals
        // Different target types for boolean checks.
        private constant integer TARGET_TYPE_NONE = 0
        private constant integer TARGET_TYPE_UNIT = 1
        private constant integer TARGET_TYPE_DEST = 2
        private constant integer TARGET_TYPE_ITEM = 3
        private constant integer TARGET_TYPE_LAND = 4
    endglobals
    
    // My attempt at not using GetHandleId().
    private struct EventId
    
        readonly boolean  isSpell = false // If it is a spell or order event.
        private  eventid  eventId = null  // The event itself.
        private  thistype next    = 0     // Next event in the list.
        
        private static timer tempTimer = null // Timer needed for initial registration.
        private static eventid array eventIds // Array to store the used events.
    
        // I kind of liked this interface.
        static method operator[] takes eventid id returns thistype
            local thistype this = thistype(0).next
            
            loop
                exitwhen this == 0
                if id == this.eventId then
                    return this
                endif
                set this = this.next
            endloop
            
            return 0
        endmethod
        
        // Set up each event.
        private static method setEvent takes eventid id, integer i returns nothing
            local thistype this = thistype.allocate()
            
            set this.eventId = id // Set the eventid.
            
            if i <= 9 then
                set this.isSpell = true // The first 9 events are spell events.
            endif
            
            set this.next=thistype(0).next
            set thistype(0).next=this
        endmethod
        
        // Register the events at 0.00 seconds of game time (It wouldn't work otherwise).
        private static method registerEvents takes nothing returns nothing
            local integer i = 0
            
            loop
                exitwhen i == 17
                call thistype.setEvent(thistype.eventIds<i>,i)
                set i = i + 1
            endloop
            
            // Clean up the temporary timer.
            call PauseTimer(thistype.tempTimer)
            call DestroyTimer(thistype.tempTimer)
            set thistype.tempTimer = null
        endmethod
        
        // Initialise all the events used.
        private static method onInit takes nothing returns nothing
            set thistype.eventIds[0]  = EVENT_PLAYER_UNIT_SPELL_CHANNEL
            set thistype.eventIds[1]  = EVENT_PLAYER_UNIT_SPELL_CAST
            set thistype.eventIds[2]  = EVENT_PLAYER_UNIT_SPELL_EFFECT
            set thistype.eventIds[3]  = EVENT_PLAYER_UNIT_SPELL_FINISH
            set thistype.eventIds[4]  = EVENT_PLAYER_UNIT_SPELL_ENDCAST
            set thistype.eventIds[5]  = EVENT_UNIT_SPELL_CHANNEL
            set thistype.eventIds[6]  = EVENT_UNIT_SPELL_CAST
            set thistype.eventIds[7]  = EVENT_UNIT_SPELL_EFFECT
            set thistype.eventIds[8]  = EVENT_UNIT_SPELL_FINISH
            set thistype.eventIds[9]  = EVENT_UNIT_SPELL_ENDCAST
            
            set thistype.eventIds[10] = EVENT_PLAYER_UNIT_ISSUED_ORDER
            set thistype.eventIds[11] = EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER
            set thistype.eventIds[12] = EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
            set thistype.eventIds[13] = EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER
            set thistype.eventIds[14] = EVENT_UNIT_ISSUED_ORDER
            set thistype.eventIds[15] = EVENT_UNIT_ISSUED_POINT_ORDER
            set thistype.eventIds[16] = EVENT_UNIT_ISSUED_TARGET_ORDER
            
            // Start the timer.
            set thistype.tempTimer = CreateTimer()
            call TimerStart(thistype.tempTimer,0.00,false,function thistype.registerEvents)
        endmethod
        
    endstruct

    // The exported EventResp struct.
    struct EventResp
    
        // All the accessible variables.
        readonly unit         casterUnit = null
        readonly widget       targetWidg = null
        readonly unit         targetUnit = null
        readonly destructable targetDest = null
        readonly item         targetItem = null
        readonly real         casterX    = 0.00
        readonly real         casterY    = 0.00
        readonly real         casterZ    = 0.00
        readonly real         targetX    = 0.00
        readonly real         targetY    = 0.00
        readonly real         targetZ    = 0.00
        readonly real         distXY     = 0.00
        readonly real         distXYZ    = 0.00
        readonly real         angle      = 0.00
        readonly real         pitch      = 0.00
        private  integer      targetType = 0
        
        private static location tempLoc = null
        
        // Boolean checks.
        method operator isTargetUnit takes nothing returns boolean
            return this.targetType == TARGET_TYPE_UNIT
        endmethod
        
        method operator isTargetDest takes nothing returns boolean
            return this.targetType == TARGET_TYPE_DEST
        endmethod
        
        method operator isTargetItem takes nothing returns boolean
            return this.targetType == TARGET_TYPE_ITEM
        endmethod
        
        method operator isTargetLand takes nothing returns boolean
            return this.targetType == TARGET_TYPE_LAND
        endmethod
        
        method operator isTargetNone takes nothing returns boolean
            return this.targetType == TARGET_TYPE_NONE
        endmethod
        
        implement optional EventRespOptions
        
        // Clean up.
        method destroy takes nothing returns nothing
            set this.casterUnit = null
            set this.targetWidg = null
            set this.targetUnit = null
            set this.targetDest = null
            set this.targetItem = null
            
            call this.deallocate()
        endmethod
        
        // Update the members if needed.
        method update takes boolean updateCaster returns nothing
            local real x = 0.00
            local real y = 0.00
            local real z = 0.00
            
            // If you want to update the initial casting position, you can.
            if updateCaster then
                set this.casterX = GetUnitX(this.casterUnit)
                set this.casterY = GetUnitY(this.casterUnit)
                call MoveLocation(thistype.tempLoc,this.casterX,this.casterY)
                set this.casterZ = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.casterUnit)
            endif
            
            // Only units can move, so we only update the coordinates for them.
            if this.isTargetUnit then
                set this.targetX = GetUnitX(this.targetUnit)
                set this.targetY = GetUnitY(this.targetUnit)
                call MoveLocation(thistype.tempLoc,this.targetX,this.targetY)
                set this.targetZ = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.targetUnit)
            
                set x = this.targetX - this.casterX
                set y = this.targetY - this.casterY
                set z = this.targetZ - this.casterZ
                
                set this.distXY  = SquareRoot(x * x + y * y)
                set this.distXYZ = SquareRoot(x * x + y * y + z * z)
                set this.angle   = Atan2(y,x)
                set this.pitch   = Atan2(SquareRoot(x * x + y * y),z)
            endif
        endmethod   
        
        // Scabbed off Jesus4Lyf, hope you don&#039;t mind. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite7" alt=":p" title="Stick Out Tongue    :p" loading="lazy" data-shortname=":p" />
        //! textmacro SetTargetMembers takes TYPE, LAND
            set this.targetUnit = Get$TYPE$TargetUnit()
            if this.targetUnit == null then
                set this.targetDest = Get$TYPE$TargetDestructable()
                if this.targetDest == null then
                    set this.targetItem = Get$TYPE$TargetItem()
                    if this.targetItem == null then
                        set this.targetWidg = null
                        set this.targetX    = Get$LAND$X()
                        set this.targetY    = Get$LAND$Y()
                        call MoveLocation(thistype.tempLoc,this.targetX,this.targetY)
                        set this.targetZ    = GetLocationZ(thistype.tempLoc)
                        if this.targetX != 0.00 or this.targetY != 0.00 or this.targetZ != 0.00 then
                            set this.targetType = TARGET_TYPE_LAND
                        else
                            set this.targetType = TARGET_TYPE_NONE
                            set this.targetX    = this.casterX
                            set this.targetY    = this.casterY
                            set this.targetZ    = this.casterZ
                        endif
                    else
                        set this.targetWidg = this.targetItem
                        set this.targetType = TARGET_TYPE_ITEM
                        set this.targetX    = GetItemX(this.targetItem)
                        set this.targetY    = GetItemY(this.targetItem)
                        call MoveLocation(thistype.tempLoc,this.targetX,this.targetY)
                        set this.targetZ    = GetLocationZ(thistype.tempLoc)
                    endif
                else
                    set this.targetWidg = this.targetDest
                    set this.targetItem = null
                    set this.targetType = TARGET_TYPE_DEST
                    set this.targetX    = GetDestructableX(this.targetDest)
                    set this.targetY    = GetDestructableY(this.targetDest)
                    call MoveLocation(thistype.tempLoc,this.targetX,this.targetY)
                    set this.targetZ    = GetLocationZ(thistype.tempLoc)
                endif
            else
                set this.targetWidg = this.targetUnit
                set this.targetDest = null
                set this.targetItem = null
                set this.targetType = TARGET_TYPE_UNIT
                set this.targetX    = GetUnitX(this.targetUnit)
                set this.targetY    = GetUnitY(this.targetUnit)
                call MoveLocation(thistype.tempLoc,this.targetX,this.targetY)
                set this.targetZ    = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.targetUnit)
            endif
        //! endtextmacro
            
        // Create the event responses.
        static method create takes nothing returns thistype
            local EventId  id   = EventId[GetTriggerEventId()]
            local thistype this = 0
            local real     x    = 0.00
            local real     y    = 0.00
            local real     z    = 0.00
            
            // If the event responses were created for an unsupported event, show an error.
            if id == 0 then
                debug call BJDebugMsg(&quot;|cFFFF0000Error using EventResponses:|r Invalid event type.&quot;)
                return 0
            endif           
            
            // Assign all the members.
            set this            = thistype.allocate()
            set this.casterUnit = GetTriggerUnit()
            set this.casterX    = GetUnitX(this.casterUnit)
            set this.casterY    = GetUnitY(this.casterUnit)
            call MoveLocation(thistype.tempLoc,this.casterX,this.casterY)
            set this.casterZ    = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.casterUnit)
            
            if id.isSpell then
                //! runtextmacro SetTargetMembers(&quot;Spell&quot;,&quot;SpellTarget&quot;)
            else
                //! runtextmacro SetTargetMembers(&quot;Order&quot;,&quot;OrderPoint&quot;)
            endif
            
            set x = this.targetX - this.casterX
            set y = this.targetY - this.casterY
            set z = this.targetZ - this.casterZ
            
            set this.distXY  = SquareRoot(x * x + y * y)
            set this.distXYZ = SquareRoot(x * x + y * y + z * z)
            set this.angle   = Atan2(y,x)
            set this.pitch   = Atan2(SquareRoot(x * x + y * y),z)
            
            return this
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype.tempLoc = Location(0.00,0.00)
        endmethod
        
    endstruct
        
endlibrary
</i>


I actually wrote this a while ago, but I re did it today.

Example:
JASS:
scope Example initializer Init

    globals
        private constant integer ABIL_ID = &#039;A000&#039;
    endglobals

    private function Actions takes nothing returns nothing
        local EventResp er = EventResp.create()

        if er.isTargetUnit then
            call UnitDamageTarget(er.casterUnit,er.targetUnit,200.00,false,false,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL,null)
            call DestroyEffect(AddSpecialEffect(&quot;SomeEffect.mdl&quot;,er.targetX,er.targetY))

            call SetWidgetLife(er.casterUnit,GetWidgetLife(er.casterUnit) + 200.00)
            call DestroyEffect(AddSpecialEffect(&quot;SomeOtherEffect.mdl&quot;,er.casterX,er.casterY))
        elseif er.isTargetLand then
            call SetUnitX(er.casterUnit,er.targetX)
            call SetUnitY(er.casterUnit,er.targetY)
            call DestroyEffect(AddSpecialEffect(&quot;SomeOtherOtherEffect.mdl&quot;,er.targetX,er.targetY))
        endif

        // Do more stuff here.

        call er.destroy()
    endfunction

    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ABIL_ID then
            call Actions()
        endif
    endfunction

    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()

        call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(trig,Condition(function Conditions)
    endfunction

endscope


Limits:

- Only works for a set number of events (the most common events used). This limitation was set in place because other events have different native responses, while spell and order events are quite similar.

So yeah, tell me what you think and why.
 

Kenny

Back for now.
Reaction score
202
Thanks for your... input? :p

To be honest, the only reason I will ever go to wc3c now is to update jasshelper. The attitude of certain individuals there reassured me that I shouldn't be wasting my time there. But if I can remember correctly, I never saw something like this.

Anywho, more comments please. :)

(This is still in the early stages of development, so there is bound to be improvements)
 

Narks

Vastly intelligent whale-like being from the stars
Reaction score
90
It looks kind of interesting. Would speed things up a lot when creating a lot of stuff.
 

Kenny

Back for now.
Reaction score
202
That is basically the whole point of this. I was reminded of it by Jesus4Lyf's SpellStruct.

His is built for rapid map and spell development, and gives heaps of functionality with a nice interface (basically better than this in all ways). But it has the downside of needing to extend structs and implement modules.

This is a lower level system that can be used in many spells without the need to extend or implement anything. It is basically just used to remove all the [ljass]local unit cast = GetTriggerUnit()[/ljass] crap.
 

BlackRose

Forum User
Reaction score
239
I find it useful, but I prefer to do it my own way :) Classic typing it all out. Now get onto getting out that projectile system :)
 

Kenny

Back for now.
Reaction score
202
@ kingkingyyk3:

Hmm.. He stole my idea. :p

To be honest, I find a fair bit of the stuff in his system a bit useless for what the system is meant to be.

- Systems like this shouldn't add functionality for stuff that is quite uncommon (such as his item stuff).
- He uses methods to return readonly struct members. Which to me, isn't very intuitive. Either one or the other is fine.
- His uses GetHandleId() and Hashtables. Therefore it isn't pre 1.23 compatible (not like this is a big issue). Mine isn't compatible either, but it can be if I remove GetSpellTargetX/Y.
- His also has a fair new things that are only important for debugging. I don't think they should belong in a non-debugging library, let alone be accessible.
- I find autoDestroy a bit much for something so simple. Just ask users to destroy the struct when they are finished. Chances are they will be using the preserve method most of the time anyway.
- His can't update coordinates and stuff when needed by the user.

There are a variety of things that are quite different in the systems, as you can see. His is probably a bit faster due to the use of GetHandleId(), but speed seems irrelevant in this situation.

@ BlackRose:

For one or two spells, writting it all out is probably preferable, however, when writing 10+ spells, this soon becomes a lot more preferable.

PS: The Projectile system is underway, but there are a few things that should be removed from it before release.

Edit:

I was thinking... Instead of the update method that updates everything at once, I could divide up the method to make it more efficient. If a user only needs to update the targets X, Y, and Z coordinates, then only those should be updated via a method. Something like this:

JASS:
method updateCaster takes nothing returns nothing
    set this.casterX = GetUnitX(this.casterUnit)
    set this.casterY = GetUnitY(this.casterUnit)
    call MoveLocation(thistype.tempLoc,this.casterX,this.casterY)
    set this.casterZ = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.casterUnit)
endmethod

// Again, only units will be moving (not destructables and items).
method updateTarget takes nothing returns nothing
    set this.targetX = GetUnitX(this.targetUnit)
    set this.targetY = GetUnitY(this.targetUnit)
    call MoveLocation(thistype.tempLoc,this.targetX,this.targetY)
    set this.targetZ = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.targetUnit)
endmethod
 

Viikuna

No Marlo no game.
Reaction score
265
There is already SpellEvent for doing that stuff for spell events.

As for orders, that stuff should go to some Order library, which is desinged to handle order related stuff, such as finding units previous orders and re-issuing them, as well as handling order events ( with priority events hopefully, because they are damn usefull sometimes ) and whatever order related stuff you need.
 

Kenny

Back for now.
Reaction score
202
> There is already SpellEvent for doing that stuff for spell events.

Hmm... Seems like a mixture between GTrigger and this library. It seems that whenever someone comes up with a good idea on thehelper, someone over at wc3c attempts to make their own version of it...

Anywho, I made this for my upcoming attempt at making an order library that does indeed store a units orders and allows you to reissue them if possible. I will probably attempt it without hashtables and GetHandleId() first and see how I go. I have some AIDS / array wrapper / order struct concoction in mind. :p
 

Jesus4Lyf

Good Idea™
Reaction score
397
>an order library that does indeed store a units orders and allows you to reissue them if possible
That's been done, too... :D

>It seems that whenever someone comes up with a good idea on thehelper, someone over at wc3c attempts to make their own version of it...
GTrigger.
Was responded to by SpellEvent.
Was responded to by SpellStruct. :p
 

Kenny

Back for now.
Reaction score
202
> That's been done, too...

Yeah, I actually know about that Last Order library over at wc3c, but I like writing my own stuff, and I like not using GetHandleId() and Hashtables where possible. I know there is nothing wrong with them, I just like attempting to do it.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>I like writing my own stuff, and I like not using GetHandleId() and Hashtables where possible. I know there is nothing wrong with them, I just like attempting to do it.
Yeah, I'm with ya.

>But it has the downside of needing to extend structs and implement modules.
Hey... extending a struct is a problem, but implementing a module is not, right?
JASS:
struct ProjectileSpellStruct extends SpellStruct
    // ROAR MY PROJECTILE STUFF
endstruct

struct MySpell extends ProjectileSpellStruct
    implement SpellStruct
    
    // ROAR MY SPELL STUFF
endstruct

Magic.
Is there anything you don't like of SpellStruct? Remember, you can actually override nearly every method I provide. :)
 

Kenny

Back for now.
Reaction score
202
>Hey... extending a struct is a problem, but implementing a module is not, right?

Actually I don't have a problem with either. But some people might not want to even use structs for some simple spells, or just don't want to implement SpellStruct into their maps. That is the only downside, and that is where something like this can come in handy.

>Is there anything you don't like of SpellStruct? Remember, you can actually override nearly every method I provide.

The only thing that I dislike about SpellStruct right now, is that the script seems a little messy. :p I found bits of it a little hard to understand. But the interface is pure gold.
 

Viikuna

No Marlo no game.
Reaction score
265
CasterSystem existed before GTrigger and thats the first place ( that I know ) where that idea was used.

Its not like Jesus4Lyf invented it or anything. Probably it wasnt Vexorian either. Most of this stuff has already been done by someone else back in the day when wc3 modding was not so dead.


And SpellEvent is used for a reason. Its the only system ( that I know ) that does this stuff properly and its also pretty cool.


edit.

People have long forgotten that who invented this and who invented that.

Noone remembers for example that it was Tides of Blood team that found out how to make 6v6 AoS when it became possible thanks to Blizzards AI updates. Those guys where only guys who even cared about any AI updates, and everybody else had already accepted the fact that 6v6 AoS was something that could not be done.

Now theres shitload of 6v6 AoS maps, and none of them gives credit to the guys who made it possible.
 

Jesus4Lyf

Good Idea™
Reaction score
397
CasterSystem existed before GTrigger and thats the first place ( that I know ) where that idea was used.

Its not like Jesus4Lyf invented it or anything. Probably it wasnt Vexorian either. Most of this stuff has already been done by someone else back in the day when wc3 modding was not so dead.


And SpellEvent is used for a reason. Its the only system ( that I know ) that does this stuff properly and its also pretty cool.


edit.

People have long forgotten that who invented this and who invented that.

Noone remembers for example that it was Tides of Blood team that found out how to make 6v6 AoS when it became possible thanks to Blizzards AI updates. Those guys where only guys who even cared about any AI updates, and everybody else had already accepted the fact that 6v6 AoS was something that could not be done.

Now theres shitload of 6v6 AoS maps, and none of them gives credit to the guys who made it possible.

>Now theres shitload of 6v6 AoS maps, and none of them gives credit to the guys who made it possible.
Probably because everyone figures it out themselves.

>Its not like Jesus4Lyf invented it or anything.
Just like I did with GTrigger.

>And SpellEvent is used for a reason. Its the only system ( that I know ) that does this stuff properly
Might be time to see SpellStruct (you helped start it by calling me an efficiency whore).
 

Viikuna

No Marlo no game.
Reaction score
265
Okay, yea. Since we cant see inside peoples head, its pretty hard to know when some guy really invents something really new.

I just had to reply since you said that SpellEvent is response of GTrigger, which might indeed be true, but probably aint, because SpellEvent was mainly created to fix event responses, which is something that GTrigger does not do, and because Anitarf has been in the game for so long there in wc3c, that its very likely that it was CasterSystem that inspired him and not GTrigger, although its maybe more likely that it indeed was GTrigger, because some guys indeed tried to submit spells using it to wc3c.
 

Kenny

Back for now.
Reaction score
202
Hmm... I am actually thinking about submitting this, I think it could be incredibly valuable to members here at thehelper who aren't 'great' at JASS and for people who want to simplify their scripts.

Heres is what I have now:

JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  ~~    EventResponses    ~~    By kenny!    ~~    Version 1.00    ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is EventResponses?
//      - EventResponses is a system that allows users to easily access
//        the most common event response natives from spell and order events.
//      - It was created due to the repetitive nature of writing multiple scripts,
//        both for maps and for released spells and systems. The systems aims to
//        reduce the amount of time spent developing these spells and systems by
//        simplifying the tedious coding needed to write them.
//
//  Functions:
//      - EventResp.create(boolean) --&gt; Creates a new event response struct.
//                                      Holds all the variables needed. The
//                                      boolean argument is to determine whether
//                                      or not the distances and angles should be set.
//      - .updateCaster()           --&gt; Updates the x, y and z locations of
//                                      the caster.
//      - .updateTarget()           --&gt; Updates the x, y and z locations of
//                                      the target (for units only).
//      - .updateDistances()        --&gt; Updates the distance between the caster
//                                      and target coordinates (includes XYZ dist).
//      - .updateAngles()           --&gt; Updates the angle between the caster and
//                                      target coordinates (includes XYZ angle).
//      - .destroy()                --&gt; Destroys the struct holding the variables.
//                      
//  Variables:
//      - .casterUnit   --&gt; Returns the unit that casted the spell, or the unit that
//                          was issued an order.
//      - .targetWidg   --&gt; Returns the targeted widget of an order or spell. Does not
//                          return a unit, destructable or item.
//      - .targetUnit   --&gt; Returns the targeted unit of an order or spell. Will return
//                          null if the target isn&#039;t a unit.
//      - .targetDest   --&gt; Returns the targeted destructable of an order or spell.
//                          Will return null if the target isn&#039;t a destructable.
//      - .targetItem   --&gt; Returns the targeted item of an order or spell. Will return
//                          null if the target isn&#039;t an item.
//      - .casterX      --&gt; Returns the X coordinate of the casting unit or the ordered
//                          unit.
//      - .casterY      --&gt; Returns the Y coordinate of the casting unit or the ordered
//                          unit.
//      - .casterZ      --&gt; Returns the Z coordinate of the casting unit or the ordered
//                          unit.
//      - .targetX      --&gt; Returns the X coordinate of the target of an order or 
//                          spell. Returns .casterX for immediate orders.
//      - .targetY      --&gt; Returns the Y coordinate of the target of an order or 
//                          spell. Returns .casterY for immediate orders.
//      - .targetZ      --&gt; Returns the Z coordinate of the target of an order or 
//                          spell. Returns .casterZ for immediate orders.
//      - .distXY       --&gt; Returns the distance between the .casterUnit and the target
//                          widget or location of an order or spell.
//      - .distXYZ      --&gt; Similar to the above variable, however .distXYZ will take
//                          into account the Z coordinate of the caster and target.
//      - .angle        --&gt; Returns the angle between the caster and the target in
//                          radians.
//      - .pitch        --&gt; Returns the angle between the caster and the target, also
//                          takes into account the Z location of both.
//      - .orderId      --&gt; Returns the order id of the spell or order that the caster
//                          was issued in integer format.
//      - .orderStr     --&gt; Returns the order string of the spell or order that the
//                          caster was issued.
//
//  Bonus Variables:
//      - .isTargetWidg --&gt; Returns true if the target is a widget and false if there
//                          is no target (point or immediate order).
//      - .isTargetUnit --&gt; Returns true if the target is a unit and false if it is
//                          either a destructable or item, or if there is no target.
//      - .isTargetDest --&gt; Returns true if the target is a destructable and false if
//                          it is either a unit or item, or if there is no target.
//      - .isTargetItem --&gt; Returns true if the target is an item and false if it is
//                          either a unit or destructable, or if there is no target.
//      - .isTargetLand --&gt; Returns true if there is no target widget but still a
//                          target location, and false if there is a target.
//      - .isTargetNone --&gt; Returns true if there is no target widget or location and
//                          false if there is (works for immediate orders).     
//
//  Details:
//      - There are no configurables for this system, just import it and use it.
//      - Aquiring the Z coordinate of units uses GetLocationZ() which can desync.
//      - EventResponses will only work with some player unit and unit events,
//        these include:
//          - EVENT_PLAYER_UNIT_SPELL_CHANNEL
//          - EVENT_PLAYER_UNIT_SPELL_CAST
//          - EVENT_PLAYER_UNIT_SPELL_EFFECT
//          - EVENT_PLAYER_UNIT_SPELL_FINISH
//          - EVENT_PLAYER_UNIT_SPELL_ENDCAST
//          - EVENT_PLAYER_UNIT_ISSUED_ORDER
//          - EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
//          - EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER
//          - EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER
//
//          - EVENT_UNIT_SPELL_CHANNEL
//          - EVENT_UNIT_SPELL_CAST
//          - EVENT_UNIT_SPELL_EFFECT
//          - EVENT_UNIT_SPELL_FINISH
//          - EVENT_UNIT_SPELL_ENDCAST
//          - EVENT_UNIT_ISSUED_ORDER
//          - EVENT_UNIT_ISSUED_TARGET_ORDER
//          - EVENT_UNIT_ISSUED_POINT_ORDER
//      - Only these events will work as they have common native responses. Other
//        Events have different responses, which would make this system messy if
//        it attempted to work for all of them.
//
//  How to import:
//      - Create a trigger named EventResponses.
//      - Convert it to custom text and replace the whole trigger text with this.
//
//  Thanks:
//      - Jesus4Lyf for hopefully letting me use his method of determining
//        whether or not the spell or order has a target widget or location.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library EventResponses

    globals
        // Different target types for boolean checks.
        private constant integer TARGET_TYPE_NONE = 1
        private constant integer TARGET_TYPE_UNIT = 2
        private constant integer TARGET_TYPE_DEST = 3
        private constant integer TARGET_TYPE_ITEM = 4
        private constant integer TARGET_TYPE_LAND = 5
        
        // Required for event initialisation.
        private constant integer MAXIMUM_EVENTS   = 17
        private constant integer MAX_SPELL_EVENTS = 9
    endglobals
    
    // My attempt at not using GetHandleId().
    private struct EventId
    
        readonly boolean  isSpell = false // If it is a spell or order event.
        private  eventid  eventId = null  // The event itself.
        private  thistype next    = 0     // Next event in the list.
        
        private static timer tempTimer = null // Timer needed for initial registration.
        private static eventid array eventIds // Array to store the used events.
    
        // I kind of liked this interface.
        static method operator[] takes eventid id returns thistype
            local thistype this = thistype(0).next
            
            loop
                exitwhen this == 0
                if id == this.eventId then
                    return this
                endif
                set this = this.next
            endloop
            
            return 0
        endmethod
        
        // Set up each event.
        private static method setEvent takes eventid id, integer i returns nothing
            local thistype this = thistype.allocate()
            
            set this.eventId = id // Set the eventid.
            
            if i &lt;= MAX_SPELL_EVENTS then
                set this.isSpell = true // The first 9 events are spell events.
            endif
            
            set this.next=thistype(0).next
            set thistype(0).next=this
        endmethod
        
        // Register the events at 0.00 seconds of game time (It wouldn&#039;t work otherwise).
        private static method registerEvents takes nothing returns nothing
            local integer i = 0
            
            loop
                exitwhen i == MAXIMUM_EVENTS
                call setEvent(eventIds<i>,i)
                set i = i + 1
            endloop
            
            // Clean up the temporary timer.
            call PauseTimer(tempTimer)
            call DestroyTimer(tempTimer)
            set tempTimer = null
        endmethod
        
        // Initialise all the events used.
        private static method onInit takes nothing returns nothing
            set eventIds[0]  = EVENT_PLAYER_UNIT_SPELL_CHANNEL
            set eventIds[1]  = EVENT_PLAYER_UNIT_SPELL_CAST
            set eventIds[2]  = EVENT_PLAYER_UNIT_SPELL_EFFECT
            set eventIds[3]  = EVENT_PLAYER_UNIT_SPELL_FINISH
            set eventIds[4]  = EVENT_PLAYER_UNIT_SPELL_ENDCAST
            set eventIds[5]  = EVENT_UNIT_SPELL_CHANNEL
            set eventIds[6]  = EVENT_UNIT_SPELL_CAST
            set eventIds[7]  = EVENT_UNIT_SPELL_EFFECT
            set eventIds[8]  = EVENT_UNIT_SPELL_FINISH
            set eventIds[9]  = EVENT_UNIT_SPELL_ENDCAST
            
            set eventIds[10] = EVENT_PLAYER_UNIT_ISSUED_ORDER
            set eventIds[11] = EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER
            set eventIds[12] = EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
            set eventIds[13] = EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER
            set eventIds[14] = EVENT_UNIT_ISSUED_ORDER
            set eventIds[15] = EVENT_UNIT_ISSUED_POINT_ORDER
            set eventIds[16] = EVENT_UNIT_ISSUED_TARGET_ORDER
            
            // Start the timer.
            set tempTimer = CreateTimer()
            call TimerStart(tempTimer,0.00,false,function thistype.registerEvents)
        endmethod
        
    endstruct

    // The exported EventResp struct.
    struct EventResp
    
        // All the accessible variables.
        readonly unit         casterUnit = null
        readonly widget       targetWidg = null
        readonly unit         targetUnit = null
        readonly destructable targetDest = null
        readonly item         targetItem = null
        readonly string       orderStr   = &quot;&quot;
        readonly real         casterX    = 0.00
        readonly real         casterY    = 0.00
        readonly real         casterZ    = 0.00
        readonly real         targetX    = 0.00
        readonly real         targetY    = 0.00
        readonly real         targetZ    = 0.00
        readonly real         distXY     = 0.00
        readonly real         distXYZ    = 0.00
        readonly real         angle      = 0.00
        readonly real         pitch      = 0.00
        readonly integer      orderId    = 0
        readonly integer      targetType = 0
        
        private static location tempLoc   = null
        
        // Boolean checks.
        method operator isTargetWidg takes nothing returns boolean
            return this.targetType == TARGET_TYPE_UNIT or this.targetType == TARGET_TYPE_DEST or this.targetType == TARGET_TYPE_ITEM
        endmethod
        
        method operator isTargetUnit takes nothing returns boolean
            return this.targetType == TARGET_TYPE_UNIT
        endmethod
        
        method operator isTargetDest takes nothing returns boolean
            return this.targetType == TARGET_TYPE_DEST
        endmethod
        
        method operator isTargetItem takes nothing returns boolean
            return this.targetType == TARGET_TYPE_ITEM
        endmethod
        
        method operator isTargetLand takes nothing returns boolean
            return this.targetType == TARGET_TYPE_LAND
        endmethod
        
        method operator isTargetNone takes nothing returns boolean
            return this.targetType == TARGET_TYPE_NONE
        endmethod
        
        // Clean up.
        method destroy takes nothing returns nothing
            set this.casterUnit = null
            set this.targetWidg = null
            set this.targetUnit = null
            set this.targetDest = null
            set this.targetItem = null
            
            call this.deallocate()
        endmethod
        
        method updateCaster takes nothing returns nothing
            set this.casterX = GetUnitX(this.casterUnit)
            set this.casterY = GetUnitY(this.casterUnit)
            call MoveLocation(tempLoc,this.casterX,this.casterY)
            set this.casterZ = GetLocationZ(tempLoc) + GetUnitFlyHeight(this.casterUnit)
        endmethod
        
        method updateTarget takes nothing returns nothing
            set this.targetX = GetUnitX(this.targetUnit)
            set this.targetY = GetUnitY(this.targetUnit)
            call MoveLocation(tempLoc,this.targetX,this.targetY)
            set this.targetZ = GetLocationZ(tempLoc) + GetUnitFlyHeight(this.targetUnit)
        endmethod
        
        method updateDistances takes nothing returns nothing
            local real x = this.targetX - this.casterX
            local real y = this.targetY - this.casterY
            local real z = this.targetZ - this.casterZ
                
            set this.distXY  = SquareRoot(x * x + y * y)
            set this.distXYZ = SquareRoot(x * x + y * y + z * z)
        endmethod
        
        method updateAngles takes nothing returns nothing
            local real x = this.targetX - this.casterX
            local real y = this.targetY - this.casterY
            local real z = this.targetZ - this.casterZ
            
            set this.angle   = Atan2(y,x)
            set this.pitch   = Atan2(SquareRoot(x * x + y * y),z)
        endmethod
        
        // Scabbed off Jesus4Lyf, hope you don&#039;t mind. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite7" alt=":p" title="Stick Out Tongue    :p" loading="lazy" data-shortname=":p" />
        //! textmacro SetTargetMembers takes TYPE, LAND
            set this.targetUnit = Get$TYPE$TargetUnit()
            if this.targetUnit == null then
                set this.targetDest = Get$TYPE$TargetDestructable()
                if this.targetDest == null then
                    set this.targetItem = Get$TYPE$TargetItem()
                    if this.targetItem == null then
                        set this.targetWidg = null
                        set this.targetX    = Get$LAND$X()
                        set this.targetY    = Get$LAND$Y()
                        call MoveLocation(tempLoc,this.targetX,this.targetY)
                        set this.targetZ    = GetLocationZ(tempLoc)
                        if this.targetX != 0.00 or this.targetY != 0.00 or this.targetZ != 0.00 then
                            set this.targetType = TARGET_TYPE_LAND
                        else
                            set this.targetType = TARGET_TYPE_NONE
                            set this.targetX    = this.casterX
                            set this.targetY    = this.casterY
                            set this.targetZ    = this.casterZ
                        endif
                    else
                        set this.targetWidg = this.targetItem
                        set this.targetType = TARGET_TYPE_ITEM
                        set this.targetX    = GetItemX(this.targetItem)
                        set this.targetY    = GetItemY(this.targetItem)
                        call MoveLocation(tempLoc,this.targetX,this.targetY)
                        set this.targetZ    = GetLocationZ(tempLoc)
                    endif
                else
                    set this.targetWidg = this.targetDest
                    set this.targetItem = null
                    set this.targetType = TARGET_TYPE_DEST
                    set this.targetX    = GetDestructableX(this.targetDest)
                    set this.targetY    = GetDestructableY(this.targetDest)
                    call MoveLocation(tempLoc,this.targetX,this.targetY)
                    set this.targetZ    = GetLocationZ(tempLoc)
                endif
            else
                set this.targetWidg = this.targetUnit
                set this.targetDest = null
                set this.targetItem = null
                set this.targetType = TARGET_TYPE_UNIT
                set this.targetX    = GetUnitX(this.targetUnit)
                set this.targetY    = GetUnitY(this.targetUnit)
                call MoveLocation(tempLoc,this.targetX,this.targetY)
                set this.targetZ    = GetLocationZ(tempLoc) + GetUnitFlyHeight(this.targetUnit)
            endif
        //! endtextmacro
            
        // Create the event responses.
        static method create takes boolean setDistAndAngle returns thistype
            local EventId  id   = EventId[GetTriggerEventId()]
            local thistype this = 0
            local real     x    = 0.00
            local real     y    = 0.00
            local real     z    = 0.00
            
            // If the event responses were created for an unsupported event, show an error.
            if id == 0 then
                debug call BJDebugMsg(&quot;|cFFFF0000Error using EventResponses:|r Unsupported event type used.&quot;)
                return 0
            endif           
            
            // Assign all the members.
            set this            = thistype.allocate()
            set this.casterUnit = GetTriggerUnit()
            set this.casterX    = GetUnitX(this.casterUnit)
            set this.casterY    = GetUnitY(this.casterUnit)
            call MoveLocation(tempLoc,this.casterX,this.casterY)
            set this.casterZ    = GetLocationZ(tempLoc) + GetUnitFlyHeight(this.casterUnit)
            set this.orderId     = GetIssuedOrderId()
            set this.orderStr    = OrderId2String(this.orderId)
            
            if id.isSpell then
                //! runtextmacro SetTargetMembers(&quot;Spell&quot;,&quot;SpellTarget&quot;)
            else
                //! runtextmacro SetTargetMembers(&quot;Order&quot;,&quot;OrderPoint&quot;)
            endif
            
            // Set distances and angles if required.
            if setDistAndAngle then
                set x = this.targetX - this.casterX
                set y = this.targetY - this.casterY
                set z = this.targetZ - this.casterZ
                
                set this.distXY  = SquareRoot(x * x + y * y)
                set this.distXYZ = SquareRoot(x * x + y * y + z * z)
                set this.angle   = Atan2(y,x)
                set this.pitch   = Atan2(SquareRoot(x * x + y * y),z)
            endif
            
            return this
        endmethod
        
        private static method onInit takes nothing returns nothing
            set tempLoc = Location(0.00,0.00)
        endmethod
        
    endstruct
        
endlibrary 
</i>


(As you can see, I scabbed the documentation section off Jesus4Lyf, who I think scabbed it off Cohadar).

So yeah, any feedback about stuff I could add or remove, or even stuff that can be made better would be nice. :)

Edit:

For me (because my computer broke and I can only use notepad):

JASS:
library OrderLib initializer Init requires AIDS, EventResp

    globals
        private constant integer ORDERS_PER_UNIT = 3
	private constant integer MAX_ORDERS      = 8191
	private constant integer ARRAY_SIZE      = ORDERS_PER_UNIT + 1
    endglobals
	
    private function FilterUnits takes unit ordered returns nothing
	return GetUnitAbilityLevel(ordered,&#039;Aloc&#039;) == 0
    endfunction
	
    private function FilterOrders takes integer id returns boolean
        return id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or id == 851993 or (id &gt;= 852025 and id &lt;= 852762)
    endfunction
	
    struct Order[MAX_ORDERS]
	
	private delegate EventResp er = 0
	private boolean  isFin        = false
		
	method operator isFinished takes nothing returns boolean
	    return this.isFin
	endmethod
		
	method operator isFinished= takes boolean flag returns nothing
	    set this.inFin = flag
	endmethod
		
	static method create takes nothing returns thistype
	    local thistype this = thistype.allocate()
			
	    set this.er = EventResp.create(false)
			
	    return this
	endmethod
		
        method destroy takes nothing returns nothing
	    call this.er.destroy()
	    call this.deallocate()
	endmethod
		
    endstruct
	
    private struct Orders

        private Order array order[ARRAY_SIZE]
		
	method operator[] takes integer i returns Order
       	    return this.order<i>
	endmethod
		
	method operator[]= takes integer i, Order o returns nothing
	    set this.order<i> = o
	endmethod
		
	method destroy takes nothing returns nothing
	    local integer i = ORDERS_PER_UNIT
			
            loop
	        exitwhen i == 0
		if order<i> != 0 then
		    call this.order<i>.destroy()
		endif
		set i = i - 1
            endloop

            call this.deallocate()
	endmethod
		
    endstruct
	
    private struct Data extends array
        //! runtextmacro AIDS()
		
        readonly Orders orders
		
	private static method AIDS_filter takes unit u returns boolean
       	    return FilterUnits(u)
	endmethod
		
	private method AIDS_onCreate takes nothing returns nothing
	    set this.orders = Orders.create()
	endmethod
		
	private method AIDS_onDestroy takes nothing returns nothing
	    call this.orders.destroy()
	endmethod
		
    endstruct
	
    function GetPastOrder takes unit u, integer i returns Order
        return Data<u>.orders<i>
    endfunction
	
    function GetPastOrderTarget takes unit u, integer i returns unit
	return Data<u>.orders<i>.targetUnit
    endfunction
	
    function GetPastOrderTargX takes unit u, integer i returns real
	return Data<u>.orders<i>.targetX
    endfunction
	
    function GetPastOrderTargY takes unit u, integer i returns real
	return Data<u>.orders<i>.targetY
    endfunction
	
    function GetPastOrderId takes unit u, integer i returns integer
	return Data<u>.orders<i>.orderId
    endfunction
	
    function GetPastOrderString takes unit u, integer i returns string
 	return Data<u>.orders<i>.orderStr
    endfunction
	
    function IssuePastOrder takes unit u, integer i returns boolean
        local Order ord = GetPastOrder(u,i)
		
	if GetUnitTypeId(u) != 0 and IsUnitType(u,UNIT_TYPE_DEAD) == false and ord != 0 and not ord.isFinished then
	    if ord.isTargetNone then
		return IssueImmediateOrderById(u,ord.orderId)
	    elseif ord.isTargetWidget then
		return IssueTargetOrderById(u,ord.orderId,ord.targetWidg)
	    elseif ord.isTargetLand then
		if ord.orderId == 851971 then
		    call IssuePointOrderById(u,ord.orderId,ord.targetX,ord.targetY)
		    return true
		else
		    return IssuePointOrderById(u,ord.orderId,ord.targetX,ord.targetY)
		endif
	    endif
	endif
		
	return false
    endfunction
	
    private function OrderActions takes nothing returns boolean
	local unit    ordered = GetTriggerUnit()
	local integer id      = GetIssuedOrderId()
	local integer max     = ORDERS_PER_UNIT
	local Orders  ords    = Data[ordered].orders
		
	if FilterUnits(ordered) and FilterOrders(id) then
	    loop
		exitwhen max == 1
		if ords[max - 1] != 0 then
		    if max == ORDERS_PER_UNIT and ords[max] != 0 then
			call ords[max].destroy()
		    endif
		    set ords[max] = ords[max - 1]
		endif
	        set max = max - 1
	    endloop
	    set ords[max] = Order.create()
        endif
		
	set ordered = null
		
	return false
    endfunction
	
    private function SpellActions takes nothing returns boolean
	set Data[GetTriggerUnit()].orders<i>.isFinished = true
	return false
    endfunction
	
    private function Init takes nothing returns nothing
	local trigger trig = CreateTrigger()
		
	call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_ISSUED_ORDER)
	call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
	call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
	call TriggerAddCondition(trig,Condition(function OrderActions))
		
	set trig = CreateTrigger()
	call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_EFFECT)
	call TriggerAddCondition(trig,Condition(function SpellActions))
    endfunction
	
endlibrary
</i></i></u></i></u></i></u></i></u></i></u></i></u></i></i></i></i>
 

Vexorian

Why no custom sig?
Reaction score
187
I am a little mad because a guy has been spreading the rumor that I copied GTrigger and stuff like that... I was handled some links today but unfortunately the forum in which that was claimed does not allow bumps. My hunch is that his confusion started by reading the strange interpretations of reality found in this thread. So without being able to handle the issue directly I 'll instead post stuff here.

CasterSystem existed before GTrigger and thats the first place ( that I know ) where that idea was used.

Its not like Jesus4Lyf invented it or anything. Probably it wasnt Vexorian either. Most of this stuff has already been done by someone else back in the day when wc3 modding was not so dead.

And SpellEvent is used for a reason. Its the only system ( that I know ) that does this stuff properly and its also pretty cool.


edit.

People have long forgotten that who invented this and who invented that.

AIAndy (One of the guys behind AMAI, I guess people don't remember him) was the first person I remember to do what was both an event system and a templates system. It was either 2003 or 2004 (my memory is not that good) the time after philosopher stones such as game cache and the return bug were discovered (lol, in retrospective, when I think of how excited people were after finding those things and how we started using that crap it is very funny) That he thought "great we can now do OOP!" and as a way to prove it he made a class system, with inheritance and polymorphism all based on gamecache. I was rather inexperienced back then and it seemed like Chinese, what I did know is that it didn't perform too well but it was VERY powerful. So he made events and 'templates' as an application of it. I put templates between quotes because he would call them instances of a spell class.

It was VERY powerful but too few people would understand it. As for me, it seemed over complicated so I made the spell templates system about in 2004 which was a blatant copy of the idea behind the stuff AIAndy made, but it was much simpler. Granted, this is what happens just about every time, somebody does not really think a system was a good implementation so he codes an alternative which may or may not be better (I wouldn't dare to say mine was better, because again AIAndy's was VERY powerful).

The spell templates system was simplistic but it had two parts, the events system ( RegisterSpellEvent('A000', "Function") ) and the templates system (a bunch of gamecache tables, really) and the spell templates themselves, increasing in amount over the years (and many of them were horribly bugged, this is what was released post-vJass under the name spell factory)

Sometime later I became VERY stupid and tried to make the Caster system have a TON of stuff. In my defense, that was much before vJass so modularity was very complicated to do in a way that would be easy for users to implement. But it was nevertheless stupid. So I adapted the old spell events stuff, renamed the functions to avoid name conflicts and... embedded all of them inside the caster system, boy that was stupid. (OnAbilityEffect('A000', "action") )

Years later, vJass is made and we finally had modules I remember telling Anitarf about my intention to do something like that, however, also replacing event responses with function arguments but I eventually forgot about it. If you ask me, this kind of system is pretty overrated :) (yep, including the old ones). Anyway, Anitarf made his library and I think it was in response for me not making a vJass response to the caster system's events stuff. But he also wanted event responses, much to my disagreement he didn't go with function arguments. I personally haven't heard of GTrigger till today and I don't think Anitarf did back when he made SpellEvents either.

However. The whole notion is silly because really very few things in wc3 really count as great 'inventions' or 'original research'. Most of these things are really things that are born out of necessity and it is no shocker multiple systems with the same function would exist. I can say that the last years the whole community including all sites has not really discovered much but just tried to 'improve' implementations of previous systems instead. Most of the attempts to improve things are not blatant copies but are, of course, similar as they do the same thing...
 
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