Ultima Online Enhanced Client Walkthrough – Targeting: ID Codes: objectID

Arroth_ThaielArroth_Thaiel Posts: 1,031
edited August 2022 in General Discussions

Introduction

Many players are familiar with the concept that each item in UO has a unique number that separates that item from all other items in the game. For instance, two piles of gold will have unique numbers, so that the game can tell “Hey, this is pile of gold 1 here and that other gold pile over there is pile 2”. These numbers are referred to in UO’s code as “objectID” (most of the time).

However, items have more numerical codes associated with them than objectID. In the EC, there are dedicated tools (functions) for obtaining an item’s graphic number (typeID tool) and an item’s color number (hueID tool), but there are not specific tools for obtaining objectID or other ID codes. There are many ways to obtain these codes in the EC, just not functions in the Actions list for doing so.

--A caveat here, “typeID” is a less than ideal reference to the graphic number, as “type” is also associated with a different numerical ID.

Different Methods of Targeting via objectID

So, why would you want to know an item’s objectID? The most likely answer is to target and interact with a specific object and no others. Within the EC there are two general paths to target and manipulate an item via objectID:

1.      Calling a cursor and then using a stored cursor target.

2.      Targeting a stored item and using that stored item.

These paths may seem similar, but the coding is different. The two methods can also accomplish separate tasks. Additionally, the second method uses simpler functionality, making it slightly faster. Let’s look at some examples to understand the differences.

Cursor Target Stored (Stored Object won’t work):
Let’s say you to want to repair an item. You can write a macro to hit the repair button, wait for the targeting cursor to appear, and then target a Cursor Target Stored object. This would work and the object would be repaired. However, if you wrote the macro to hit the repair button, then target a Stored Object, when you ran the macro the cursor would appear, the object would be targeted, and nothing else would happen. That is because the macro isn’t being told to execute on Stored Object, just to target it. If you’re calling a cursor, you need to use Cursor Target Stored to execute the task.

Stored Object (Cursor Target Stored won’t work):
Let’s say you want to use your pet summoning ball. In this case you might drag Store Pet Summoning Ball 1 into a hotbar slot, click on the that slot, and target Pet Summoning Ball 1, thereby storing Pet Summoning Ball 1 via objectID. You could then write a macro to Target Pet Summoning Ball 1 and Use Targeted Object. This would work and you would interact with Pet Summoning Ball 1. However, since there is no action in the macro that requires a cursor, and no cursor would appear, using Cursor Target Stored would have no effect and the macro would not work.

Cursor Target Stored and Stored Object both work:
Let’s say you write a macro to cast Heal on a pet. You can write the macro as: Cast Spell (Heal), wait for Target Cursor, Cursor Target Stored (Pet). This will cast and execute Heal on Pet, but not on anything else, because you have cursor targeted Pet by Pet’s objectID. In idealized code, this macro would look something like this:
Cast Spell (Heal)
Open Target Cursor
Wait for Target Cursor to Finalize
Target(objectID)
Close Target Cursor

You could write this same macro as Cast Spell (Heal), right click> Target Stored (Pet). This macro will also cast and execute Heal on Pet, but not on anything else, because this time you have Stored Object (Pet) via Pet’s objectID, directly on Cast Spell (Heal). In idealized code, this macro would look something like this:
spell(Heal(target(objectID)))

 

The result of these different paths is that if you wish to target via objectID:

1.      When a targeting cursor is required, Cursor Target Stored or a similar function (the default cannon actions are all Cursor Target Stored) must be used.

2.      When no targeting cursor is required, Stored Object (Default Objects, Pets, Pet Summoning Balls, Bard Super Slayers, and Bard Slayers are all Stored Object) must be used.

If both Cursor Target Stored and Stored Object are available, it may be slight faster to use Stored Object.

Creating a generic “Stored Object”

Stored Object actions are incredibly useful, but in the default client there are a limited number of those actions and many of them are additionally restricted in the types of objects that can be stored. For instance, Pet Stored Objects must be pets, Bard Slayers Stored Objects must be in your backpack, etc. The only widely usable Stored Object actions are Default Stored Objects, but there are only five of those in the default client. One of the major targeting flaws in the default EC is that while many Stored Object actions exist, all with their own uniquely identifiable icons, no Generic Stored Object action exists. Such an action would work as a counterpart to the generic Cursor Target Stored and vastly improve Stored Object targeting. Below is an image displaying the relationship among Actions regarding the creation of a basic macro for target Stored Object, Use Targeted Object. While the icons are pretty, a generic Stored Object would be far more functional.


Luckily, the EC is mod-able, you can create a bazillion Default Stored Objects if you’d like, all with their own icons. However, a more accessible solution would be preferable. As the EC also provides the tools to create functions in-game with Actions>Command, such a solution exists. Allow yourself to be introduced to the most generic of targeting commands and one of the simplest and most useful bits in the EC:

script HandleSingleLeftClkTarget(objectID)

This simple function, accessible via Action>Command, will allow you to Store/Target Object for anything in the game, but without the need for a cursor.
-Arroth

Comments

  • Arroth_ThaielArroth_Thaiel Posts: 1,031
    edited August 2022

    Using HandleSingleLeftClkTarget(objectID)

    In order to use this Action>Command we are first going to have to obtain objectID’s for items. There is a simple way and a more complex way to do this. The simple way is to open Agents Settings from the Main Menu and start a new Organizer. Then, Add by ID. You will see the objectID for whatever you clicked on added to the Organizer. This will work for all items that you can own (that appear in your backpack or another container). If you need the objectID for a world object, you can left click on the object to target it, then once the world object appears in your targeting window, Add by ID. The image below shows me trying to Add by ID my front door prior to clicking on the door and after clicking on the door.


    That is the simplest way to obtain an objectID (that I am aware of). However, Organizer Add by ID might not work with everything.

    The more complex way to obtain an item’s objectID is to use Store Default Object 1 from the Actions list, then look up the stored objectID in the Character File. To use this method, drag Store Default Object 1 from the Actions list onto a hotbar. Then click the icon and target the item whose objectID you wish to obtain.


    Immediately log out and open your character’s Character File. Since you’ve performed no other in-game actions, the objectID of the item you just clicked on should be the last entry in the character file, under UIVariables, with the name DefaultObject1 and the numerical objectID. The line of code in the character file with that information for this example is highlighted in the image below.


    If you have performed tasks after Store Default Object 1, or have stored many objects over time, DefaultObject1 is unlikely to be the last line in UIVariables, but it will still be present, you just have to find it.


    Using an item’s objectID, we can now write a macro to interact with that item. The simplest and one of the most useful of those interactions is the two part macro, Target(objectID), Use Targeted Object.

    Since we are no longer limited to five default objects, we can set up this macro to work with anything, and we can have as many as we like. Since we have the objectID for my front door, let’s build a macro to ShutTheFrontDoor. Yes, this will only work with this one door, but we’ll use this for practice.

    For completeness, a nearly identical macro could be established with Default Object 1, as in the image below. This would be a simple Target Default Object 1, Use Targeted Object.



    Now we will use the same structure but with Command instead of Target Default Object 1. When we drag in Command we will copy/paste

    script HandleSingleLeftClkTarget(1078689798)
    
    into the Command text entry box. Where 1078689798 is the objectID of my front door.

    When mousing over Command, the final macro should appear as in the image below:


    Yeah! Now I can close my front door without having to mouse over and click on it!


    Ok, so what are some situations where you might actually use this little macro:

    Targeting and using the mooring line on your ship.

    Targeting and using your ship’s hold.

    Targeting and opening your loot (/mining/lumberjacking/etc) chest so the organizer can run without having to click the chest again.

    Targeting and opening frequently accessed nested containers. (Just extend the macro, target(objectID)-use targeted object, repeated to open nested containers.)

    Targeting and opening containers blocked by other containers.

    Targeting and using small objects or difficult to see objects.

    Targeting your pack animal and opening its backpack without the need for the context menu or the health bar.

    Targeting as many pets as you would like, instead of the default 15.

    Targeting as many objects as you would like, instead of the default 5.

     

    I hope this helps people understand a bit about ID codes and targeting via objectID. I’m sure many of you will come up with far more uses for

    script HandleSingleLeftClkTarget(objectID) 

    -Arroth
  • Arroth_ThaielArroth_Thaiel Posts: 1,031
    edited August 2022

    So, when I posted this yesterday, I skimmed over one of the big reasons for wanting a generic Store Object action and the entire reason I started looking into this topic: cannons!

    The default EC cannon actions are all Cursor Target Stored. That is useful for repair, but not so much for interacting with the cannon. Yes, you can mod the client and make your own pretty little Store and Target icons for each cannon, but you are still limited to the number of actions and associated artwork you feel like creating.


    However, using the methods described in this thread, you can make as many ‘use cannon’ macros as you like, all in the default EC, no mods required! You can also increase the complexity of the macro to interact with the cannon gumps via .PressButton, allowing you to reload or fire with a single keypress.



    An additional use of objectID targeting, which can be accomplished without Action>Command or mods, is to create macro “blocks” for specific uses. When we normally write a macro, it goes something like, CastSpell, WaitForTargetCursor, CursorTargetCurrent, or similar.


    However, the macro could also be written as, Target Default Object 1, Cast Spell, WaitForTargetCursor, CursorTargetCurrent.


    The result would be the same for a single spell. However, you can write macros for many spells, all using Target Default Object 1, and change the target of all those macros, simply by changing the objectID stored for Default Object 1. So, if you were to put Store Default Object 1 on a hotbar

    you could maintain objectID targeting, while changing the target, for a host of spells (a block of macros), all by simply clicking on Store Default Object 1 and selecting a new target.

    You could also place the five Store Default Object on a hotbar, each associated with a macro block for healing, attacking, etc., and switch targets for all the macros in a specific block by simply retargeting the associated Store Default Object.

    Such a setup might work well for boss battles. If you Store Default Object the Boss once, all associated Target Default Object macros would target that specific Boss via objectID, never losing target and without the need to pull a health bar or see through visual noise. When the next Boss was encountered, a single click of Store Default Object and targeting the new Boss, would retarget all of your macros in the associated block to this new Boss.
    -Arroth
  • ForeverFunForeverFun Posts: 827
    Sherry the mouse dropped this in a mailbox today:

    Get Type ID action --

    Actions.ItemIDRequestTargetInfoReceived()

    Debug.PrintToChat(L"ObjectId "..towstring(objectId))

    if(DoesWindowNameExist ... ()


  • SethSeth Posts: 2,904
    Another post that need to be saved.
    If it ain't broke, don't fix it. 
    ESRB warning: Some Blood. LOTS of Alcohol. Some Violence. LOTS of Bugs
  • Arroth_ThaielArroth_Thaiel Posts: 1,031
    edited August 2022
    For those who need a little help with code (many times this is all of us!), what ForeverFun posted above is a method to mod a file to give the EC's default TypeID tool the ability to return both the typeID and the objectID.

    To make that explicit, as if you've never made a mod before:

    Open the UserInterface folder in the install directory for the Enhanced Client.
    (C:\Program Files (x86)\Ultima Online Enhanced Client\UserInterface) - or something similar

    Create a new folder in this location. Call it something like, MyCustomUI.

    Also, unzip "Default.zip" from this location to a location on your desktop.

    Open the MyCustomUI folder and create another folder called Source.

    Open the unzipped "Default\Source" on your desktop and copy "Actions.lua" to "MyCustomUI\Source".
    --Always keep a copy of your default unedited files. You won't regret it.

    Open the Actions.lua file you just placed in MyCustomUI\Source. You can use Notepad to do this.
    The directory you're working from should be:
    (C:\Program Files (x86)\Ultima Online Enhanced Client\UserInterface\MyCustomUI\Source\Actions.lua)

    Within Actions.lua, scroll down and locate "function Actions.ItemIDRequestTargetInfoReceived()". This will be on line 1351 of an un-modified file.

    Insert the line
    Debug.PrintToChat(L"ObjectID"..towstring(objectId))
    between the lines
    WindowUnregisterEventHandler("Root", SystemData.Events.TARGET_SEND_ID_CLIENT)
    and
    if(IsMobile(objectId)) then

    The modified code is shown in the following image, with the inserted line highlighted:


    Save the file and log out.

    Load the client. At the Login screen click the CustomUI button and choose MyCustomUI.

    Now, when you use the typeID tool, in addition to returning information about the typeID, the objectID of the item you clicked on will appear as a system message.


    -Arroth
  • ForeverFunForeverFun Posts: 827
    edited August 2022
    Sherry the mouse recommends you comment out lines 1395, 1396 above.  (prefix the line with --)  Then you can easily get other player/pet/etc objectID.

    Use L"ObjectID " (with a trailing space inside the quotes for readability).

  • SethSeth Posts: 2,904
    I don't mind a quick fix like what we did for the egg picking event...

    But it would help if the EC is gets all these scripts built iin.So anyone can just download and use them instead of messing with the codes.

    If it ain't broke, don't fix it. 
    ESRB warning: Some Blood. LOTS of Alcohol. Some Violence. LOTS of Bugs
  • McDougleMcDougle Posts: 3,742
    Seth said:
    I don't mind a quick fix like what we did for the egg picking event...

    But it would help if the EC is gets all these scripts built iin.So anyone can just download and use them instead of messing with the codes.

    Now that's just crazy talk what do you want next the developers to communicate 
    Acknowledgment and accountability go a long way... 
  • Arroth_ThaielArroth_Thaiel Posts: 1,031
    edited August 2022
    So, I made tool specifically for objectID codes.

    Copy the following code chunk into the end of Actions.lua.

    function Actions.returnObjectID()
    	WindowUtils.SendOverheadText(GetStringFromTid(1154972), 1152, true)
    	RequestTargetInfo()
    	WindowRegisterEventHandler("Root", SystemData.Events.TARGET_SEND_ID_CLIENT, "Actions.objectIDRequestTargetInfoReceived")
    end
    
    function Actions.objectIDRequestTargetInfoReceived()
    	local objectID = SystemData.RequestInfo.ObjectId
    		
    	WindowData.CurrentTarget.TargetId = objectID
    	WindowUnregisterEventHandler("Root", SystemData.Events.TARGET_SEND_ID_CLIENT)
      
      	if (objectID == 0) then
    		WindowUtils.ChatPrint(GetStringFromTid(1155183), SystemData.ChatLogFilters.SYSTEM )
    		return
    	end
    	
    	local textReturned = (L"ObjectID: " .. towstring(objectID))	
      
    	WindowUtils.SendOverheadText(textReturned , 2498, true)
    	
    end

    Then call the tool with Action>Command:
    script Actions.returnObjectID()

    If you don't like the Bright White overhead text, change 2498, back to 33. That parameter changes the hue of the bark text. So, the following would be that dark red color again:
    WindowUtils.SendOverheadText(textReturned , 33, true)
    The "true" parameter in the above line sends the return to the chat window as a system message, remove "true", or change it to "false" to not have a system message.

    Good luck!
    -Arroth
Sign In or Register to comment.