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

[JASS] Register/Unregister Unit Event?

Status
Not open for further replies.
So I have this:
JASS:
call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_DEATH)
call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_ORDER)
call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_TARGET_ORDER)
Is it possible to remove caster from DockingSystem_Undock_Trigger without having to destroy the trigger or something? Sort of like a TriggerUnregisterUnitEvent, if you will.

PS: I noticed that the event fires for every single unit that was registered, instead for just the one that was ordered to do something. Is it possible to only make it fire once per unit that is ordered/dies?
 
No, I mean, I could destroy and then recreate it when a new unit is registered, but that would have to be done if there's only 1 registered unit, no? If 4 units are registered and I destroy it after the even fires for one unit, the other 3 won't be able to trigger the events they were registered for. I would have to wait for all 4 units to fire their events, THEN destroy the trigger, given that there are no new units that were added meanwhile.

Alternatively, I could save the units, destroy the trigger, and re-add the 3 others that have not yet fired their events. Sounds pretty unsophisticated, though.
 
PS: I noticed that the event fires for every single unit that was registered, instead for just the one that was ordered to do something. Is it possible to only make it fire once per unit that is ordered/dies?
What do you mean exactly -- it fires X-times onOrder, depending on the amount of registered units?

Sort of like a TriggerUnregisterUnitEvent, if you will.
Nope, though your suggestion with tracking units, inside for example a group, is not bad, and then re-registering them.
You could also have a generel PlayerUnitEvent, and then filter for if trggering unit is simply inside a unit group.
 
What do you mean exactly -- it fires X-times onOrder, depending on the amount of registered units?
Yeah, if I've registered 10 units, the debug message prints 10 times for every ONE unit that was ordered. The effects themselves don't trigger, however, just the debug message, which I find weird. For example, I've set the function to passive transform a knight into a sorceress on order, so when I order one registered unit to move, only that one unit transforms, but the debug message is still printing for as many times as there are registered units.

Nope, though your suggestion with tracking units, inside for example a group, is not bad, and then re-registering them.
You could also have a generel PlayerUnitEvent, and then filter for if trggering unit is simply inside a unit group.
Hmm, I could give OrderEvent a go, tho I'm trying to limit the amount of requirements as much as I can. I'm always using a load of libraries, and that can be off-putting to some people.
 
Are you sure units arent registered multiple times, or so? try demo:
JASS:
scope Demo initializer Init
    private function onOrder takes nothing returns nothing
        if GetObjectName(GetIssuedOrderId()) == "Default string"  then
            call BJDebugMsg(GetUnitName(GetTriggerUnit()) + " -- Order (1) -- " + OrderId2String(GetIssuedOrderId()) + " [" + I2S(GetIssuedOrderId()) + "]")
        else
            call BJDebugMsg(GetUnitName(GetTriggerUnit()) + " -- Order (2) -- " + GetObjectName(GetIssuedOrderId()) + " [" + I2S(GetIssuedOrderId()) + "]")
        endif
    endfunction

    private function onCast takes nothing returns nothing
        call BJDebugMsg(GetUnitName(GetTriggerUnit()) + " -- Spell -- " + GetObjectName(GetSpellAbilityId()) + " [" + I2S(GetSpellAbilityId()) + "]")
    endfunction

    private function Init takes nothing returns nothing
        local unit u
        local integer i
        local trigger t
       
        set t = CreateTrigger()
        call TriggerAddAction(t, function onOrder)
       
        set i = 1
        loop
            exitwhen i > 5
           
            set u = CreateUnit(GetLocalPlayer(), 'Hpal', 0, 0, 0)
           
            call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_ISSUED_ORDER)
            call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_ISSUED_POINT_ORDER)
            call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_SPELL_EFFECT)
           
            set i = i + 1
        endloop
        call PanCameraTo(0, 0)
    endfunction
endscope
it works fine, for me

One don't even need OrderEvent, I just meant something like:

JASS:
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_ORDER )
 
Hmm, your system does indeed seem to print for just the right amount of paladins that are ordered. I'm pretty sure I'm not registering my units multiple times...
JASS:
library DockingSystem initializer DockingSystem_OnCast_Func requires SpellEffectEvent optional BlizzardMessage
   
    /*** If you're getting a duplicate of UnitAlive, you can delete or comment out this one ***/
    native UnitAlive takes unit id returns boolean
   
    globals
        private trigger DockingSystem_Dock_Trigger = null
        private trigger DockingSystem_Undock_Trigger = null
        private integer RegisterCount = 0
        private unit array RegisteredUnit
        private constant integer REGISTER_CLEANUP_LIMIT = 2
        private constant integer MAX_PORTS_HARD_LIMIT = 8
        private constant string ERROR_MESSAGE_NOT_A_STATION = "This structure is not viable."
        private constant string ERROR_MESSAGE_NO_SPACE = "All docking ports are occupied."
    endglobals

   
    function DockingSystem_Undock takes nothing returns boolean
        local unit caster = GetTriggerUnit()
        local unit docking_station = SetDockingPlug[caster].station
        local integer index = SetDockingPlug[caster].port_index
        local integer i = SetDockingPlug[caster].reg_index
        call BJDebugMsg("Undock")
        call UnitRemoveAbility(caster, 'ARal')
        call UnitAddAbility(caster, SetDockingPlug[caster].plug_out_ability)
        call UnitRemoveAbility(caster, SetDockingPlug[caster].plug_out_ability)
        call UnitAddAbility(caster, SetDockingPlug[caster].docking_ability)
        call SetUnitPathing(caster, true)
        set SetDockingPlug[caster].port_index = 0
        set SetDockingPlug[caster].station = null
        set SetDockingPlug[caster].reg_index = 0
        set SetDockingStation[docking_station].is_port_occupied[index] = false
        set SetDockingStation[docking_station].docked_unit[index] = null
        set SetDockingStation[docking_station].number_of_occupied_ports = SetDockingStation[docking_station].number_of_occupied_ports - 1
        //Events
            set udg_DockingEvent_Plug = caster
            set udg_DockingEvent_Station = docking_station
            set udg_DockingEvent_Ports = SetDockingStation[docking_station].number_of_occupied_ports
            set udg_DockingEvent = 2.
            set udg_DockingEvent = 0.
            set udg_DockingEvent_Plug = null
            set udg_DockingEvent_Station = null
        //End Events
        /*call DestroyTrigger(DockingSystem_Undock_Trigger)
        set DockingSystem_Undock_Trigger = CreateTrigger()
        loop
            set RegisteredUnit[i] = RegisteredUnit[i + 1]
            call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, RegisteredUnit[i], EVENT_UNIT_DEATH)
            call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, RegisteredUnit[i], EVENT_UNIT_ISSUED_ORDER)
            call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, RegisteredUnit[i], EVENT_UNIT_ISSUED_POINT_ORDER)
            call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, RegisteredUnit[i], EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerAddCondition(DockingSystem_Undock_Trigger, function DockingSystem_Undock)
            set i = i + 1
            exitwhen i > RegisterCount
        endloop
        //set RegisteredUnit[i] = null
        set RegisterCount = RegisterCount - 1*/
        set caster = null
        return false
    endfunction
   
   
    function DockingSystem_OnCast_Actions takes nothing returns nothing
        local unit caster = GetTriggerUnit()
        local unit target = GetSpellTargetUnit()
        local integer index = GetRandomInt(1, SetDockingStation[target].number_of_ports)
        local integer segments = 0
        local real angle = 0.
        local real newX = 0.
        local real newY = 0.
        if SetDockingStation[target].is_station then
            if SetDockingStation[target].number_of_occupied_ports < SetDockingStation[target].number_of_ports then
                if SetDockingStation[target].is_port_occupied[index] then
                    loop
                        set index = index + 1
                        if index > SetDockingStation[target].number_of_ports then
                            set index = 1
                        endif
                        exitwhen not SetDockingStation[target].is_port_occupied[index] 
                    endloop
                endif
                call UnitAddAbility(caster, SetDockingPlug[caster].plug_in_ability)
                call UnitRemoveAbility(caster, SetDockingPlug[caster].plug_in_ability)
                set angle = 6.28319 / SetDockingStation[target].number_of_ports * index + SetDockingStation[target].angle_offset
                //Atan2(GetUnitY(caster)-GetUnitY(target), GetUnitX(caster)-GetUnitX(target))
                call SetUnitFacing(caster, angle * bj_RADTODEG + SetDockingPlug[caster].facing_angle)
                call SetUnitPathing(caster, false)
                call SetUnitX(caster, GetUnitX(target) + Cos(angle) * SetDockingStation[target].port_distance)
                call SetUnitY(caster, GetUnitY(target) + Sin(angle) * SetDockingStation[target].port_distance)
                call UnitRemoveAbility(caster, 'Amov')
                call UnitAddAbility(caster, 'ARal')
                set SetDockingPlug[caster].station = target
                set SetDockingPlug[caster].port_index = index
                set SetDockingStation[target].is_port_occupied[index] = true
                set SetDockingStation[target].docked_unit[index] = caster
                set SetDockingStation[target].number_of_occupied_ports = SetDockingStation[target].number_of_occupied_ports + 1
                if DockingSystem_Undock_Trigger == null then
                    set DockingSystem_Undock_Trigger = CreateTrigger()
                endif
                call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_DEATH)
                call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_ORDER)
                call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_POINT_ORDER)
                call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_TARGET_ORDER)
                call TriggerAddCondition(DockingSystem_Undock_Trigger, function DockingSystem_Undock)
                //set RegisterCount = RegisterCount + 1
                //set RegisteredUnit[RegisterCount] = caster
                //set SetDockingPlug[caster].reg_index = RegisterCount
                //Events
                    set udg_DockingEvent_Plug = caster
                    set udg_DockingEvent_Station = target
                    set udg_DockingEvent_Ports = SetDockingStation[target].number_of_occupied_ports
                    set udg_DockingEvent = 1.
                    set udg_DockingEvent = 0.
                    set udg_DockingEvent_Plug = null
                    set udg_DockingEvent_Station = null
                //End Events
            else
                static if LIBRARY_BlizzardMessage then
                    call BlizzardMessage(ERROR_MESSAGE_NO_SPACE, "|cffffcc00", 31, GetOwningPlayer(caster))
                endif
            endif
        else
            static if LIBRARY_BlizzardMessage then
                call BlizzardMessage(ERROR_MESSAGE_NOT_A_STATION, "|cffffcc00", 31, GetOwningPlayer(caster))
            endif
        endif
        set caster = null
        set target = null
    endfunction
   
   
   
   
    /*
        STRUCTS ------------------------------------------------------------------------------------------------------------
    */
   
    //STATION
    struct SetDockingStation
        boolean is_station
        integer number_of_ports
        integer number_of_occupied_ports
        integer instance
        real angle_offset
        real port_distance
        unit array docked_unit[MAX_PORTS_HARD_LIMIT]
        boolean array is_port_occupied[MAX_PORTS_HARD_LIMIT]
       
        method destroy takes nothing returns nothing
            local integer iLoop = 0
            set this.number_of_ports = 0
            set this.number_of_occupied_ports = 0
            set this.angle_offset = 0.
            set this.port_distance = 0.
            set this.is_station = false
            loop
                exitwhen iLoop > MAX_PORTS_HARD_LIMIT
                set this.docked_unit[iLoop] = null
                set this.is_port_occupied[iLoop] = false
                set iLoop = iLoop + 1
            endloop
            call this.deallocate()
        endmethod
       
        static method operator [] takes unit u returns thistype
            return thistype(GetUnitUserData(u)).instance
        endmethod
       
        static method create takes unit u, integer ports, real angle, real distance returns thistype
            local thistype this = allocate()
            set thistype(GetUnitUserData(u)).instance = this
            set this.number_of_ports = ports
            set this.number_of_occupied_ports = 0
            set this.angle_offset = angle
            set this.port_distance = distance
            set this.is_station = true
            return this
        endmethod
    endstruct
   
    //PLUG
    struct SetDockingPlug
        boolean is_plug
        integer plug_in_ability
        integer plug_out_ability
        integer docking_ability
        integer instance
        integer port_index
        integer reg_index
        real facing_angle
        unit station
       
        method destroy takes nothing returns nothing
            set this.is_plug = true
            set this.plug_in_ability = 0
            set this.plug_out_ability = 0
            set this.docking_ability = 0
            set this.facing_angle = 0.
            set this.port_index = 0
            set this.station = null
            call this.deallocate()
        endmethod
       
        static method operator [] takes unit u returns thistype
            return thistype(GetUnitUserData(u)).instance
        endmethod
       
        static method create takes unit u, integer pt_plug_in, integer pt_plug_out, integer docking_abil, real facing returns thistype
            local thistype this = allocate()
            set thistype(GetUnitUserData(u)).instance = this
            set this.is_plug = true
            set this.station = null
            set this.plug_in_ability = pt_plug_in
            set this.plug_out_ability = pt_plug_out
            set this.docking_ability = docking_abil
            set this.facing_angle = facing
            set this.port_index = 0
            call UnitAddAbility(u, docking_abil)
            /*if DockingSystem_Dock_Trigger == null then
                set DockingSystem_Dock_Trigger = CreateTrigger()
            endif
            call TriggerRegisterUnitEvent(DockingSystem_Dock_Trigger, u, EVENT_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(DockingSystem_Dock_Trigger, function DockingSystem_OnCast_Check)*/
            return this
        endmethod
    endstruct
   
    //===========================================================================
    function DockingSystem_OnCast_Func takes nothing returns nothing
        call RegisterSpellEffectEvent('dock', function DockingSystem_OnCast_Actions)
        //Copy-paste the above function call and change the first argument to the id any docking ability you may have.
    endfunction
   
endlibrary
 
call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_DEATH)
call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_ORDER)
call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterUnitEvent(DockingSystem_Undock_Trigger, caster, EVENT_UNIT_ISSUED_TARGET_ORDER)
call TriggerAddCondition(DockingSystem_Undock_Trigger, function DockingSystem_Undock)

^this happens on each cast.
So each time the caster will get registered to the trigger.
On top of that, each time the event listener "function DockingSystem_Undoch" gets also registered again to the trigger.
 
Docking the same unit multiple times would register it multiple times, but what I'm getting is that if I dock two different units and order just one of them, the debug message prints twice, once for each unit. The demo you provide only prints for the one unit that was ordered :/

Once a unit it docked, it can't cast. The next order he is given will undock him.
 
Oh... so I should have that line only once like when the trigger is created? :/
You learn new things each day, I suppose.

Also, I do seem to be running into a little problem with destroying trigger > recreating trigger > re-registering units. The whole section that was commented in DockingSystem_Undock out was my attempt to do that.
 
have that line only once like when the trigger is created?
Yes, because else the function simply gets fired as often as it also was registered to the trigger.

destroying trigger > recreating trigger > re-registering units.
There can be a public constant group GROUP.
Each time a unit gets registered, it also gets added to GROUP.
When you wanna "unregister" a unit "u", you make following:

1. Remove u from GROUP
2. Destroy trigger
3. Create Trigger
4. Loop through GROUP and register all enum units again

But, as also mentioned, you might just have "PlayerUnitEvents" registered, so you never have to care aout create/destroy triggers.
Then you just make a condition which checks if triggering unit is inside GROUP or not.
 
Idk about unit groups, ever since I found this weird bug that happens if a unit is removed from the game while still being inside a group, I've just avoided them completely, unless if I'm enumerating and clearing right after. Since I'm already using a unit indexer in that system, I guess I can just use a boolean array as check for orders.
 
ever since I found this weird bug that happens if a unit is removed from the game while still being inside a group
As far as I know, you only have to worry about the issue if you decide to use FirstOfGroup() to enumerate through a unit group that stores units over time. Given that a removed unit would be null, the loop can end prematurely. This wouldn't be an issue if you used a custom alternative (which I think @Aniki did) or used ForGroup() to enumerate through it.
 
I was trying a method that I used before that just counts the global number of units that use a thing, and whenever one of those units is removed from the thing, I loop through them all and do set Thing[iLoop] = Thing[iLoop + 1], but it wasn't working when I re-registered the Thing[iLoop] to the newly-created trigger. Not all of them were getting registered.

I've never actually used ForGroup() in JASS, only ever used the GUI version. I suppose I could give it a try, since the ForGroup() won't be called every X seconds like the stuff I usually make.

PS: do you have a link to that custom alternative you mentionned?
 
Status
Not open for further replies.
Top