Skip to content

Quick Guide on matching portraits to character id/data

SeritySerity Member Posts: 16
edited April 2019 in UI Modding
As some of you may know, there are no functions to pull a character id from a portrait index. With a bit of hacking, you can work around this. A caveat is that it won't work if multiple characters have the same name, but nobody would be that unoriginal, right?

First, as always, you have to click a portrait before you can retrieve a character id - data won't be stored in the characters table until you do this. So far, this is the fastest way I've figured out to pull all the character data, which takes about 120ms for me - I suggest pairing with "if (e:GetActiveEngine() == worldScreen)".
e:SelectEngine(optionsScreen)
local numchars = Infinity_GetNumCharacters() - 1
for i=0,numchars do 
      Infinity_OnPortraitLClick(index) 
end
e:SelectEngine(worldScreen)
This "quickly" switches the screen to the options screen, clicks all portraits, then switches back to the world screen. By switching away from the world screen (or the map screen), clicking a portrait will not change the selection nor cause character reaction quotes. It takes about 20ms to switch to the world screen, then about 10-20ms per portrait click. Switching to the options screen takes 0ms.

This is obviously unreasonable to do constantly (causes framerate hitches), and only the "currently selected character" will have their data updated with Infinity_UpdateLuaStats() (but you can use this to constantly (like, once a second, not every frame) update the current character without much lag). I like to have all the characters' info update when I pause, personally.

Once you have figured out however you want to populate the characters table, you can then match portraits to ids. It's pretty simple: you can get a portrait's character name, by portrait index, by retrieving the portrait tooltip, then match it to the characters table. The tooltip displays "characterName\nhp/maxHp", so by getting the part before the \n, you can grab their name, then retrieve the name field for each entry in the characters table and match it.
function getCharacterName(index)
    if (not index) then return end
    local tt = Infinity_GetPortraitTooltip(index) 
    local charaName = tt:match("(.*)\n")
    return charaName
end
function getCharacterKey(name)
    if (type(name) == "number") then name = getCharacterName(name) end
    for id,data in pairs(characters) do
        if data.name == name then 
            return id
        end
    end
end

data = characters[getCharacterKey(0)]
-- You can also use Infinity_GetSelectedCharacterName() to get the currently selected character's name
data = characters[getCharacterKey(Infinity_GetSelectedCharacterName())]
-- You can view all fields with table printing code (look some up), but for a simple 'get all categories' you can use:
-- for _,d in pairs(characters) do for key,val in pairs(d) do Infinity_Log(key) end break end

As a little personal project, I made little XP bars on the portrait interface (although they only update if I click to the character or pause the game), but you can probably come up with more creative solutions. Some examples are custom GUI elements or buttons based on the current character's class (characters[x].class), alternate health bars (characters[x].HP.(current/max)), displaying number of inventory slots free (characters[x].equipment.<slots>.empty), priest/mage spell data (characters[x].(priest/mage)Spells)...
zbGdxND.png
Post edited by Serity on

Comments

  • GusindaGusinda Member Posts: 1,915
    @Serity, great stuff and welcome. In your digging around, did you happen to notice whether there was a method of increasing the size of the font or changing the colour for the HP/HP display on the portrait?

    Thanks
    Gus
  • SeritySerity Member Posts: 16
    edited April 2019
    Gusinda wrote: »
    @Serity, great stuff and welcome. In your digging around, did you happen to notice whether there was a method of increasing the size of the font or changing the colour for the HP/HP display on the portrait?

    Thanks
    Gus
    As far as I am aware, and believe me I've looked, portrait stuff (and action-bar) is entirely hardcoded. They just use a simple tag in the button code called "portrait #" (or "actionBar #") which the game then reads and then automatically plops the portrait, HP/MHP, effect icons, etc (or for actionBar, handles the clickiness, the icons, what happens when you click, etc). There's some tweaks you can do, but for the most part it's stuck on.

    That's not to say you can't change it at all, though. Regardless of your method, you'd still also have to deal with the issue of updating the portrait constantly (since "clicking" on a portrait, even through code, is laggy). I haven't a way around that yet, so a real-time tweak would be a bit difficult.. Here's some ideas, though:

    You can draw over the top of these two elements by placing a new element below it in the UI.MENU file, so you could probably place a black box where the HP text is and then add your own custom-label on top of that. If you really wanted to go hardcore, given enough effort, you might be able to manually duplicate the portrait behavior (read character data as above to get portrait ('bitmap lua "characters[id].portrait"' or similar), hp, status effects (characters[id].statusEffects)), although I couldn't tell you how to duplicate every individual element.

    You can add a [text "xyz"] line inside the button element (portrait or actionbar) and style it with [text style "whichever"], although if the text style doesn't exist it will crash. You can add new styles like so, as you probably know:
    styles['actionbartextl'] =
    {
    	color = 'S', -- if color is invalid it will crash.
    	font = 'NORMAL',
    	point = 12,
    	useFontZoom = 0,
    	valign = 'bottom',
    	halign = 'left',
    	pad = {4,0,0,-2} -- left,top,right,bottom
    }
    

    I use this myself to show cooldowns for potions/usable items (timer:GetCurrentTime(), 100 is a round) or to display an indicator on the stealth actionBar to indicate whether I'm in stealth or not (the tiny border around the square to indicate "active" is difficult for me to see), as well as some extra helper functions for Thieving (so it displays "Stole XXX!" in chat upon pick pocketing).

    oasfV2G.png
    6B4Sjlt.png

    You can also watch the combat log ["combatLog"] for any mention of "<charname>: Takes <dam> <type> damage from <Target>" and update the hp bar yourself, although it's a bit tricky to catch it on every update since the game sometimes will put more than one line into the combatLog at once and the game doesn't respect __newIndex/__index metatable parameters for updating the combatLog :/
  • GusindaGusinda Member Posts: 1,915
    Hi @Serity, yeah, I have tracked it all the way to baldur.exe but can't seemed to go any further. Did some hex editing and was able to change fonts but not the size, there are easier ways. I was hoping that you may have seen a style component (to put in the M_style.lua file) of some sort but alas, that is not the case. I am currently using fonts (using Copperplate Bold as the FLOATTXT.TTF in the override folder) to replace the default ones to increase the size slightly but I would like to take it 2 points higher.

    Interesting in what you have done there. Opens the game up for a little more 'fine tuning'. I think I am playing in the backend more that actually playing the game right now. Will play with what you have hinted at and see what happens... Also keeping an eye on what Bubb's mastery is doing. Fascinating stuff!

    Thanks
    Gus
  • SeritySerity Member Posts: 16
    Gusinda wrote: »
    Also keeping an eye on what Bubb's mastery is doing. Fascinating stuff!
    Yeah, Bubb's stuff looks great, he's done good work. I'm not currently using EEex right now though, I'm a little wary since the group I play with already has a mod set built up, and I don't want to get into messing around with it and possibly causing any compatibility errors with other mods or having to reinstall, so I'm strictly sticking to straight UI.menu mods with what hooks the base game offers at the moment. Maybe if we restart, though!
  • BubbBubb Member Posts: 1,000
    Thanks for the EEex love guys!



    @Serity: The stuff you've done here is really impressive. The UI / Lua environment is super hardcoded and practically undocumented, and the fact that you've been able to traverse it and implement some very clever workarounds is great! If you ever need to know how something works internally don't hesitate to send me a PM :)



    @Gusinda:
    Gusinda wrote: »
    I am currently using fonts (using Copperplate Bold as the FLOATTXT.TTF in the override folder) to replace the default ones to increase the size slightly but I would like to take it 2 points higher.

    Not sure if you mean increasing the size inside the override file or actually getting the engine to render the font at a higher point size. Increasing the point size via hex edits isn't easy - it would require something like EEex - though I did discover that the engine will respect a font size defined in Baldur.lua:
    SetPrivateProfileString('Fonts','FLOATTXT','15')
    

    That'll make all instances of FLOATTXT a point size of 15. Combine a setting like that with a hex edit that switches the portrait font might do what you want.
  • GusindaGusinda Member Posts: 1,915
    edited April 2019
    Thank you very much @Bubb, that works like a charm on the WorldMap (and takes the HP/MHP out to where it can fit). I have been playing around with this for months!

    There wasn't any need for a hex edit (I was just playing around there). I just grabbed the font that I wanted to use and renamed it to FLOATTXT.TTF and put it in the override folder.

    The world map was the main prob when playing on the phone and this syntax has fixed that. I don't suppose you know how to change colour as well. The red and the green just become a blur (might be better set to a higher size, will have to see), I have adjusted the colours inside a M_Styles.lua but I get the feeling that there is a direct RGB setting when drawing the text. Would prefer just white if this is possible.

    Thanks Again
    Gus

    Edit: BTW, the stuff you are doing is worth the love...
  • BubbBubb Member Posts: 1,000
    The portrait renderer directly sets the color of the text based on an internal array... the only way to force it to white would be to do some hex edits unfortunately.

    If you're interested in that, here's the edit, (at least on PC):

    Search for
    BA 80 80 80 00 66 85 C9 8D 8D 08 FF FF FF 0F 44 C2
    

    Replace with
    BA FF FF FF 00 66 85 C9 8D 8D 08 FF FF FF 8B C2 90
    
  • GusindaGusinda Member Posts: 1,915
    Thanks @Bubb, will give it a go when I get home.

    Gus
  • GusindaGusinda Member Posts: 1,915
    @Bubb, that worked and was what I was looking for!

    Thanks again
    Gus
Sign In or Register to comment.