Quick Guide on matching portraits to character id/data
Serity
Member Posts: 16
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)".
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.
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)...
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)...
Post edited by Serity on
2
Comments
Thanks
Gus
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:
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).
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
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
@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:
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:
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.
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...
If you're interested in that, here's the edit, (at least on PC):
Search for
Replace with
Gus
Thanks again
Gus