• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

GUI Unit Event (now with a state-of-the-art Lua version)

Unit Event for GUI gives you access to all kinds of events which normal GUI events can't do:

FunctionLuavJass
Subject unit of the eventUnitEvent_unitUDexUnits[UDex]
Event index (for use within an array).UnitEvent_indexUDex
Data attachment to unitSet UnitEvent_setKey = unit
Set MyArray[UnitEvent_getKey] = data
Set MyArray2[UnitEvent_getKey] = data2
Set TempInt = (Custom value of unit)
Set MyArray[TempInt] = data
Set MyArray2[TempInt] = data2
Data retrieval from unitSet UnitEvent_setKey = unit
Set data = MyArray[UnitEvent_getKey]
Set data2 = MyArray2[UnitEvent_getKey]
Set TempInt = (Custom value of unit)
Set data = MyArray[TempInt]
Set data2 = MyArray2[TempInt]
Manually created in World Editor
Unit who summoned the subject unit
Know the previous type in a transform event
Is the unit reincarnating
Unit who is carrying the subject unit
All units carried by the unit
Detect the unit's killer in a death event
UnitEvent_preplaced
UnitEvent_summoner
UnitEvent_unitType
UnitEvent_reincarnating
UnitEvent_transporter
UnitEvent_cargo
(Killing unit)
IsUnitPreplaced
SummonerOfUnit
UnitTypeOf
IsUnitReincarnating
CargoTransportUnit
CargoTransportGroup
KillerOfUnit
Starts existing
Fully created
Stops existing
Starts reincarnating
Resurrects, reanimated or finishes reincarnating
Dies
Loaded into a transport
Unloaded from a transport
Transforms into a new unit
Is playable (alive/not loaded)
Is unplayable (dead/loaded)
OnUnitIndexed
OnUnitCreation
OnUnitRemoval
OnUnitReincarnating
OnUnitRevival
OnUnitDeath
OnUnitLoaded
OnUnitUnloaded
OnUnitTransform
OnUnitActive
OnUnitPassive
UnitIndexEvent == 1.00
UnitIndexEvent == 1.50
UnitIndexEvent == 2.00
DeathEvent == 0.50
DeathEvent == 2.00
DeathEvent == 1.00
CargoEvent == 1.00
CargoEvent == 2.00
UnitTypeEvent == 1.00
UnitInActionEvent == 1.00
UnitInActionEvent == 2.00

You can distinguish between types of unit revival with three conditions:
1) If it is summoned, that means it was reanimated (e.g. "Animate Dead (Death Knight Ultimate)").
2) If UnitEvent_reincarnating (Lua version) / IsUnitReincarnating (JASS version) is true, that means it finished reincarnating (e.g. "Reincarnation (Tauren Chieftan Ultimate)").
3) If the above are False, the unit was resurrected (e.g. "Ressurrection (Paladin Ultimate)").

Lua:
OnGlobalInit(function()
    
    Require "Timed"                         --https://www.hiveworkshop.com/threads/timed-call-and-echo.339222/
    Require "AddHook"                       --https://www.hiveworkshop.com/threads/hook.339153
    Require "GlobalRemap"                   --https://www.hiveworkshop.com/threads/global-variable-remapper.339308
    Require "RegisterAnyPlayerUnitEvent"    --https://www.hiveworkshop.com/threads/collection-gui-repair-kit.317084/
    Require "CreateEvent"                   --https://www.hiveworkshop.com/threads/event-gui-friendly.339451/
    Require "PreciseWait"                   --https://www.hiveworkshop.com/threads/precise-wait-gui-friendly.316960/
--[[
Lua Unit Event 1.2.0.2

In addition to the existing benefits enjoyed over the past years, this Lua version supports linked events that allow
your trigger to Wait until another event runs (meaning you can do attachment and cleanup from one trigger).

Variable names have been completely changed from all prior Unit Event incarnations.
> All real variable event names are now prefixed with OnUnit...
> All array references (unit properties) are now prefixed with UnitEvent_
> Lua users can access a unit's properties via UnitEvent[unit].property (e.g. reincarnating/cargo)
> Lua users can easily add a readonly GUI property to a unit via UnitEvent.addProperty("propertyName")
>>> GUI accesses it via UnitEvent_propertyName, the second is readable and writable within Lua via UnitEvent[unit].propertyName
> UnitUserData (custom value of unit) has been completely removed. This is the first unit event/indexer to not use UnitUserData nor hashtables.
>>> UnitEvent_unit is the subject unit of the event.
>>> UnitEvent_index is an integer in GUI, but points to a the unit.
>>> UnitEvent_setKey lets you assign a unit to the key.
>>> UnitEvent_getKey is an integer in GUI, but points to the unit you assigned as the key.
>>>>> Lua doesn't care about array max sizes, nor the type of information used as an index in that array (because it uses tables and not arrays).
>>>>> GUI is over 20 years old and can easily be fooled. As long as the variable is defined with the correct type, it doesn't care what happens to that variable behind the scenes.
--]]
    UnitEvent={}

    local _REMOVE_ABIL      = FourCC('A001')
    local _TRANSFORM_ABIL   = FourCC('A002') --be sure to assign these to their respective abilities if you prefer not to initialize via GUI
    
    --Un-comment this next line if you want to use the custom value of units functionality (for backwards compatibility):
    --GetUnitUserData = function(unit) return unit end

--[[
    Full list of GUI variables:
    real    udg_OnUnitIndexed
    real    udg_OnUnitCreation
    real    udg_OnUnitRemoval
    real    udg_OnUnitReincarnating
    real    udg_OnUnitRevival
    real    udg_OnUnitLoaded
    real    udg_OnUnitUnloaded
    real    udg_OnUnitTransform
    real    udg_OnUnitDeath
    real    udg_OnUnitActive
    real    udg_OnUnitPassive

    ability udg_DetectRemoveAbility
    ability udg_DetectTransformAbility

    unit    udg_UnitEvent_unit
    integer udg_UnitEvent_index

    unit    udg_UnitEvent_setKey
    integer udg_UnitEvent_getKey

    boolean   array udg_UnitEvent_preplaced
    unit      array udg_UnitEvent_summoner
    unittype  array udg_UnitEvent_unitType
    boolean   array udg_UnitEvent_reincarnating
    unit      array udg_UnitEvent_transporter
    unitgroup array udg_UnitEvent_cargo
--]]

    local eventList={}
    local udg_prefix="udg_OnUnit"
    local makeAPI = function(name)
        UnitEvent["on"..name],
        eventList["on"..name] = CreateEvent(udg_prefix..name, true)
    end
    local unitIndices={} ---@type UnitEventTable[]

    --onIndexed and onCreation occur at roughly the same time, but the unit's creation should be used instead as it will have more data.
    --Combined, they are the counterparts to onRemoval.
    makeAPI("Indexed")
    makeAPI("Creation")
    makeAPI("Removal")

    --counterparts (though revival doesn't only come from reincarnation):
    makeAPI("Reincarnating")
    makeAPI("Revival")
    
    --perfect counterparts:
    makeAPI("Loaded")
    makeAPI("Unloaded")
    
    --stand-alone events:
    makeAPI("Transform")
    makeAPI("Death")
    
    --perfect counterparts that generalize all but the "transform" event:
    makeAPI("Active")
    makeAPI("Passive")

    --Used to get the UnitEvent table from the unit to detect UnitEvent-specific properties.
    UnitEvent.__index = function(_, unit) return unitIndices[unit] end
    
    ---@param name string
    UnitEvent.addProperty = function(name)
        GlobalRemapArray("udg_UnitEvent_"..name, function(unit) return unitIndices[unit][name] end)
    end

    ---@class UnitEventTable : table
    ---@field unit          unit
    ---@field preplaced     boolean
    ---@field summoner      unit
    ---@field transporter   unit
    ---@field cargo         group
    ---@field reincarnating boolean
    ---@field private new boolean
    ---@field private alive boolean
    ---@field private unloading boolean
    
    --The below two variables are intended for GUI typecasting, because you can't use a unit as an array index.
    --What it does is bend the rules of GUI (which is still bound by strict JASS types) by transforming those
    --variables with Global Variable Remapper (which isn't restricted by any types).
    --"setKey" is write-only (assigns the key to a unit)
    --"getKey" is read-only (retrieves the key and tells GUI that it's an integer, allowing it to be used as an array index)
    local lastUnit
    GlobalRemap("udg_UnitEvent_setKey", nil, function(unit)lastUnit=unit end) --assign to a unit to unlock the getKey variable.
    GlobalRemap("udg_UnitEvent_getKey",      function() return lastUnit  end) --type is "integer" in GUI but remains a unit in Lua.

    local runEvent
    do
        local eventUnit
        local getEventUnit  = function() return eventUnit end
        runEvent            = function(event, unitTable)
            local cached    = eventUnit
            eventUnit       = unitTable.unit
            eventList[event](unitTable)
            eventUnit       = cached
        end
        GlobalRemap("udg_UnitEvent_unit",  getEventUnit) --the subject unit for the event.
        GlobalRemap("udg_UnitEvent_index", getEventUnit) --fools GUI into thinking unit is an integer
    end
    --add a bunch of read-only arrays to access GUI data. I've removed the "IsUnitAlive" array as the GUI living checks are fixed with the GUI Enhancer Colleciton.
    UnitEvent.addProperty("preplaced")
    UnitEvent.addProperty("unitType")
    UnitEvent.addProperty("reincarnating")
    UnitEvent.addProperty("transporter")
    UnitEvent.addProperty("summoner")
    
    if rawget(_G, "udg_UnitEvent_cargo") then
        UnitEvent.addProperty("cargo")
    end
    
    --Flag a unit as being able to move or attack on its own:
    local function setActive(unitTable)
        if unitTable and not unitTable.active and UnitAlive(unitTable.unit) then --be sure not to run the event when corpses are created/unloaded.
            unitTable.active = true
            runEvent("onActive", unitTable)
        end
    end
    ---Flag a unit as NOT being able to move or attack on its own:
    local function setPassive(unitTable)
        if unitTable and unitTable.active then
            unitTable.active = nil
            runEvent("onPassive", unitTable)
        end
    end
    local function getFunc(active)
        return function(unitTable) (active and setActive or setPassive)(unitTable) end
    end

    UnitEvent.onCreation(getFunc(true), 2, true)
    UnitEvent.onUnloaded(getFunc(true), 2, true)
    UnitEvent.onRevival(getFunc(true), 2, true)
    
    UnitEvent.onLoaded(getFunc(), 2, true)
    UnitEvent.onReincarnating(getFunc(), 2, true)
    UnitEvent.onDeath(getFunc(), 2, true)
    UnitEvent.onRemoval(getFunc(), 2, true)
    
    --UnitEvent.onIndex(function(unitTable) print(tostring(unitTable.unit).."/"..GetUnitName(unitTable.unit).." has been indexed.") end)
    
    setmetatable(UnitEvent, UnitEvent)

    --Wait until GUI triggers and events have been initialized. 
    OnTrigInit(function()
        if rawget(_G, "Trig_Unit_Event_Config_Actions") then
            Trig_Unit_Event_Config_Actions()
            _REMOVE_ABIL    = udg_DetectRemoveAbility    or _REMOVE_ABIL
            _TRANSFORM_ABIL = udg_DetectTransformAbility or _TRANSFORM_ABIL
        end
        local function checkAfter(unitTable)
            if not unitTable.checking then
                unitTable.checking              = true
                Timed.call(0, function()
                    unitTable.checking          = nil
                    if unitTable.new then
                        unitTable.new           = nil
                        runEvent("onCreation", unitTable) --thanks to Spellbound for the idea
                    elseif unitTable.transforming then
                        local unit = unitTable.unit
                        runEvent("onTransform", unitTable)
                        unitTable.unitType = GetUnitTypeId(unit) --Set this afterward to give the user extra reference

                        --Reset the transforming flags so that subsequent transformations can be detected.
                        unitTable.transforming  = nil
                        UnitAddAbility(unit, _TRANSFORM_ABIL)
                    elseif unitTable.alive then
                        unitTable.reincarnating = true
                        unitTable.alive         = false
                        runEvent("onReincarnating", unitTable)
                    elseif UnitAlive(unitTable.unit) then
                        unitTable.alive = true
                        runEvent("onRevival", unitTable)
                        unitTable.reincarnating = false
                    end
                end)
            end
        end
    
        local re = CreateRegion()
        local r = GetWorldBounds()
        local maxX, maxY = GetRectMaxX(r), GetRectMaxY(r)
        RegionAddRect(re, r); RemoveRect(r)
        
        local function unloadUnit(unitTable)
            local unit, transport       = unitTable.unit, unitTable.transporter
            GroupRemoveUnit(unitIndices[transport].cargo, unit)
            unitTable.unloading         = true
            runEvent("onUnloaded", unitTable)
            unitTable.unloading         = nil
            if not IsUnitLoaded(unit) or not UnitAlive(transport) or GetUnitTypeId(transport) == 0 then
                unitTable.transporter   = nil
            end
        end
        
        local preplaced = true
        local onEnter = Filter(
        function()
            local unit = GetFilterUnit()
            local unitTable = unitIndices[unit]
            if not unitTable then
                unitTable = {
                    unit    = unit,
                    new     = true,
                    alive   = true,
                    unitType= GetUnitTypeId(unit)
                }
                UnitAddAbility(unit, _REMOVE_ABIL)
                UnitMakeAbilityPermanent(unit, true, _REMOVE_ABIL)
                UnitAddAbility(unit, _TRANSFORM_ABIL)

                unitIndices[unit] = unitTable

                unitTable.preplaced = preplaced
                runEvent("onIndexed", unitTable)
                
                checkAfter(unitTable)
            elseif unitTable.transporter and not IsUnitLoaded(unit) then
                --the unit was dead, but has re-entered the map (e.g. unloaded from meat wagon)
                unloadUnit(unitTable)
            end
        end)
        TriggerRegisterEnterRegion(CreateTrigger(), re, onEnter)
        
        RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_LOADED,
        function()
            local unit = GetTriggerUnit()
            local unitTable = unitIndices[unit]
            if unitTable then
                if unitTable.transporter then
                    unloadUnit(unitTable)
                end
                --Loaded corpses do not issue an order when unloaded, therefore must
                --use the enter-region event method taken from Jesus4Lyf's Transport: https://www.thehelper.net/threads/transport-enter-leave-detection.126051/
                if not unitTable.alive then
                    SetUnitX(unit, maxX)
                    SetUnitY(unit, maxY)
                end
                local transporter = GetTransportUnit()
                unitTable.transporter = transporter
                local g = unitIndices[transporter].cargo
                if not g then
                    g=CreateGroup()
                    unitIndices[transporter].cargo = g
                end
                GroupAddUnit(g, unit)
                
                runEvent("onLoaded", unitTable)
            end
        end)
        
        RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH,
        function()
            local unitTable = unitIndices[GetTriggerUnit()]
            if unitTable then
                unitTable.alive = false
                runEvent("onDeath", unitTable)
                if unitTable.transporter then
                    unloadUnit(unitTable)
                end
            end
        end)
        
        RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON,
        function()
            local unitTable = unitIndices[GetTriggerUnit()]
            if unitTable.new then
                unitTable.summoner = GetSummoningUnit()
            end
        end)
        
        local orderB = Filter(
        function()
            local unit = GetFilterUnit()
            local unitTable = unitIndices[unit]
            if unitTable then
                if GetUnitAbilityLevel(unit, _REMOVE_ABIL) == 0 then

                    runEvent("onRemoval", unitTable)
                    unitIndices[unit] = nil
                    unitTable.cargo = nil
                elseif not unitTable.alive then
                    if UnitAlive(unit) then
                        checkAfter(unitTable)
                    end
                elseif not UnitAlive(unit) then
                    if unitTable.new then
                        --This unit was created as a corpse.
                        unitTable.alive = false
                        runEvent("onDeath", unitTable)

                    elseif not unitTable.transporter or not IsUnitType(unit, UNIT_TYPE_HERO) then
                        --The unit may have just started reincarnating.
                        checkAfter(unitTable)
                    end
                elseif GetUnitAbilityLevel(unit, _TRANSFORM_ABIL) == 0 and not unitTable.transforming then
                    unitTable.transforming = true
                    checkAfter(unitTable)
                end
                if unitTable.transporter and not unitTable.unloading and not (IsUnitLoaded(unit) and UnitAlive(unit)) then
                    unloadUnit(unitTable)
                end
            end
        end)
        
        local p
        local order = CreateTrigger()
        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            p = Player(i)
            GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, onEnter)
            SetPlayerAbilityAvailable(p, _REMOVE_ABIL, false)
            SetPlayerAbilityAvailable(p, _TRANSFORM_ABIL, false)
            TriggerRegisterPlayerUnitEvent(order, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, orderB)
        end
        preplaced = false
    end)
end)

How to install:

- Delete Unit Indexer from your map (if you already have it)
- Copy the "Unit Event Ability" ability from Object Editor
- Copy the Unit Event trigger category
- Paste the Unit Event Ability from object editor twice
- Set the DetectRemoveAbility variable in the Unit Event Config trigger to one of the abilities.
- Set the DetectTransformAbility variable in the same trigger to the other of the two.


Inspiration:

- AutoEvents by grim001 (was hosted on the now-defunct wc3c.net)
- UnitEvent by Nestharus: https://github.com/nestharus/JASS/blob/master/jass/Systems/Unit%20Event/script.j
- Transport by Jesus4Lyf: https://www.thehelper.net/threads/transport-enter-leave-detection.126051/

nestharus, grim001, unitevent, autoevents, unit indexer, reincarnate, reincarnation, reanimation, resurrect, resurrection, detect, death, removal, load, unload, transport, transform, unit type
Contents

Lua Unit Event Test Map (Map)

Unit Event 2.5.3.1 (Map)

Reviews
Approved. Makes easy to detect many events that do not exist in the editor. Very useful system.

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Knockback 2.5D is as JASSified as it gets, already, though HealEvent is pure GUI so I'd like to leave it that way.

The removal of a timer means increased performance for meat wagons with loaded corpses and paused units in transit.

Unit Indexer is one trigger, and I'd like to keep it that way to make importing of systems which need it a breeze.
 
When using call TriggerRegisterVariableEvent( gg_trg_onCargo_Event, "udg_CargoEvent", EQUAL, 1.00 ), both udg_CargoTransportUnit[udg_UDex] and udg_UDexUnits[udg_UDex] use the same index. What index does udg_UDex refer to specifically? I assume it's for the loading unit and not the transporter, in which case, should I just do GetUnitUserData(udg_CargoTransportUnit[udg_UDex]) to get the index of the transport or is there just a handy variable I can use instead? Not an issue, just curious.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I'm not sure I understand your question, so I'll explain my method and hope it answers it:

CargoTransportUnit - each unit has either 0 or 1 transports holding it. So I map the transporting unit to those units who can be carried.

CargoTransportGroup - each transport has cargo or doesn't. The group is therefore mapped to the transport unit.

So if you have a unit being transported, you use CargoTransportUnit[(Custom value of transportedUnit)] to get the carrying unit.

If you want to enumerate the units within that transporter, you use CargoTransportGroup[(Custom value of transportingUnit)].

I am concerned about simplifying it further by mapping the group to each carried unit as well, in the event a transport is loaded into a transport (though I'm unsure if this is even possible).
 
EDIT: checking for unit ID did it, nevermind me. Very, very helpful system.


So I have another question. It's about the transformation event. In the demo map, you use this as an example:
  • On Transform
    • Events
      • Game - UnitTypeEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Game - Display to (All players) the text: ((Name of UDexUnits[UDex]) + has transformed!)
When I test it, both on bearform and unbearform, the message displayed is Druid of the Claw has transformed. I'm trying to use this event to detect when a unit transforms and give it spells and trigger some stuff depending on whether it's in the normal or alternate form, but how would I differentiate between the unit id of the transform variants if it always returns the normal unit form all the time?

(PS: sorry if the answer is easy to figure out, I haven't had time to test it and I need to afk right now)
 
Last edited:
Level 18
Joined
Nov 21, 2012
Messages
835
I am using your indexer (now Unit Event) for a very long time. It is a "must" system for me. Thanks Bribe :piru:
Today I encountered a bug: custom values for units are zero edit at the moment when I attach data to units using custom values. It also may happend for other half jass'ers half gui'ers like me :)

So I have some jass code in trigger "Walls"
JASS:
function InitTrig_Walls takes nothing returns nothing
  set wallShadowGroup = CreateGroup()
  set wallGroup = CreateGroup()
  call BJDebugMsg("3..")
  call Walls_CreateShadowWalls()
  call BJDebugMsg("4..")
  call Walls_CreateTask(2)
  call BJDebugMsg("5..")
endfunction
In function Walls_CreateShadowWalls() I'm using GetUnitUserData but values are 0 [del]in game[/del] edit at the moment when I attach data to units. That's why it happends:
JASS:
function InitCustomTriggers takes nothing returns nothing
  call InitTrig_Unit_Event_Config()
  call InitTrig_Unit_Event()
  call InitTrig_Walls()
  call InitTrig_Z1order()
endfunction

//=========================================
function RunInitializationTriggers takes nothing returns nothing
  call ConditionalTriggerExecute(gg_trg_Unit_Event_Config)
  call ConditionalTriggerExecute(gg_trg_Walls)
endfunction

//=========================================
function main takes nothing returns nothing
  call SetCameraBounds(- 3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), - 3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), - 3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), - 3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM))
  call SetDayNightModels("Environment\\DNC\\DNCDalaran\\DNCDalaranTerrain\\DNCDalaranTerrain.mdl", "Environment\\DNC\\DNCDalaran\\DNCDalaranUnit\\DNCDalaranUnit.mdl")
  call NewSoundEnvironment("Default")
  call SetAmbientDaySound("DalaranDay")
  call SetAmbientNightSound("DalaranNight")
  call SetMapMusic("Music", true, 0)
  call CreateRegions()
  call CreateAllItems()
  call CreateAllUnits()
  call InitBlizzard()


  call InitGlobals()
  call InitCustomTriggers()
  call RunInitializationTriggers()

endfunction
now it's clear that my "Walls" trigger (inside InitCustomTriggers) runs before call ConditionalTriggerExecute(gg_trg_Unit_Event_Config) (inside RunInitializationTriggers) that's why custom values are 0.

If I may suggest a solution:
1.remove settings and exec func from trigger "Unit Event Config"
2.ask user to set 2 abilities inside Variables Table:
unitEvent1.png


3.run call UnitEventInit() inside function InitTrig_Unit_Event
 
Last edited:
Level 18
Joined
Nov 21, 2012
Messages
835
I described problem bad: custom values are zero but at the moment when I attach data to units like set myInt[GetUnitUserData(u)] = 10 becouse I did it in InitTrig_ function which runs before Unit Event (call ConditionalTriggerExecute(gg_trg_Unit_Event_Config))

InitCustomTriggers, RunInitializationTriggers, main functions are made by compiler not by me
 
Level 18
Joined
Nov 21, 2012
Messages
835
hey Bribe

To detect when any unit is created, use the event "Game - UnitIndexEvent becomes Equal to 1.00".

Is this possible that you add functionality to this event to work also on pre-placed units?
so it fires at map init or 0sec of the game,
it will save me a lot of work, cause I have to additionally GroupEnumUnits and make whatever on them because UnitIndexEvent does not work for preplaced units
thanks :)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I checked to make sure this system still waits until the Map Initialization phase to run and yes, it does.

You just need to make sure you have the event added to your trigger before that phase.

If your trigger is a standard GUI one, then including the event UnitIndexEvent == 1.00 is all that's needed.

However, if you're adding the event to a trigger after a delay, you're creating a new problem which the system in its current state can't account for.
 
Speaking of pre-placed units, I'd like to pick your brains on one thing: is it possible to add two new UnitIndexEvent == # because while a value of 1.00 detects both pre-placed and units that appear later in the game, when it comes to buildings, it will index them straight away as they begin their building cycle. To circumvent that I have an onIndexMapStart trigger, an onIndex trigger, an onBuildFinish trigger, and a fourth trigger to switch the first two triggers on and off. onIndex is initially off, and onIndexMapStart is turned off after 1 second of game time, after which onIndex is turned on. If there was a UnitIndexEvent == 1.1 for pre-placed units and UnitIndexEvent == 1.2 that ignores pre-placed units, I could use both values without the need to switch things on and off.

Thoughts?
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
UnitIndexEvent does not work for preplaced units
The standalone version of Bribe's Index system does that with this:
Use the event - "Game - UnitIndexEvent Equal to 3.00" instead of "Map Initialization" if you want to make sure all of your units have custom value when used in that trigger's actions.
 
Level 18
Joined
Nov 21, 2012
Messages
835
I made bad change in system, by running UnitEventInit to early (at function InitTrig_Unit_Event) cause I wanted to have custom values have been set at Init_Trig_XX stages, my mistake.
Event works fine when I came back to System original form, thanks for clarification

Bribe
UnitIndexEvent runs always before triggers with "Map Initialization" event

  • Index1
    • Events
      • Game - UnitIndexEvent becomes Equal to 1.00
you know maybe is there a way to run trigger in GUI format before "UnitIndexEvent becomes Equal to 1.00" trigger runs?
I need it for configuration trigger for user, this config GUI trigger writes data to hashtable
and "UnitIndexEvent becomes Equal to 1.00" trigger check those data.
 
Last edited:
Level 18
Joined
Nov 21, 2012
Messages
835
@ZiB: KILLCIDE's reply above your post answers that question.
are you sure??

I need to run "Config_Trigger" and then after that "Enters_Trigger".

"Config_Trigger" is a trigger with UnitIndexEvent==3 event
"Enters_Trigger" is a trigger with UnitIndexEvent==1 event
and triggers runs in the following order: "Enters_Trigger" --> "Config_Trigger" = bad
(UnitIndexEvent==3 will run always after UnitIndexEvent==1 event)

I changed event in "Config_Trigger" with MapInitialization event, and now it depends on where "Config_Trigger" is placed:
  • if above "Unit Event Config" then they run in correct order: "Config_Trigger" --> "Enters_Trigger" = OK
  • if "Config_Trigger" is below "Unit Event Config" they run in wrong order
So I have to ask users to place system I'm making above UnitEvent category. I'll be glad if you give me alternative. Is there is not, thats also fine.
 
It would only take 2 lines of code to add the IsUnitPreplaced native. Maybe 1. But what is the use in discriminating this way?
Mostly just so I don't have code repeating (and for buildings, tho I guess I would be better served with a native that can tell if the building is under construction or not), but it's not really a big deal. I was mostly curious about whether it was a good idea or not.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
are you sure??

I need to run "Config_Trigger" and then after that "Enters_Trigger".

"Config_Trigger" is a trigger with UnitIndexEvent==3 event
"Enters_Trigger" is a trigger with UnitIndexEvent==1 event
and triggers runs in the following order: "Enters_Trigger" --> "Config_Trigger" = bad
(UnitIndexEvent==3 will run always after UnitIndexEvent==1 event)

I changed event in "Config_Trigger" with MapInitialization event, and now it depends on where "Config_Trigger" is placed:
  • if above "Unit Event Config" then they run in correct order: "Config_Trigger" --> "Enters_Trigger" = OK
  • if "Config_Trigger" is below "Unit Event Config" they run in wrong order
So I have to ask users to place system I'm making above UnitEvent category. I'll be glad if you give me alternative. Is there is not, thats also fine.


Show me your trigger and I'll see if i can make it possible for it to work without messing with Unit Event.

If i can't, I'll hook in my dead laptop and add an extra event.
 
Level 18
Joined
Nov 21, 2012
Messages
835
  • config1
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- I need this trigger runs BEFORE any triggers with UnitIndexEvent==1 --------
      • -------- this is for end-user who make some configurations here --------
      • -------- these configuration will be used in Index1 trigger --------
      • -------- ---------- --------
      • Set testUnitType = Footman
      • -------- ---------- --------
      • Game - Display to (All players) for 30.00 seconds the text: Configurations DONE
      • -------- ---------- --------
      • -------- if you move this trigger ABOVE "Unit Event Config" it will work as I need --------
      • -------- (after move please save map, close/open/save. Sometimes Editor mess up queue when writing map) --------
      • -------- ---------- --------
  • Index1
    • Events
      • Game - UnitIndexEvent becomes Equal to 1.00
    • Conditions
      • (Unit-type of UDexUnits[UDex]) Equal to testUnitType
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: (Unit Enters Map : + (Name of UDexUnits[UDex]))
      • -------- -------------- --------
      • -------- this trigger will be used in system Im working on (not available for end-user) --------
      • -------- -------------- --------
      • -------- this will fire for pre-placed Footman only when config1 ran before this trigger --------
      • -------- -------------- --------
      • -------- -------------- --------
catalogtree1.jpg


OK:
catalogTreeOK.jpg
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
The best way is to merge the triggers, but in the future I'll add a pre-Index event to avoid that :

  • Trigger
    • Events
      • UnitIndexEvent == 1.00
    • Conditions
    • Actions
      • If
        • HasStarted Equal to True
      • Then
        • If
          • (Unit-type of UDexUnits[UDex]) Equal to testUnitType
        • Then
          • -------- Do your stuff
        • Else
      • Else
        • Set HasStarted = True
        • Set testUnitType = Dude
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Yes, this does everything Unit Indexer does. This only adds features.

Please let me know if there's any features anyone needs from my systems in case some of them might be possible to implement.

I'm currently under the impression it all works the way it should except for Knockback 2.5D which is basically a neverending project which I had to force myself to back away from.
 
Level 24
Joined
Feb 9, 2009
Messages
1,787
How do you handle Loops?
like setting with 3 loops:

Set Tempkeyloop[(Integer A)] = Tempkey
Unit - Create 1 Orbit unit for (Owner of (Target unit of ability being cast)) at tempp2 facing Default building facing degrees

doing so only makes one unit
 
How do you handle Loops?
like setting with 3 loops:

Set Tempkeyloop[(Integer A)] = Tempkey
Unit - Create 1 Orbit unit for (Owner of (Target unit of ability being cast)) at tempp2 facing Default building facing degrees

doing so only makes one unit

It seems that the problem presented does not correspond to the thread at hand.

Don't post the problem here, doing so is an act of hi(gh)-jacking someone else's thread.
Post the problem in the Triggers and Scripts section.
 
Level 24
Joined
Feb 9, 2009
Messages
1,787
Like so
  • Set Tempkey = (Custom value of (Target unit of ability being cast))
  • For each (Integer A) from 1 to 3, do (Actions)
    • Loop - Actions
      • Set OrbitUnitLoop[Tempkey] = (Integer A)
      • Set tempp1 = (Target point of ability being cast)
      • Set tempp2 = (tempp1 offset by 50.00 towards ((120.00 x (Real((Integer A)))) + Orbit_Distance[Tempkey]) degrees)
      • Unit - Create 1 Orbit unit for (Owner of (Target unit of ability being cast)) at tempp2 facing Default building facing degrees
      • Set OrbitUnit[OrbitUnitLoop[Tempkey]] = (Last created unit)
      • Custom script: call RemoveLocation(udg_tempp2)
      • Custom script: call RemoveLocation(udg_tempp1)

So is this the proper way to label a unit through "For each integer loop"?
 
Assuming Tempkey is 5:

For each bj_forLoopAIndex from... {1}
set bj_forLoopAIndex = 1

to {3}
JASS:
loop
    exitwhen bj_forLoopAIndex > 3

Not exact code, no BJ's present.

JASS:
    udg_OrbitUnitLoop[5] = 1
    udg_tempp1 = GetSpellTargetLocation()
    udg_tempp2 = Location(GetLocationX(udg_tempp1) + 50*Cos((120*I2R(1) + udg_Orbit_Distance[5]) * bj_DEGTORAD), GetLocationY(udg_tempp1) + 50*Sin((120*I2R(1) + udg_Orbit_Distance[5]) * bj_DEGTORAD))
    bj_lastCreatedUnit = CreateUnitAtLoc(GetOwningPlayer(GetSpellTargetUnit()), '<orbit_unit_rawcode>', udg_tempp2 ,bj_UNIT_FACING)
    udg_OrbitUnit[1] = bj_lastCreatedUnit
    RemoveLocation(udg_tempp1)
    RemoveLocation(udg_tempp2)
endloop

It is quite possible to override the value of the Orbit Unit

Tempkey: 10

JASS:
    udg_OrbitUnitLoop[10] = 1
    udg_tempp1 = GetSpellTargetLocation()
    udg_tempp2 = Location(GetLocationX(udg_tempp1) + 50*Cos((120*I2R(1) + udg_Orbit_Distance[10]) * bj_DEGTORAD), GetLocationY(udg_tempp1) + 50*Sin((120*I2R(1) + udg_Orbit_Distance[10]) * bj_DEGTORAD))
    bj_lastCreatedUnit = CreateUnitAtLoc(GetOwningPlayer(GetSpellTargetUnit()), '<orbit_unit_rawcode>', udg_tempp2 ,bj_UNIT_FACING)
    udg_OrbitUnit[1] = bj_lastCreatedUnit
    // New value for udg_OrbitUnit[1], unwanted behavior...
    RemoveLocation(udg_tempp1)
    RemoveLocation(udg_tempp2)

The indices used by the variable OrbitUnit are just 1, 2, and 3. This will encounter collisions.

Thus, it is not the proper way to label a unit.
What you could do instead is something like this.

(TempKey: 3)

From
JASS:
    udg_OrbitUnit[1] = bj_lastCreatedUnit

To
JASS:
    udg_OrbitUnit[3*TempKey + bj_forLoopIndexA - 1] = bj_lastCreatedUnit
 
Top