UI:Main Page

From Total War Modding
Revision as of 08:49, 30 March 2022 by DrunkFlamingo (talk | contribs) (Undo revision 478 by DrunkFlamingo (talk))

So, you’ve come to this crazy place. You want to learn about UI modding. Congrats, and good luck.

This section is going to cover a lot – I’d like to share about as much UI modding tips as I have – and because of that, it’s likely things will go back and forth. Primarily, this is a subject based in Lua, and most of the actual UI creation will take place in Lua scripts. Lua knowledge is expected. I’m not going to make this a copy of the Lua chapters, but with UI. If you don’t know what I’m talking about, it should be in the Lua chapters. If it is not, tell me, and I will add it.

There will also be assumptions that you understand what various game objects are – like buildings, effects, localisation – and there will be assumptions that you know how to do them and use them. If you do not – find out, then come back.

The final large assumption this tutorial bears in mind is that you have a goal, a specific thing you want to create. I’m going to mostly be doing lightly guiding, and I’ll be explaining one general concept at a time. We’ll start from the very basics, and will eventually rabbit-hole into a bunch of fun specific tasks – like creating custom tooltips, generating UI from data. If a chapter doesn’t cover whatever your specific needs are – I would read it. The progression is setup in mind for someone trying to go from “create a button” to “create fully custom UI panels and mess with vanilla ones as well”.

Note that this is heavily geared towards WH3, but almost all of the information here will carry over to Three Kingdoms. I’ll try to make clear any differences, if I can recall any!

And without further adieu, let’s get started.

Relevant Interfaces Types

The interface (or userdata, I'm going to use interface) types that are necesssary for UI modding using Lua are: UIComponents, CampaignUI, and the cco script interface.

UIComponents

UIComponents represent pieces of the UI. They include: text; images, buttons; hud elements, and pretty much everything else you can see or click on. We typically refer to them as UIC, or just components, for shorthand.

Hierarchy

All UIC have children, and all but one of them has a parent. The one that doesn't have any parents is the UI Root, and can be accessed as a variable at any time using core:get_ui_root(). This setup creates a tree or hierarchy of components. If you're modding Warhammer 3, you can see this tree by opening the context viewer and pressing "Component Tree" at the top. The hierarchy typically moves from larger pieces and containers to smaller individual elements.

For instance, my Lord's unit card in the units panel is represented by a UIC named LandUnit0. This UIC It has a child named card_image_holder, which itself has lots of children, such as experience. Each component in this setup plays a different role: the first acts as a container for the other elements and displays a tooltip; the second displays the actual unit card png; and the third represents the experience icon on the unit.

We can also follow this tree backwards. LandUnit0 has a parent named units, which acts as a list and is contained by main_units_panel, which itself is contained within the units_panel, which is a child of the UI root. We refer to this chain of component names as the "path" to the component.

Following the hierarchy is the main way to get access to a UI Component. For instance, if we wanted to define a variable in our script for LandUnit0, we could do this:

local units_panel = UIComponent(core:get_ui_root():Find("units_panel"))

local main_units_panel = UIComponent(units_panel:Find("main_units_panel"))

and so forth until you got to the element you wanted, but that is extremely cubersome, so CA defines a function to do it for you: find_uicomponent. You will call this function a lot when you are UI modding.

To use it, we just provide a parent and the path to the element we want, in this case: local LandUnit0 = find_uicomponent(core:get_ui_root(), "units_panel", "main_units_panel", "units", "LandUnit0").

On the rare occasion we need to, we can also define the parent of any element using UIComponent(LandUnit0:Parent())

Addresses and the UIComponent() function

In the above function calls, we didn't define our variables as the return values of Find() and Parent(), but rather passed those return values to UIComponent.

That is because those functions don't return the UIComponent that is a child or parent of the current one; instead, they return an address for it. There are two uses for addresses:

  1. You can pass the address to UIComponent() and it will return the UIC which the address points to.
  2. You can use the address with certain other UIC Methods, such as Divorce and Adopt
  3. You can pass the address to tostring() and it will give you a string that can be used to refer to that specific UIC. This is useful when you want to distinguish between two UIC with the same name.

States

All UI Components have a state, which changes how they appear. For instance, our unit card from the previous example has active hover inactive locked queued raise reinforcement routing selected selected_hover taking_damage taking_damage_selected unknown wavering wavering_selected

That's a lot of states, but it does a good job showing what they're for: they let a single element change how it looks, which of its children are visible, what image it is displaying, and what text and tooltip it shows, and pretty much anything else.

That makes them very useful when you're trying to change the UI. For instance, if I wanted to make it impossible to disband, upgrade, or merge one of your units, I could achieve that by simply calling

local LandUnit5 = find_uicomponent(core:get_ui_root(), "units_panel", "main_units_panel", "units", "LandUnit5")

LandUnit5:SetState("inactive")