该编辑可以被撤销。 请检查下面的对比以核实您想要撤销的内容,然后发布下面的更改以完成撤销。
最后版本 | 您的文本 | ||
第1行: | 第1行: | ||
{{Version|timeless}} | {{Version|timeless}}{{需要翻译}} | ||
CK3的用户界面(UI)是高度可修改的,但由于这个原因,UI修改器会禁用成就,因为玩家可能会用它们作弊。 | CK3的用户界面(UI)是高度可修改的,但由于这个原因,UI修改器会禁用成就,因为玩家可能会用它们作弊。 | ||
第115行: | 第115行: | ||
这可以帮助你更好地看到代码的结构,注意到任何缺失的或额外的括号,对于一些编辑器来说,这也是正确折叠代码块所需要的。 | 这可以帮助你更好地看到代码的结构,注意到任何缺失的或额外的括号,对于一些编辑器来说,这也是正确折叠代码块所需要的。 | ||
=== UI组件 === | === UI组件 === | ||
第177行: | 第157行: | ||
* 在另一个hbox中的hbox将有0个大小,并且不会展开其子代。使用<code>layoutpolicy_horizontal = expanding</code>来调整它的大小(或者在vbox中使用<code>layoutpolicy_vertical = expanding</code>)。 | * 在另一个hbox中的hbox将有0个大小,并且不会展开其子代。使用<code>layoutpolicy_horizontal = expanding</code>来调整它的大小(或者在vbox中使用<code>layoutpolicy_vertical = expanding</code>)。 | ||
* 接受数据模型(从游戏数据中创建列表)。 | * 接受数据模型(从游戏数据中创建列表)。 | ||
<code>dynamicgridbox</code> | <code>dynamicgridbox</code> | ||
* | * Is only used with datamodels. | ||
* | * Arranges all the items vertically. Use <code>flipdirection = yes</code> to make it horizontal. | ||
* | * Doesn't ignore invisible items by default. Use <code>ignoreinvisible = yes</code> to change it. | ||
* | * Can be fixed size, resized by the content and limited by minimumsize and maximumsize. | ||
* | * Items can be of different size. | ||
* | * Can become laggy with very long lists. | ||
<code>fixedgridbox</code> | <code>fixedgridbox</code> | ||
* | * Similar to a dynamic box but all its itmes are of fixed size (it's essentially a table). | ||
* | * Is only used with datamodels. | ||
* | * Arranges all its items vertically. Use <code>flipdirection = yes</code> to make it horizontal. | ||
* | * Cannot ignore invisible items. | ||
* | * Can be fixed size, resized by the content and limited by minimumsize and maximumsize. | ||
* | * Much better for performance with long lists. | ||
<code>overlappingitembox</code> | <code>overlappingitembox</code> | ||
* | * Is only used with datamodels. | ||
* | * Arranges all its items horizontally and overlaps them if the list is longer that the size of the box. Use <code>flipdirection = yes</code> to make it horizontal. | ||
* | * Can be fixed size or autoresized. | ||
<code>scrollarea</code> | <code>scrollarea</code> | ||
* | * A widget with scrollbars that appear if the content is bigger than its size. | ||
* | * Scrollbars can be disabled with <code>scrollbarpolicy_horizontal = always_off</code> and <code>scrollbarpolicy_vertical = always_off</code>. | ||
** | ** A scrollarea with no scrollbars can be used to crop lists or images. | ||
* | * Can be fixed size, resized by the content and limited by minimumsize and maximumsize. | ||
<code>button</code> | <code>button</code> | ||
* | * A clickable object. Accepts <code>onclick</code> and <code>onrightclick</code>. | ||
** | ** When adding a right click function, include <code>button_ignore = none</code>. | ||
* | * Doesn't have a texture by default. | ||
* | * Can be fixed size or resized by its children. | ||
** | ** A 0x0 button can be used to add invisible hotkeys. | ||
<code>icon</code> | <code>icon</code> | ||
* | * Displays a texture. | ||
* | * Can be used as a widget to store children. | ||
* | * Can be flipped with <code>mirror = horizontal</code> or <code>mirror = vertical</code>. | ||
<code>textbox</code> | <code>textbox</code> | ||
* | * Shows text. | ||
* | * Can be fixed size or autoresized. | ||
* | * Use <code>elide = right</code> or <code>elide = left</code> to cut off text that is too long | ||
* | * Can be a single line or multiple, with <code>multiline = yes</code>. | ||
* | * Game files often use templates set in gui/shared/text.gui, like <code>text_single</code>. Use them to keep visual consistency and to type less code every time. | ||
=== Promotes and Functions === | |||
Each window has a predefined set of commands - promotes and functions - available for it. These can be found in the data_types.log file in <code>Documents/Paradox Interactive/Crusader Kings III/logs/</code> after you use the <code>DumpDataTypes</code> console command. | |||
They are used to display all data from the game, like your name, gold, children, and to set button actions. | |||
A promote returns a scope, i.e a game object, like Character or Province, while a function returns a number, a string or a boolean (true/false) value, etc. | |||
Global commands can be used anywhere, like GetPlayer (returns the player character) or GetCurrentDate. | |||
Other commands can only be used in their window/object, for example, GetParents can only be used in the character window and must be started with <code>CharacterWindow.GetParents</code>. | |||
Commands can be chained like this: | |||
<code>CharacterWindow.GetCharacter.GetPrimaryTitle.GetHeir.GetPrimarySpouse.GetFather</code> | |||
Children can inherit the scope from their parent, meaning we wouldn't need to retype the line above to show information about this character. Instead we can set <code>datacontext</code> of a widget to this line and then every textbox in it will use <code>"[Character.GetNameNoTooltip]"</code>, <code>"[Character.GetGold]"</code>, etc. | |||
< | |||
The same applies to items in gridboxes. | |||
=== Templates === | |||
Templates are named blocks of code which can be used multiple times throughout the code, which helps maintain the same style and reduce the amount of code we write. Editing the template will edit all instances of it! | |||
Templates are global and can be defined in any file. Most of the game templates are stored in gui/shared. A local version, local_template, has to be defined within the same file. | |||
Templates can store the contents of an entire window or just one line, like this one: | |||
<pre> | <pre> | ||
template Window_Size_Sidebar | template Window_Size_Sidebar | ||
第353行: | 第245行: | ||
</pre> | </pre> | ||
This template can be used inside another element with "using = Window_Size_Sidebar", which will, essentially, replace the "using" line with the contents of the template. | |||
==== | ==== Types ==== | ||
If templates can contain just a few properties, types are always whole elements, like a button or a widget. | |||
text_single and text_multi are types of a textbox with many properties already defined for them, so we don't need to retype them every time and instead simply write: | |||
<pre> | <pre> | ||
第371行: | 第259行: | ||
</pre> | </pre> | ||
Types are defined in a slightly different way, by creating a named group of types first: | |||
<pre> | <pre> | ||
第382行: | 第270行: | ||
</pre> | </pre> | ||
==== | ==== Blockoverride ==== | ||
Templates and types can have named override blocks, which allow us to edit a part of an instance without changing the whole template. For example, a template may have a default block of text: | |||
<pre> | <pre> | ||
第393行: | 第281行: | ||
</pre> | </pre> | ||
To replace it, we add a blockoverride with the same name in our instance: | |||
<pre> | <pre> | ||
第402行: | 第290行: | ||
</pre> | </pre> | ||
We can also remove it from our instance like this: | |||
<code>blockoverride "text" {}</code> | <code>blockoverride "text" {}</code> | ||
== | == Scripted GUIs == | ||
Scripted guis are, essentially, hidden events triggered from the UI. | |||
The are stored as .txt files in game/common/scripted_guis and cannot be reloaded from the game, unlike the .gui files. | |||
The basic structure of a scripted gui is: | |||
<pre> | <pre> | ||
gui_name = { | gui_name = { | ||
scope = character # | scope = character # the root scope, i.e. the target of the effects | ||
saved_scopes = {} # | saved_scopes = {} # any additional targets | ||
is_shown = {} # | is_shown = {} # is it visible on the UI? | ||
ai_is_valid = {} # AI | ai_is_valid = {} # is the AI allowed to use it? Disabled by default. | ||
is_valid = {} # | is_valid = {} # can the player use it? | ||
effect = { # | effect = { # what it does | ||
custom_tooltip = "" # | custom_tooltip = "" # adds a tooltip | ||
} | } | ||
} | } | ||
</pre> | </pre> | ||
Not all of the blocks are necessary. Sometimes a scripted gui may only contain the scope and is_shown or effect. | |||
In the .gui file we use the following: | |||
<pre> | <pre> | ||
第458行: | 第334行: | ||
</pre> | </pre> | ||
datacontext is necessary to link the element to the scripted gui. Other commands won't work without it. | |||
In this example the scripted gui is scoped to the player with a global function <code>GetPlayer.MakeScope</code>. It can be changed to someone in the character window, e.g. <code>CharacterWindow.GetCharacter.MakeScope</code>, or if the scope was a province, <code>HoldingView.GetProvince.MakeScope</code>. | |||
ScriptedGui.Execute is used with buttons and will execute everything listed in the <code>effect</code> block in the scripted gui. | |||
IsShown and IsValid check for conditions in is_shown and is_valid blocks. | |||
BuildTooltip can also be used with a textbox to display custom text in the UI. | |||
To save another scope to our scripted gui, we use AddScope like this: | |||
"[ScriptedGui.Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).AddScope('target', CharacterWindow.GetCharacter.MakeScope ).End )]" | "[ScriptedGui.Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).AddScope('target', CharacterWindow.GetCharacter.MakeScope ).End )]" | ||
And then we use the same name in the scripted gui: | |||
saved_scope = { | saved_scope = { | ||
target | target | ||
} | } | ||
Multiple scopes can be saved this way. | |||
It is important to not put any spaces before the dots or opening parentheses! <code>Execute(</code> is correct, <code>Execute (</code> is not. Other spaces can be omitted, but they help with readability. | |||
=== | === Displaying a variable or script value === | ||
Variables and script values can be shown on the UI like this: | |||
variable: | |||
<code> | <code> | ||
第496行: | 第368行: | ||
</code> | </code> | ||
svalue: | |||
<code> | <code> | ||
第502行: | 第374行: | ||
</code> | </code> | ||
In this example the variable is stored in the player character and is called <code>test_var</code>. Any other scope can be used, just remember to add <code>MakeScope</code>. | |||
|1 at the end is optional and will cut off all decimals save for one, so instead of 1.573 you will see 1.5. Note that it does not round the value. You can set this at any number, add % to convert to a percentage, add = or + to color the value if it's positive or negative. | |||
When using localization to display values in events, we use this: | |||
<code>event_var: "[ROOT.GetCharacter.MakeScope.Var('test_var').GetValue|0]"</code> | |||
<code>event_value: "[SCOPE.ScriptValue('test_value')|0]"</code> | |||
If you have a saved scope (named "target" here), it changes to this: | |||
<code>event_var: "[target.MakeScope.Var('test_var').GetValue|0]"</code> | |||
< | <code>event_value: "[GuiScope.SetRoot( target.MakeScope ).ScriptValue('test_value')|0]"</code> | ||
=== Displaying data lists === | |||
We can create custom lists of characters, for example, to make societies. | |||
To do this, first, we need to add them to a variable list. This can be done though an event or a scripted gui, like this: | |||
<pre> | <pre> | ||
第544行: | 第413行: | ||
</pre> | </pre> | ||
If we fire this effect for the player, they will be the <code>root</code>, so the list will be stored in them. | |||
Then we use any list box (vbox, dynamicgridbox, fixedgridbox) with the datamodel set to our list: | |||
<pre> | <pre> | ||
dynamicgridbox = { | dynamicgridbox = { | ||
第565行: | 第435行: | ||
</pre> | </pre> | ||
== | == New windows and toggles == | ||
There isn't a simple way to create a new window and make your mod compatible with others. We have to either overwrite the hud or other windows. | |||
The usual way is to add the new window to hud.gui inside of ingame_topbar widget and then add a button to show/hide it. | |||
The alternative is to rewrite one of the unused windows, like test_gui and add a button with the console command that shows it. | |||
=== Toggles with PdxGuiWidget === | |||
=== | |||
PdxGuiWidget is a simple function used to hide or reveal named elements. | |||
In this example we have a container with a hidden submenu, one button that shows it and another that hides it. | |||
# | # when clicked, the first button goes back to its parent, searches in it for the submenu and the other button, reveals them and hides itself | ||
# | # the second button searches for the element and the button, reveals them and hides itself | ||
<pre> | <pre> | ||
第613行: | 第477行: | ||
</pre> | </pre> | ||
If the elements are separated by more parents/children, we can repeat AccessParent like this: | |||
onclick = "[PdxGuiWidget.AccessParent.AccessParent.AccessParent.AccessParent.FindChild('submnenu').Show]" | |||
Each button can hide or reveal multiple elements of any type. You only need to provide the name. | |||
Pros: | |||
* easy to edit on the fly as any changes can be reloaded with the "reload gui" command | |||
Cons: | |||
* | * the toggles will reset any time the game is restarted | ||
* If you want to hide multiple things or toggles, the code will get very bloated and hard to manage | |||
* trying to hide entries in a data list (like a dynamicgridbox) will only hide the first instance | |||
=== Toggles with animation === | |||
We can set up animations that are triggered by buttons (or conditions) to hide/show elements or to even move them. This way we don't need to count how many parents separate the button and the window and we can trigger many things at once with just one onclick. | |||
The previous example would look like this and work the same way. The first button triggers "show_submenu", which hides the button and shows the rest, while the second button triggers "hide_submenu", which hides this button and the widget and shows the first button. | |||
<pre> | <pre> | ||
container = { | container = { | ||
第680行: | 第542行: | ||
</pre> | </pre> | ||
it is longer, but animations can be saved as templates and reused with one line, like <code>using = hide_animation</code>. Fullscreen Barbershop uses animations extensively, if you want a better example. | |||
Pros: | |||
* | * can also be edited on the fly, with "reload gui" command | ||
* easier to link many things together, and even open a different window and trigger an animation in it | |||
Cons: | |||
* | * animation blocks can be quite lengthy | ||
* | * all toggles will reset when the game is restarted | ||
=== | === Toggles with Scripted GUIs === | ||
Scripted guis allows us to use script to toggle visibility. They make it easier to manage multiple things but can impact performance when used in big numbers. | |||
For a scripted toggle we need to create a .txt file in common/scripted_guis/. The name of the file can be anything. | |||
A basic toggle in this file would looks like this: | |||
<pre> | <pre> | ||
第719行: | 第582行: | ||
</pre> | </pre> | ||
When clicked, it adds a variable to the scoped character and removes it when clicked again. | |||
Then, if the variable is present, our window will be visible. If it's not, it's hidden. | |||
This is how the gui file would look like. We can use just one button as its function will change with each click. | |||
<pre> | <pre> | ||
第739行: | 第602行: | ||
</pre> | </pre> | ||
"GetPlayer" is a global promote and returns the player character. | |||
"Player.MakeScope" makes our player the scope of the scripted gui, meaning this is where we store the variable in. | |||
Two buttons can be used if you want different tooltips for them. In this case, copy the 'visible' property to both buttons, but add 'Not' to one of them, so it's hidden by default: | |||
visible = "[Not(ScriptedGui.IsShown( GuiScope.SetRoot( GetPlayer.MakeScope ).End ))]" | |||
Pros: | |||
* the toggles are saved in the character and won't reset unless they die or you start a new game | |||
* easier to link many things, even in different windows | |||
The downsides: | |||
* you need to restart the game to update scripted guis | |||
* it's a little harder to remember the syntax (copy the code from here to reduce the chance of mistakes) | |||
=== System Variables === | |||
System variables are used internally by the game, they are not save persistent and cannot be directly accessed through scripts. | |||
No setup is needed for them as they can be directly created in the .gui file. | |||
The syntax for using system variables is: | |||
<code>onclick = "[GetVariableSystem.Toggle( 'var_name' )]"</code> | <code>onclick = "[GetVariableSystem.Toggle( 'var_name' )]"</code> | ||
or: | |||
<pre>datacontext = "[GetVariableSystem]" | <pre>datacontext = "[GetVariableSystem]" | ||
onclick = "[VariableSystem.Toggle( 'var_name' )]"</pre> | onclick = "[VariableSystem.Toggle( 'var_name' )]"</pre> | ||
The available functions are: | |||
*Clear - <code>Clear( 'var_name' )</code> | * Clear - <code>Clear( 'var_name' )</code> clears the variable | ||
*ClearIf - <code>ClearIf( 'var_name', Condition )</code> | * ClearIf - <code>ClearIf( 'var_name', Condition )</code> clears the variable if Condition is true | ||
*Exists - <code>Exists( 'var_name' )</code> | * Exists - <code>Exists( 'var_name' )</code> Boolean, returns true if the variable exists | ||
*Get - <code>Get( 'var_name' )</code> | * Get - <code>Get( 'var_name' )</code> CString, returns the value stored in the variable | ||
*HasValue - <code>HasValue( 'var_name', 'string' )</code> | * HasValue - <code>HasValue( 'var_name', 'string' )</code> Boolean, returns true if the stored value matches the provided value | ||
*Set - <code>Set( 'var_name', 'string' )</code> | * Set - <code>Set( 'var_name', 'string' )</code> sets the stored value to the provided value | ||
*Toggle - <code>Toggle( 'var_name' )</code> | * Toggle - <code>Toggle( 'var_name' )</code> clears the variable if it exists, creates it if it does not | ||
==== Toggles with System Variables ==== | |||
A basic toggle using system variables in this file would looks like this: | |||
<pre> | <pre> | ||
container = { | container = { | ||
第793行: | 第657行: | ||
</pre> | </pre> | ||
When clicked, the system variable is toggled depending on whether it exists, this is then used to show/hide the widget. | |||
==== | ==== Tabs with System Variables ==== | ||
A basic setup for three tabs would look like this: | |||
<pre> | <pre> | ||
第823行: | 第687行: | ||
</pre> | </pre> | ||
Note that the variable initially has no value and none of the widgets would show. | |||
To set a default tab the variable needs to be set by the button opening the window: | |||
<pre> | <pre> | ||
第834行: | 第698行: | ||
</pre> | </pre> | ||
or using a state block when the window is shown: | |||
<pre> | <pre> | ||
第843行: | 第707行: | ||
</pre> | </pre> | ||
Alternatively one of the widgets can be set to appear when the variable doesn't exist, avoiding the need for an initial value: | |||
<pre> | <pre> | ||
第869行: | 第733行: | ||
</pre> | </pre> | ||
This is the equivalent of the first example with one of the methods to set a default value. | |||
Pros: | |||
* | * simple and easy to remember syntax | ||
* | * easier to link many things, even in different windows | ||
* | * can be extended with additional commands (see below) to show entirely new windows, avoiding the need to have the widget in hud.gui | ||
The downsides: | |||
* | * no direct interaction with scripts, they must be set & cleared using the gui | ||
* | * can be harder to keep track of | ||
* | * all toggles will reset when the game is restarted | ||
=== 添加新的UI元素 === | === 添加新的UI元素 === |