Snippet Transport (Enter/Leave Detection)

Jesus4Lyf

Good Idea™
Reaction score
397
Transport​
Version 1.02​

Requirements:
- Jass NewGen
- AIDS
- Event

Code:
JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  ~~    Transport     ~~    By Jesus4Lyf    ~~    Version 1.02    ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is Transport?
//         - Transport provides Events for transport in Warcraft III.
//         - It also tracks what transportation unit a unit is in, and
//           can iterate over all units a transport unit is carrying.
//
//  Functions:
//         - Transport_RegisterLoadEvent(trigger)
//           Registers when a unit enters a transport. Within this:
//            - Use GetTriggerUnit() to refer to the passenger.
//            - Use GetTransportUnit() to refer to the transport.
//
//         - Transport_RegisterUnloadEvent(trigger)
//           Registers when a unit leaves a transport. Within this:
//            - Use GetUnloadedUnit() to refer to the passenger.
//            - Use GetUnloadingTransport() to refer to the transport.
//
//         - Transport_GetCarrier(passenger unit)
//           Returns the transport the unit has currently boarded.
//
//         - Transport_ForPassengers(transport unit, callback function)
//           Calls a ForGroup using the units on board as the group. Within this:
//            - Use GetEnumUnit() to refer to each unit on board.
//
//         - Transport_CountPassengers(transport unit)
//           Returns the number of units on board a transport unit.
//
//  Details:
//         - Unloading is detected by setting a unit's position to outside
//           the map when it is loaded into transport, and then registering when
//           it enters the map again, meaning it was unloaded.
//
//         - The purpose of the Load event is that it fires when the unit's x/y
//           coordinates are correct, but the state of Transport has been updated
//           (so Transport_GetCarrier and Transport_ForPassengers will work correctly).
//
//  How to import:
//         - Create a trigger named Transport.
//         - Convert it to custom text and replace the whole trigger text with this.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Transport uses AIDS, Event, optional Recycle, optional GroupUtils
    globals
        private Event LoadEvent
        private Event UnloadEvent
        private real MAX_X
        private real MAX_Y
    endglobals
    public function RegisterLoadEvent takes trigger t returns EventReg
        return LoadEvent.register(t)
    endfunction
    public function RegisterUnloadEvent takes trigger t returns EventReg
        return UnloadEvent.register(t)
    endfunction
    
    // Group recycling stuff, if available.
    //! textmacro Transport__GetGroup takes VAR
        static if LIBRARY_Recycle then
            call Group.release($VAR$)
        elseif LIBRARY_GroupUtils then
            call ReleaseGroup($VAR$)
        else
            call DestroyGroup($VAR$) // never had enums called.
            set $VAR$=null
        endif
    //! endtextmacro
    //! textmacro Transport__ReleaseGroup takes VAR
        static if LIBRARY_Recycle then
            set $VAR$=Group.get()
        elseif LIBRARY_GroupUtils then
            set $VAR$=NewGroup()
        else
            set $VAR$=CreateGroup()
        endif
    //! endtextmacro
    
    // Data for system
    private struct Data extends array
        // Transport
        group carrying
        integer passengerCount
        // Passenger
        thistype carriedBy
        private static method unloadEnum takes nothing returns nothing
            set thistype[GetEnumUnit()].carriedBy=thistype(0)
        endmethod
        private method AIDS_onDestroy takes nothing returns nothing
            // Transport
            if this.carrying!=null then
                call ForGroup(this.carrying,function thistype.unloadEnum)
                //! runtextmacro Transport__ReleaseGroup("this.carrying")
            endif
            set this.passengerCount=0
            
            // Passenger
            set this.carriedBy=thistype(0)
        endmethod
        //! runtextmacro AIDS()
    endstruct
    public function GetCarrier takes unit passenger returns unit
        return Data[passenger].carriedBy.unit
    endfunction
    public function ForPassengers takes unit transport, code callback returns nothing
        call ForGroup(Data[transport].carrying,callback)
    endfunction
    public function CountPassengers takes unit transport returns integer
        return Data[transport].passengerCount
    endfunction
    
    // Stack for event responses, with recursion safety.
    private module Stack
        static thistype top=0 // thistype(0) throws a syntax error, does not compile
        static method increment takes nothing returns nothing
            set thistype.top=thistype(thistype.top+1)
        endmethod
        static method decrement takes nothing returns nothing
            set thistype.top=thistype(thistype.top-1)
        endmethod
    endmodule
    
    // Event responses for UnloadEvent.
    private struct UnloadStack extends array
        implement Stack
        unit transport
        unit passenger
    endstruct
    function GetUnloadingTransport takes nothing returns unit
        return UnloadStack.top.transport
    endfunction
    function GetUnloadedUnit takes nothing returns unit
        return UnloadStack.top.passenger
    endfunction
    
    // When a unit enters world boundaries...
    globals//locals
        private unit EnteringUnit
        private Data EnteringData
        private Data EnteringTransportData
    endglobals
    private function OnEnterWorld takes nothing returns boolean
        set EnteringUnit=GetFilterUnit()
        set EnteringData=Data[EnteringUnit]
        if EnteringData.carriedBy!=null then // Unit was unloaded.
            
            call UnloadStack.increment()
            set EnteringTransportData=EnteringData.carriedBy
            set UnloadStack.top.transport=EnteringTransportData.unit
            set UnloadStack.top.passenger=EnteringUnit
            
            call GroupRemoveUnit(EnteringTransportData.carrying,EnteringUnit)
            set EnteringTransportData.passengerCount=EnteringTransportData.passengerCount-1
            set EnteringData.carriedBy=Data(0)
            
            // Global locals unsafe from this point on
            call UnloadEvent.fire()
            call UnloadStack.decrement()
            
        endif
        return false
    endfunction
    
    // When a unit enters a transport...
    globals//locals
        private unit LoadedUnit
        private Data LoadingData
    endglobals
    private function OnLoad takes nothing returns boolean
        set LoadedUnit=GetTriggerUnit()
        set LoadingData=Data[GetTransportUnit()]
        if LoadingData.carrying==null then
            //! runtextmacro Transport__GetGroup("LoadingData.carrying")
        endif
        call GroupAddUnit(LoadingData.carrying,LoadedUnit)
        set LoadingData.passengerCount=LoadingData.passengerCount+1
        set Data[LoadedUnit].carriedBy=LoadingData
        call LoadEvent.fire()
        call SetUnitX(LoadedUnit,MAX_X)
        call SetUnitY(LoadedUnit,MAX_Y)
        return false
    endfunction
    
    // Init.
    private struct Initializer extends array
        private static method onInit takes nothing returns nothing
            local trigger t
            local rect r=GetWorldBounds()
            local region g=CreateRegion()
            call RegionAddRect(g,r)
            set MAX_X=GetRectMaxX(r)
            set MAX_Y=GetRectMaxY(r)
            call RemoveRect(r)
            set r=null
            
            set LoadEvent=Event.create()
            set UnloadEvent=Event.create()
            
            // On transport load.
            set t=CreateTrigger()
            call TriggerAddCondition(t,Filter(function OnLoad))
            call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_LOADED)
            
            // On enter world.
            // Reuse previous trigger:
            call TriggerRegisterEnterRegion(t,g,Filter(function OnEnterWorld))
            
            set t=null
            set g=null
        endmethod
    endstruct
endlibrary
This snippet detects when a unit leaves a transport, and fires an Event. It also contains functions for iterating over all units in a transport unit, and for getting what transport unit a unit has currently boarded.

Example:
JASS:
scope TransportDemo initializer OnInit
    private function OnLoad takes nothing returns boolean
        call BJDebugMsg(GetUnitName(GetTransportUnit())+" loaded "+GetUnitName(GetTriggerUnit()))
        call BJDebugMsg("at ("+R2S(GetUnitX(GetTriggerUnit()))+", "+R2S(GetUnitY(GetTriggerUnit()))+").")
        return false
    endfunction
    
    private function OnUnload takes nothing returns boolean
        call BJDebugMsg(GetUnitName(GetUnloadingTransport())+" unloaded "+GetUnitName(GetUnloadedUnit()))
        call BJDebugMsg("at ("+R2S(GetUnitX(GetUnloadedUnit()))+", "+R2S(GetUnitY(GetUnloadedUnit()))+").")
        return false
    endfunction
    
    private function OnInit takes nothing returns nothing
        local trigger t=CreateTrigger()
        call Transport_RegisterLoadEvent(t)
        call TriggerAddCondition(t,Filter(function OnLoad))
        
        set t=CreateTrigger()
        call Transport_RegisterUnloadEvent(t)
        call TriggerAddCondition(t,Filter(function OnUnload))
    endfunction
endscope

Updates:
- Version 1.02: Fixed Recycle usage (it didn't implement Recycle even if it was available).
- Version 1.01: Fixed the event response stack not decrementing, and added Transport_CountPassengers(transport unit) function.
- Version 1.00: Release.
 

Attachments

  • Transport.w3x
    40.3 KB · Views: 421

Jesus4Lyf

Good Idea™
Reaction score
397
>Does this really work?
Yes. :D

>Is there any kind of delay?
No. :D

It does not use a periodic timer or anything, it's all O(1) complexity and quick and nice to use.

I have tested it for KillUnit while a unit is in transport, it detects it and fires the unload event, same for RemoveUnit.
Also, unlike StatusEvent at this time, pausing a unit that is on a transport and then unloading it will actually fire the Unload event. :p
 

Kenny

Back for now.
Reaction score
202
Awesomes stuff. Efficient as always.

One thing though. Last I heard from Vex, the use of elseif within static ifs still isn't supported.

Found the post. (There have been no updates to static ifs since that post that I am aware of)

Oh and:

I have tested it for KillUnit while a unit is in transport, it detects it and fires the unload event, same for RemoveUnit.
Also, unlike StatusEvent at this time, pausing a unit that is on a transport and then unloading it will actually fire the Unload event.

Impressive. :D
 

Jesus4Lyf

Good Idea™
Reaction score
397
>Awesomes stuff. Efficient as always.
Thanks. :D
One thing though. Last I heard from Vex, the use of elseif within static ifs still isn't supported.
Um. Seems to compile fine. Tried using it with Recycle and GroupUtils, putting syntax errors in the appropriate lines to see if they errored. They did...
 

Kenny

Back for now.
Reaction score
202
Oh awesome. Well then... I have nothing more to say, this seems pretty damn good. Quite handy for RPG type maps that use zeplins and stuff to get around.

+rep if I can.

Grrr, still can't +rep. You will get some soon though.
 

Lyerae

I keep popping up on this site from time to time.
Reaction score
105
Awesome. I'll be using this. +rep

> it's all O(1)
Huh? :nuts:
 

Nestharus

o-o
Reaction score
84
Great resource!


Adding new functionality to getting states of regular warcraft 3 operations is always a major plus ; D (stuff that shoulda been included in JASS but wasn't ;o)
 

Jesus4Lyf

Good Idea™
Reaction score
397
I totally agree, but his is broken. It relies on undefend, and units can be issued undefend orders while in transport, triggering his unload event, and units can be unloaded without having the undefend order issued by being paused, which does not trigger his unload event. Therefore, this method is nicer, imho (I should mention that setting x/y with this for a unit in transport (??) would probably fire the unload event, though).

It's not so much a war as an inconvenient ban. When I find bugs in wc3c systems I have to then make my own, as I can't report them. So whatever.
 
Reaction score
341
It's not so much a war as an inconvenient ban. When I find bugs in wc3c systems I have to then make my own, as I can't report them. So whatever.

Unless it requires a total rewrite of the system, wouldn't it be easier to just ask someone to report the bugs for you?
 

Jesus4Lyf

Good Idea™
Reaction score
397
Unless it requires a total rewrite of the system, wouldn't it be easier to just ask someone to report the bugs for you?
No. Well firstly, this did require a rewrite of the system. Secondly, if I used StatusEvent, I'd need to use AutoIndex, which means no AIDS structs (I could probably make this optionally require AutoIndex instead, btw). Thirdly, it is absolutely faster to write it myself. :)

Edit: Here's an example. There is a JassHelper bug. Has anyone reported that on wc3c for me yet? Lol. I have expressed before that I'd like to contribute on wc3c, but apparently an old personal issue is worth dividing the mapping community.
 
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