Scripted GUI modding:修订间差异

本页面所适用的版本可能已经过时,最后更新于1.5
(原版文件夹名称已修改)
无编辑摘要
 
第1行: 第1行:
{{version|1.5}}
{{version|1.5}}
{{需要翻译|译者=霜泽图书馆}}


脚本化GUI是一个非常强大的功能,它允许modder实现自定义GUI接口和逻辑。
Scripted GUIs are used to assign scripted functionality to newly-created elements in the user interface, allowing it to modify the game's state or change depending on conditions.


== 概述 ==
__TOC__
{{SVersion|1.5}}
 
Scripted GUIs are defined as {{path|common/scripted_guis/*.txt}} files. The folder name must be in plural. The scripted GUI will assign functionality to user interface elements, which themselves are defined in {{path|interface/*.gui}} files.


脚本GUI被放置在 {{path|common/scripted_guis/}}
Most commonly, [[flags]], [[variables]], and [[arrays]] are used in the implementation of GUI. For example, implementing a button that opens up a window is most commonly done by assigning a flag to be set on the button press and making the window visibility depend on the flag being set on the country.


以下是脚本化gui中每个元素的概述:
A scripted GUI is defined as a block within a <code>scripted_gui = { ... }</code> container, where the name of the block determines the internal ID of the scripted GUI.
== Window assignment ==
{{See also|Interface modding}}
Scripted GUIs are assigned to <code>containerWindowType</code>s and apply to every interface element located within them. This is done with <code>window_name = example_container</code>.


<pre>
In order for a container to be a valid target for scripted GUI, it must be entirely independent, not present in any other container window. For example, in the following code in an {{path|interface/*.gui}} file, only <code>independent_container</code> can be assigned:
scripted_gui = {
<pre>guiTypes = {
    <name> = {
    containerWindowType = {
      window_name = <string>
      name = "independent_container" # Can be used in the window_name of scripted GUI
      context_type = <type>
      position = { x = 0 y = 0 }
     parent_window_token = <string>
      size = { width = 100% height= 210 }
    
      visible = {
       <triggers>
     }
      
      
      effects = {
      containerWindowType = {
        <element>_click = {
        name = "nested_container" # Located inside of another container, thus impossible to assign scripted GUI
         <effects>
        position = { x = 0 y = 0 }
       }
        size = { width = 100 height= 100 }
       <element>_<modifier>_click = {
         <effects>
        }
     }
    
     triggers = {
       <element>_click_enabled = {
         <triggers>
       }
       <element>_visible = {
         <triggers>
       }
     }
    
     properties = {
       <element> = {
         image = <string>
         frame = <var>
         x = <var>
         y = <var>
       }
     }
    
     ai_enabled = {
       <triggers>
     }
    
     ai_test_parent = <string>
    
     ai_test_interval = <int>
     ai_test_variance = <float>
     ai_test_scopes = <type>
    
     ai_check = {
       <triggers>
     }
    
     ai_check_scope = {
        <triggers>
     }
    
     ai_max_weight_taken_per_test = <int>
    
     ai_weights = {
       <element>_<modifiers>_click = {
         ai_will_do = {
           base = <int>
           factor = <float>
           add = <float>
          
           modifier = {
             factor = <float>
             add = <float>
            
             <triggers>
           }
         }
         ignore_lower_weights = {
           factor = <float>
         }
       }
      }
      }
    }
    }
}
}</pre>
</pre>
The scripted GUI will affect every single element located inside of the container window, even if they're present in a further-nested container.
 
=== 窗口名称 ===
{{SVersion|1.5}}
定义脚本化GUI使用的接口容器。


引用的接口容器的类型应为containerWindowType。
Most commonly, the containers used by scripted GUI are defined inside of fully separate interface files to ensure no overlap.
== Parent window ==
By default, a newly-created container window will show up on top of most other UI elements. Assigning a parent window is done in order to make the UI be attached to a particular container. This makes the container window only visible when the parent window is visible, assigns the drawing order to be directly on top of the parent's elements, and makes the container follow the animations done by the parent.


脚本化GUI为脚本化GUI的每个有效用户创建指定容器及其元素的实例。
There are two ways to do so in particular:
 
* <code>parent_window_token = top_bar</code> will attach to a particular base game window identified by its token.
=== Parent Window Token ===
* <code>parent_window_name = container_name</code> will attach it to a container which has the specified name.
{{SVersion|1.5}}
{{VisibleCollapse|
Defines which existing window the scripted GUI interface should be attached to. This is optional, by default a scripted GUI interface will appear anywhere whilst its '''visible''' scope is valid.
 
The following tokens can be used:
* top_bar
* top_bar
* decision_tab
* decision_tab
第112行: 第47行:
* deployment_tab
* deployment_tab
* logistics_tab
* logistics_tab
* diplomacy_tab (only when no country is selected)
* diplomacy_tab (No selected country)
* national_focus
* politics_tab
* politics_tab
* selected_country_view
* selected_state_view
* selected_country_view_info (Info ledger view of another country)
* selected_country_view_diplomacy (Diplomacy view of a country)
* army_ledger
* navy_ledger
* civilian_ledger
* air_ledger
* tech_infantry_folder
* tech_infantry_folder
* tech_support_folder
* tech_support_folder
第125行: 第69行:
* tech_electronics_folder
* tech_electronics_folder
* tech_industry_folder
* tech_industry_folder
* national_focus
|List of possible arguments for parent_window_token}}
For <code>parent_window_token</code>, a notable argument is <code>top_bar</code>. The topbar is positioned lower in the drawing order than most pop-up menus in the game. Therefore, assigning a scripted GUI to have a parent of the topbar will make it be hidden by menus such as the national focus selection or the politics tab. As the topbar container actually covers the entire screen, using it as the token will not result in the container being clipped off.


You can specifiy the exact window name (i.e. <code>tech_tree_my_modded_tree</code>) if none of the tokens above are suitable. If using this method, you must use parent_window_name instead of parent_window_token.
<code>parent_window_name = example_container</code> can be used with most containers. If the parent container is itself independent, then directly entering the name of the container will result in the scripted GUI's window be attached to it.<br/>
 
If the parent container is nested inside another one, then it's necessary to append _instance. As such, a nested container with <code>name = "nested_container"</code> can be assigned as a parent with <code>parent_window_name = nested_container_instance</code>. This can also be used to place the scripted GUI lower in the drawing order. For example, to create a custom dynamic background for a base game's UI window, it's possible to create an "anchor" container window inside of it and then use it as the parent window:
Using the '''selected_state_view''' or '''selected_country_view''' contexts will scope to the last selected country or state when used with these windows.
<pre>guiTypes = {
 
   containerWindowType = {
=== Context Type ===
     name = "countrypoliticsview"
{{SVersion|1.5}}
     position = { x=-606 y=78}
Defines the context of the GUI, i.e. what scope the GUI operates on.
     size = { width=550 height=100%% }
    
     <...>  
    
     iconType ={
       name ="pol_view_bg"
       spriteType = "GFX_pol_view_bg"
       position = { x= 0 y = 44 }
       Orientation = "UPPER_LEFT"
     }  
    
     containerWindowType = { # This container will be placed right above the background of the political view and below each of its elements
       name = "politics_anchor" # The scripted GUI will have parent_window_name = politics_anchor_instance
       position = { x = 0 y = 44 } # This will ensure that, for example, national spirits will be placed above the scripted GUI and not get hidden.
       size = { width = 100% height = 100% }
     }
    
     <...>
   }
}</pre>


The options are:
<code>parent_window_name</code> is unable to accept container windows that are used by a gridboxType.
* player_context
== Context ==
* selected_country_context
The context determines the information about the game state that is available to the scripted GUI to read or modify. This is determined with <code>context_type = player_context</code> in the scripted GUI. If unset, no information will be given to the UI, making it impossible to have it have any effect or be dynamic, including in localisation. Assigning any context type will make it possible to use the scripted GUI to modify the game's state, in part using any [[effects]] and [[triggers]], including [[scopes]].
* selected_state_context
* decision_category


'''Player context''' makes the GUI context that of the country that GUI is displayed for. This means the default scope is the country using the GUI.
In each of the options, [[Scopes#ROOT|ROOT]] always corresponds to the country that sees the scripted GUI (or "player country", which may be controlled by AI). However, the default scope depends on the context.


'''Selected country context''' makes the GUI context that of the currently selected country (i.e. clicked on). This means the default scope is the selected country, and ROOT is the country using the GUI.
{| class="wikitable sortable mw-collapsible" style="display: inline-block; padding: 5px" width="100%"
|+ style="white-space:nowrap;" | Context types:<br/>
! width="10%" |Name
! width="80%" |Description
|-
| player_context
| The baseline context. The default scope is the player country, which is also always ROOT in every context.
|-
| selected_country_context
| The default scope is the currently-selected country. The scripted GUI will not appear if no country is selected. AI evaluates this for every other country, unless otherwise restricted.
|-
| selected_state_context
| The default scope is the currently-selected state. The scripted GUI will not appear if no state is selected. AI evaluates this for every state, unless otherwise restricted.
|-
| decision_category
| Allows the GUI to read a decision category as context in order to embed itself in its description. [[Decision modding#Scripted GUI|Necessary to attach the scripted GUI to a decision category with the <code>scripted_gui</code> attribute in the category.]] The scripted GUI shouldn't have any parent window. Temporary data strcuture usage may not always work correctly with this context type.
|-
| diplomatic_action
| Allows the GUI to read a scripted diplomatic action as context in order to be sent to the target upon its selection. Necessary to attach the scripted GUI to a scripted diplomatic action in a {{path|common/scripted_diplomatic_actions/*.txt}} file.
|-
| country_mapicon
| The GUI will show up next to every country when viewing the world map. The default scope is the country that it appears on.
|-
| state_mapicon
| The GUI will show up next to every state when viewing the world map. The default scope is the state that it appears on.
|}
If the default scope differs from ROOT, then [[PREV]] is useful to refer to it with further scoping.
== Dirty ==
Defines when the scripted GUI should be updated; by default scripted GUIs are updated on hourly tick and for every change to game conditions. For large GUIs, this can make them quite performance heavy; as such it's recommended to use a [https://en.wikipedia.org/wiki/Dirty_bit dirty][[Data structures#Variables|variable]] to tell the game when to update a GUI instead of having them be updated constantly. If you do, the GUI will only update when the variable's value changes. Dirty variables can be declared like such:
dirty = example_variable


'''Selected state context''' makes the GUI context that of the currently selected state (i.e. clicked on). This means the default scope is the selected state, and ROOT is the country using the GUI.
== Visible ==
 
'''Decision category''' makes the GUI context that of the country that GUI is displayed for. This is the only scope that can be implemented into decision categories, like the congress system for USA.
 
When using the selected contexts, it is important to remember that the default scope is that of the selected type, and not the country using the GUI itself, meaning you need to explicitly scope to ROOT when dealing with said country.
 
=== Visible ===
{{SVersion|1.5}}


Defines when the scripted GUI is visible for the current scope.
Defines when the scripted GUI is visible for the current scope.
第158行: 第142行:
Note that the scripted GUI has to be visible for the AI if it is to use it.
Note that the scripted GUI has to be visible for the AI if it is to use it.


=== Effects ===
== Effects ==
{{SVersion|1.5}}


Defines what effects should be attached to button elements within the interface. '''<element>''' should be the name of the element.
Defines what effects should be attached to button elements within the interface. '''<element>''' should be the name of the element.
第173行: 第156行:
For example: <code>my_button_alt_right_click = { }</code>
For example: <code>my_button_alt_right_click = { }</code>


=== Triggers ===
== Triggers ==
{{SVersion|1.5}}


Defines when interface elements are usable or visible. By default elements are clickable and visible.
Defines when interface elements are usable or visible. By default elements are clickable and visible. Note that this allows the manipulation of single elements without affecting the overall parent window.


<code><element>_<modifiers>_click_enabled</code> evaluates when the specified element should be clickable. 
<code><element>_<modifiers>_click_enabled</code> evaluates when the specified element should be clickable. 
第185行: 第167行:


Note that if an element is not visible, the AI will be unable to click on it as well.
Note that if an element is not visible, the AI will be unable to click on it as well.
===Temporary variable usage===
In addition to determining when an element is clickable or visible, the <code>triggers</code> block of a Scripted GUI can also have temporary variables (as well as temporary arrays) set inside of an <code><element>_<trigger></code> block and those temporary data structures can be used in both the <code>properties</code> and <code>dynamic_lists</code> section of a Scripted GUI.
Here is an example of this concept in action:
triggers = {
my_temp_array_calculator_click_enabled = {
clear_temp_array = my_temp_array
all_of = {
array = my_array
add_to_temp_array = { my_temp_array = v }
}
}
my_element_click_enabled = {
if = {
limit = {
has_completed_focus = my_focus
}
set_temp_variable = { my_temp_var = 1 }
}
else = {
set_temp_variable = { my_temp_var = 2 }
}
}
}
properties = {
my_element = {
frame = my_temp_var
}
}
dynamic_lists = {
my_gridbox = {
array = my_temp_array
entry_container = "my_entry_container"
}
}
== Dynamic Lists ==
This is a special feature that is to be used in tandem with grid boxes within interface modding. It will draw a gui for each value inside of the array chosen where the grid box with the same name is.
Here is some example code of how the scripted gui should look:
<pre>
scripted_gui = {
Example_GUI = {
context_type = player_context
window_name = "Example_GUI_Window"
visible = {
}
dynamic_lists = {
Example_Grid_Box = {
#The array to use. Will call a gui for each index.
array = Example_Array
#value of the current array index. Optional. Default value is v.
value = v
#current array index. Optional. Default value is i.
index = i
#This will change the scope to the value if yes
change_scope = no
#The GUI drawn for each index
entry_container = "Example_GUI_Array"
}
}
effects = {
#Example Buttons to show it working
Button_1_click = {
add_to_array = {Example_Array = 1}
}
Button_2_click = {
remove_from_array = {Example_Array = 1}
}
}
triggers = {
}
properties = {
}
}
}
</pre>
And this would be the corresponding gui:
<pre>
guiTypes = {
#Example Scripted GUI Window
containerWindowType = {
name = "Example_GUI_Window"
size = { width = 400 height = 300 }
Orientation = upper_left
moveable = yes
position = { x = 650 y = 250 }
background = {
name = "Background"
quadTextureSprite = "GFX_tiled_window2_1b_border"
}
#Not required to put the gridbox within another container, but it allows for scrolling with the correct background and scrollbar type. This one is transparent.
#Example Buttons for array
buttonType = {
name ="Button_1"
quadTextureSprite ="GFX_button_261x34"
position = { x=50 y=15 }
#This will write onto the button the value and index of the array
buttonText = "Add 1 to array"
buttonFont = "hoi_16mbs"
Orientation = centre
}
buttonType = {
name ="Button_2"
quadTextureSprite ="GFX_button_261x34"
position = { x=50 y=50 }
#This will write onto the button the value and index of the array
buttonText = "Remove 1 from array"
buttonFont = "hoi_16mbs"
Orientation = centre
}
containerWindowType = {
name = "Example_Container_For_Grid"
Orientation = upper_left
size = {width=400 height=200}
position = {x=0 y=100}
verticalScrollbar = "right_vertical_slider_intel"
scroll_wheel_factor = 40
smooth_scrolling = yes
background = {
name = "Background"
quadTextureSprite = "GFX_tiled_window_transparent"
}
gridBoxType = {
name = "Example_Grid_Box"
position = { x = 50 y = 0}
#The size of the gridbox
size = { width = 100%% height = 100%% }
Orientation = upper_left
#The slot size
slotsize = {width=400 height=50 }
format = "UPPER_LEFT"
#the maximum horizontal gui elements
max_slots_horizontal = 1
#the maximum vertical gui elements
#max_slots_vertical = 1
}
}
}
#Example GUI for each Array Index. This will use an already existing button as an example.
containerWindowType = {
name = "Example_GUI_Array"
size = {width=400 height=50}
buttonType = {
name ="Example_GUI"
quadTextureSprite ="GFX_button_261x34"
position = { x=0 y=0 }
#This will write onto the button the value and index of the array
buttonText = "v: [?v] i: [?i]"
buttonFont = "hoi_16mbs"
}
}
}
</pre>


== Properties ==
== Properties ==
第193行: 第355行:
Properties support manipulating the frame of a texture directly with the '''frame''' attribute, which changes the frame to the value given by a specified variable.
Properties support manipulating the frame of a texture directly with the '''frame''' attribute, which changes the frame to the value given by a specified variable.


Likewise, properties support manipulating the x and y co-oordinates for an element, with the '''x''' and '''y''' attributes which changes the element position to the value given by a specified variable.
Likewise, properties support manipulating the x and y coordinates for an element, with the '''x''' and '''y''' attributes which change the element position to the value given by a specified variable.


The '''image''' attribute determines the texture used:
The '''image''' attribute determines the texture used:
第206行: 第368行:
This would assign an '''icon element''' with the name of '''my_icon''' with a texture '''GFX_my_texture'''. 
This would assign an '''icon element''' with the name of '''my_icon''' with a texture '''GFX_my_texture'''. 


At first glance this may seem useless, but the power here is that the string accepts scripted localization. This means you can create a scripted localization entry that supplies the texture string.
At first glance, this may seem useless, but the power here is that the string accepts scripted localization. This means you can create a scripted localization entry that supplies the texture string.


<pre>
<pre>
第236行: 第398行:


This is an example of using scripted localization to supply the property with the texture string.
This is an example of using scripted localization to supply the property with the texture string.
Note you can also refer to the exact frame within the string, like so:
<pre>
text = {
   localization_key = "GFX_my_texture:4"
}
</pre>


== AI ==
== AI ==
第251行: 第405行:


=== AI enabled ===
=== AI enabled ===
{{SVersion|1.5}}


The first step is to determine when the AI is enabled for the GUI.
The first step is to determine when the AI is enabled for the GUI.
第266行: 第419行:


=== AI Test Attributes ===
=== AI Test Attributes ===
{{SVersion|1.5}}


These attributes determine when the AI checks the scripted GUI.
These attributes determine when the AI checks the scripted GUI.


'''ai_test_interval''' determines how often the AI checks, in hours.
'''ai_test_interval''' determines how often the AI checks, in hours. By default set to 24.<br/>
'''ai_test_variance''' determines the variance in time for the check, which is the specified variance divided by 2 for plus and minus.
'''ai_test_variance''' determines the variance in time (In percent) for the check, which is the specified variance divided by 2 for plus and minus. By default set to 0.2 <br/>
As such, an interval of 100 with a variance of 0.2 will make the AI check every 90 to 110 hours (100±[10% of 100, which is 10]).


=== AI Check ===
=== AI Check ===
{{SVersion|1.5}}


This scope is checked on every tick of the interval specified by '''ai_test_interval'''. If it evaluates as false, the AI will ignore the GUI for the tick.
This scope is checked on every tick of the interval specified by '''ai_test_interval'''. If it evaluates as false, the AI will ignore the GUI for the tick.


=== AI Test Scopes ===
=== AI Test Scopes ===
{{SVersion|1.5}}


The AI needs to be told who it should target when using scripted GUIs. You can imagine this as the AI clicking on the countries or states.
The AI needs to be told who it should target when using scripted GUIs. You can imagine this as the AI clicking on the countries or states.
第297行: 第448行:
* test_neighbouring_enemy_countries
* test_neighbouring_enemy_countries


You can combined multiple '''ai_test_scopes''' by adding multiple to the definition.
You can combine multiple '''ai_test_scopes''' by adding multiple to the definition.


For '''selected_state_context''' the AI will check every state in the world by default. By specifying an '''ai_test_scopes''', the AI will be limited to the specified range of states.
For '''selected_state_context''' the AI will check every state in the world by default. By specifying an '''ai_test_scopes''', the AI will be limited to the specified range of states.
第316行: 第467行:
* test_contesded_states - states that shares provinces between enemy countries
* test_contesded_states - states that shares provinces between enemy countries


You can combined multiple '''ai_test_scopes''' by adding multiple to the definition.
You can combine multiple '''ai_test_scopes''' by adding multiple to the definition.


You can define '''test_if_only_major''' which will limit the other scopes to only major countries or states owned by them.
You can define '''test_if_only_major''' which will limit the other scopes to only major countries or states owned by them.
第323行: 第474行:


=== AI Check Scope ===
=== AI Check Scope ===
{{SVersion|1.5}}


Now the AI knows what to target, it is possible to filter these with triggers. 
Now the AI knows what to target, it is possible to filter these with triggers. 
第334行: 第484行:


=== AI Weights ===
=== AI Weights ===
{{SVersion|1.5}}


The AI also needs to be told how to handle the actual clickable elements within the GUI. This happens with the '''ai_weights''' scope.
The AI also needs to be told how to handle the actual clickable elements within the GUI. This happens with the '''ai_weights''' scope.
第346行: 第495行:
By default, the AI will only select one element per tick, clicking the element with the highest score. To allow the AI to click more than one element, specify '''ai_max_weight_taken_per_test = <int>''', which sets the maximum number of actions the AI will take.
By default, the AI will only select one element per tick, clicking the element with the highest score. To allow the AI to click more than one element, specify '''ai_max_weight_taken_per_test = <int>''', which sets the maximum number of actions the AI will take.


By default each action has an AI weight of 1. You can define a higher ''cost'' by adding the '''weight''' attribute to the '''ai_will_do'''. 
By default, each action has an AI weight of 1. You can define a higher ''cost'' by adding the '''weight''' attribute to the '''ai_will_do'''. 


The '''ignore_lower_weights''' attribute is added to the '''ai_will_do''' scope to tell the AI to ignore every action below the specified weight whilst its factor is above 0. 
The '''ignore_lower_weights''' attribute is added to the '''ai_will_do''' scope to tell the AI to ignore every action below the specified weight whilst its factor is above 0. 


This allows the AI to ignore other actions as it may need to ''save'' a resource to take the action. Without this, the AI would take another action that may take the same resource and so would never take the first action, even when the factor is higher, as it would never save enough resource to take it.
This allows the AI to ignore other actions as it may need to ''save'' a resource to take the action. Without this, the AI would take another action that may take the same resource and so would never take the first action, even when the factor is higher, as it would never save enough resources to take it.


=== AI Parents ===
=== AI Parents ===
{{SVersion|1.5}}
{{SVersion|Timeless}}
 
You can define an AI parent that means the AI for different scripted GUIs is processed together. This is useful if different GUIs may use resources that another would deplete, and so by processing the AI together, the actions can be properly limited.
== Template ==


You can define an AI parent that means the AI for different scripted GUIs is processed together. This is useful if different GUIs may use resources that another would depelete, and so by processing the AI together, the actions can be properly limited.
<pre>
scripted_gui = {
   <name> = {
     window_name = <string>
     context_type = <type>
     parent_window_token = <string>
    
     visible = {
       <triggers>
     }
    
     effects = {
       <element>_click = {
         <effects>
       }
       <element>_<modifier>_click = {
         <effects>
       }
     }
    
     triggers = {
       <element>_click_enabled = {
         <triggers>
       }
       <element>_visible = {
         <triggers>
       }
     }
    
     properties = {
       <element> = {
         image = <string>
         frame = <var>
         x = <var>
         y = <var>
       }
     }
    
     ai_enabled = {
       <triggers>
     }
    
     ai_test_parent = <string>
    
     ai_test_interval = <int>
     ai_test_variance = <float>
     ai_test_scopes = <type>
    
     ai_check = {
       <triggers>
     }
    
     ai_check_scope = {
       <triggers>
     }
    
     ai_max_weight_taken_per_test = <int>
    
     ai_weights = {
       <element>_<modifiers>_click = {
         ai_will_do = {
           base = <int>
           factor = <float>
           add = <float>
          
           modifier = {
             factor = <float>
             add = <float>
            
             <triggers>
           }
         }
         ignore_lower_weights = {
           factor = <float>
         }
       }
     }
   }
}
</pre>


== Scripted Localization ==
== Scripted Localization ==
{{SVersion|1.5}}
{{SVersion|Timeless}}


There is a special localization command that allows a scripted GUI element to display its effect within a tooltip.
There is a special localization command that allows a scripted GUI element to display its effect within a tooltip.


<code>[!<element>]</code>
<code>[!<element>_<action>]</code>


For example:
For example:
第368行: 第599行:
<code> MY_TOOLTIP: "[!test_button_click]"</code>
<code> MY_TOOLTIP: "[!test_button_click]"</code>


== Tips ==
A similar command is available for the triggers for that particular element.
{{SVersion|1.5}}


You can use the console command '''human_ai''' to enable AI for the current human country. This writes the AI checks and the results of said checks to the ''scripted_ai.log''.
<code>[!<element>_<action>_enabled]</code>


Event targets '''CANNOT''' be used in scripted GUIs, it will break everything. Make use of variable scopes instead.
For example:


Scripted effects and triggers can be used in scripted GUIs, and can be useful to seperate the GUI logic and the gameplay logic.
<code> MY_TOOLTIP: "[!test_button_click_enabled]"</code>


Enable '''Debug''' mode by adding -debug to your launch options in Steam. Not only this mode will give you access to error logs, you can also use '''Control + Alt + Right Click''' combination to open a debug menu which can be used for access the gui file that you hover on. By default this will open the file in notepad. Following lines can be added to settings.txt under '''\Documents\Paradox Interactive\Hearts of Iron IV\''' to use your favorite editor. (Replace the path with your actual exe path if it differs)
== Tips ==
{{SVersion|Timeless}}


For '''Notepad++'''<pre>
You can use the console command '''human_ai''' to enable AI for the current human country. This writes the AI checks and the results of said checks to the ''scripted_ai.log''.
editor="C:\\Program Files\\Notepad++\\notepad++.exe"
editor_postfix=" -n$"</pre>


For '''Sublime'''<pre>
Event targets '''CANNOT''' be used in scripted GUIs, it will break everything. Make use of variable scopes instead.
editor="C:\\Program Files\\Sublime Text 3\\sublime_text.exe"
editor_postfix=":$:1"</pre>


Scripted effects and triggers can be used in scripted GUIs, and can be useful to separate the GUI logic and the gameplay logic.
== See also ==
* [[forum:1084697|Scripted GUI Tutorial]] by a Paradox developer.
* Script examples by a Paradox developer: [https://gitlab.com/shultays/script_examples Gitlab], [https://steamcommunity.com/sharedfiles/filedetails/?id=1678247250 Steam].
{{Modding navbox}}
{{Modding navbox}}
[[分类:Modding]]
[[分类:Modding]]

2024年8月31日 (六) 20:36的最新版本

Scripted GUIs are used to assign scripted functionality to newly-created elements in the user interface, allowing it to modify the game's state or change depending on conditions.

Scripted GUIs are defined as /Hearts of Iron IV/common/scripted_guis/*.txt files. The folder name must be in plural. The scripted GUI will assign functionality to user interface elements, which themselves are defined in /Hearts of Iron IV/interface/*.gui files.

Most commonly, flags, variables, and arrays are used in the implementation of GUI. For example, implementing a button that opens up a window is most commonly done by assigning a flag to be set on the button press and making the window visibility depend on the flag being set on the country.

A scripted GUI is defined as a block within a scripted_gui = { ... } container, where the name of the block determines the internal ID of the scripted GUI.

Window assignment

Scripted GUIs are assigned to containerWindowTypes and apply to every interface element located within them. This is done with window_name = example_container.

In order for a container to be a valid target for scripted GUI, it must be entirely independent, not present in any other container window. For example, in the following code in an /Hearts of Iron IV/interface/*.gui file, only independent_container can be assigned:

guiTypes = {
    containerWindowType = {
        name = "independent_container" # Can be used in the window_name of scripted GUI
        position = { x = 0 y = 0 }
        size = { width = 100% height= 210 }
        
        containerWindowType = {
            name = "nested_container" # Located inside of another container, thus impossible to assign scripted GUI
            position = { x = 0 y = 0 }
            size = { width = 100 height= 100 }
        }
    }
}

The scripted GUI will affect every single element located inside of the container window, even if they're present in a further-nested container.

Most commonly, the containers used by scripted GUI are defined inside of fully separate interface files to ensure no overlap.

Parent window

By default, a newly-created container window will show up on top of most other UI elements. Assigning a parent window is done in order to make the UI be attached to a particular container. This makes the container window only visible when the parent window is visible, assigns the drawing order to be directly on top of the parent's elements, and makes the container follow the animations done by the parent.

There are two ways to do so in particular:

  • parent_window_token = top_bar will attach to a particular base game window identified by its token.
  • parent_window_name = container_name will attach it to a container which has the specified name.
List of possible arguments for parent_window_token
  • top_bar
  • decision_tab
  • technology_tab
  • trade_tab
  • construction_tab
  • production_tab
  • deployment_tab
  • logistics_tab
  • diplomacy_tab (No selected country)
  • national_focus
  • politics_tab
  • selected_country_view
  • selected_state_view
  • selected_country_view_info (Info ledger view of another country)
  • selected_country_view_diplomacy (Diplomacy view of a country)
  • army_ledger
  • navy_ledger
  • civilian_ledger
  • air_ledger
  • tech_infantry_folder
  • tech_support_folder
  • tech_armor_folder
  • tech_artillery_folder
  • tech_land_doctrine_folder
  • tech_naval_folder
  • tech_naval_doctrine_folder
  • tech_air_techs_folder
  • tech_air_doctrine_folder
  • tech_electronics_folder
  • tech_industry_folder


For parent_window_token, a notable argument is top_bar. The topbar is positioned lower in the drawing order than most pop-up menus in the game. Therefore, assigning a scripted GUI to have a parent of the topbar will make it be hidden by menus such as the national focus selection or the politics tab. As the topbar container actually covers the entire screen, using it as the token will not result in the container being clipped off.

parent_window_name = example_container can be used with most containers. If the parent container is itself independent, then directly entering the name of the container will result in the scripted GUI's window be attached to it.
If the parent container is nested inside another one, then it's necessary to append _instance. As such, a nested container with name = "nested_container" can be assigned as a parent with parent_window_name = nested_container_instance. This can also be used to place the scripted GUI lower in the drawing order. For example, to create a custom dynamic background for a base game's UI window, it's possible to create an "anchor" container window inside of it and then use it as the parent window:

guiTypes = {
    containerWindowType = {
        name = "countrypoliticsview"
        position = { x=-606 y=78}
        size = { width=550 height=100%% }
        
        <...>    
        
        iconType ={
            name ="pol_view_bg"
            spriteType = "GFX_pol_view_bg"
            position = { x= 0 y = 44 }
            Orientation = "UPPER_LEFT"
        }    
        
        containerWindowType = { # This container will be placed right above the background of the political view and below each of its elements
            name = "politics_anchor" # The scripted GUI will have parent_window_name = politics_anchor_instance
            position = { x = 0 y = 44 } # This will ensure that, for example, national spirits will be placed above the scripted GUI and not get hidden.
            size = { width = 100% height = 100% }
        }
        
        <...>
    }
}

parent_window_name is unable to accept container windows that are used by a gridboxType.

Context

The context determines the information about the game state that is available to the scripted GUI to read or modify. This is determined with context_type = player_context in the scripted GUI. If unset, no information will be given to the UI, making it impossible to have it have any effect or be dynamic, including in localisation. Assigning any context type will make it possible to use the scripted GUI to modify the game's state, in part using any effects and triggers, including scopes.

In each of the options, ROOT always corresponds to the country that sees the scripted GUI (or "player country", which may be controlled by AI). However, the default scope depends on the context.

Context types:
Name Description
player_context The baseline context. The default scope is the player country, which is also always ROOT in every context.
selected_country_context The default scope is the currently-selected country. The scripted GUI will not appear if no country is selected. AI evaluates this for every other country, unless otherwise restricted.
selected_state_context The default scope is the currently-selected state. The scripted GUI will not appear if no state is selected. AI evaluates this for every state, unless otherwise restricted.
decision_category Allows the GUI to read a decision category as context in order to embed itself in its description. Necessary to attach the scripted GUI to a decision category with the scripted_gui attribute in the category. The scripted GUI shouldn't have any parent window. Temporary data strcuture usage may not always work correctly with this context type.
diplomatic_action Allows the GUI to read a scripted diplomatic action as context in order to be sent to the target upon its selection. Necessary to attach the scripted GUI to a scripted diplomatic action in a /Hearts of Iron IV/common/scripted_diplomatic_actions/*.txt file.
country_mapicon The GUI will show up next to every country when viewing the world map. The default scope is the country that it appears on.
state_mapicon The GUI will show up next to every state when viewing the world map. The default scope is the state that it appears on.

If the default scope differs from ROOT, then PREV is useful to refer to it with further scoping.

Dirty

Defines when the scripted GUI should be updated; by default scripted GUIs are updated on hourly tick and for every change to game conditions. For large GUIs, this can make them quite performance heavy; as such it's recommended to use a dirtyvariable to tell the game when to update a GUI instead of having them be updated constantly. If you do, the GUI will only update when the variable's value changes. Dirty variables can be declared like such:

dirty = example_variable

Visible

Defines when the scripted GUI is visible for the current scope.

Note that the scripted GUI has to be visible for the AI if it is to use it.

Effects

Defines what effects should be attached to button elements within the interface. <element> should be the name of the element.

There are additional modifiers added before the click suffix that alter the type of click recognized. These are fairly obvious to understand, and can be chained.

Modifiers:

  • right
  • alt
  • control
  • shift

For example: my_button_alt_right_click = { }

Triggers

Defines when interface elements are usable or visible. By default elements are clickable and visible. Note that this allows the manipulation of single elements without affecting the overall parent window.

<element>_<modifiers>_click_enabled evaluates when the specified element should be clickable.

Note that <element>_click_enabled overrides the specific triggers such as <element>_right_click_enabled.

<element>_visible evaluates when the specified element should be visible. This can be used for any interface element, such as icons and text.

Note that if an element is not visible, the AI will be unable to click on it as well.

Temporary variable usage

In addition to determining when an element is clickable or visible, the triggers block of a Scripted GUI can also have temporary variables (as well as temporary arrays) set inside of an <element>_<trigger> block and those temporary data structures can be used in both the properties and dynamic_lists section of a Scripted GUI.

Here is an example of this concept in action:

triggers = {
	my_temp_array_calculator_click_enabled = {
		clear_temp_array = my_temp_array
		all_of = {
			array = my_array
			add_to_temp_array = { my_temp_array = v }
		}
	}

	my_element_click_enabled = {
		if = {
			limit = {
				has_completed_focus = my_focus
			}
			set_temp_variable = { my_temp_var = 1 }
		}
		else = {
			set_temp_variable = { my_temp_var = 2 }
		}
	}
}

properties = {
	my_element = {
		frame = my_temp_var
	}
}

dynamic_lists = {
	my_gridbox = {
		array = my_temp_array
		entry_container = "my_entry_container"
	}
}

Dynamic Lists

This is a special feature that is to be used in tandem with grid boxes within interface modding. It will draw a gui for each value inside of the array chosen where the grid box with the same name is.

Here is some example code of how the scripted gui should look:

scripted_gui = {
	Example_GUI = {
	
		context_type = player_context
		
		window_name = "Example_GUI_Window"

		visible = {
			
		}

		dynamic_lists = {
			Example_Grid_Box = {
					#The array to use. Will call a gui for each index.
					array = Example_Array

					#value of the current array index. Optional. Default value is v.
					value = v

					#current array index. Optional. Default value is i.
					index = i
					
					#This will change the scope to the value if yes
					change_scope = no

					#The GUI drawn for each index
					entry_container = "Example_GUI_Array"
			}
		}
		effects = {
			#Example Buttons to show it working
			Button_1_click = {
				add_to_array = {Example_Array = 1}
			}
			Button_2_click = {
				remove_from_array = {Example_Array = 1}
			}
		}
		triggers = {

		}
		properties = {

		}
	}
}

And this would be the corresponding gui:

guiTypes = {
	#Example Scripted GUI Window
	containerWindowType = {
		name = "Example_GUI_Window"
		size = { width = 400 height = 300 }
		Orientation = upper_left
		moveable = yes
		position = { x = 650 y = 250 }
					
		background = {
			name = "Background"
			quadTextureSprite = "GFX_tiled_window2_1b_border"
		}
		
		#Not required to put the gridbox within another container, but it allows for scrolling with the correct background and scrollbar type. This one is transparent.
			
		#Example Buttons for array
		buttonType = {
			name ="Button_1"
			quadTextureSprite ="GFX_button_261x34"
			position = { x=50 y=15 }
			#This will write onto the button the value and index of the array
			buttonText = "Add 1 to array"
			buttonFont = "hoi_16mbs"
			Orientation = centre
		}
			
		buttonType = {
			name ="Button_2"
			quadTextureSprite ="GFX_button_261x34"
			position = { x=50 y=50 }
			#This will write onto the button the value and index of the array
			buttonText = "Remove 1 from array"
			buttonFont = "hoi_16mbs"
			Orientation = centre
		} 
		containerWindowType = {
			name = "Example_Container_For_Grid"
			Orientation = upper_left
			size = {width=400 height=200}
			position = {x=0 y=100}
			verticalScrollbar = "right_vertical_slider_intel"
			scroll_wheel_factor = 40
			smooth_scrolling = yes
					
			background = {
				name = "Background"
				quadTextureSprite = "GFX_tiled_window_transparent"
			}
			
			gridBoxType = {
				name = "Example_Grid_Box"
				position = { x = 50 y = 0}
				#The size of the gridbox
				size = { width = 100%% height = 100%% }
				Orientation = upper_left
				#The slot size
				slotsize = {width=400 height=50 }
				format = "UPPER_LEFT"
				#the maximum horizontal gui elements
				max_slots_horizontal = 1
				#the maximum vertical gui elements
				#max_slots_vertical = 1
			}
		}
	}

	#Example GUI for each Array Index. This will use an already existing button as an example.
	containerWindowType = {
		name = "Example_GUI_Array"
		size = {width=400 height=50}
			
		buttonType = {
			name ="Example_GUI"
			quadTextureSprite ="GFX_button_261x34"
			position = { x=0 y=0 }
			#This will write onto the button the value and index of the array
			buttonText = "v: [?v] i: [?i]"
			buttonFont = "hoi_16mbs"
		}
	}
}

Properties

Properties allow for specified elements to be manipulated whilst in-game, allowing for their texture and position to be changed.

Properties support manipulating the frame of a texture directly with the frame attribute, which changes the frame to the value given by a specified variable.

Likewise, properties support manipulating the x and y coordinates for an element, with the x and y attributes which change the element position to the value given by a specified variable.

The image attribute determines the texture used:

properties = {
    my_icon = {
        image = "GFX_my_texture"
    }
}

This would assign an icon element with the name of my_icon with a texture GFX_my_texture.

At first glance, this may seem useless, but the power here is that the string accepts scripted localization. This means you can create a scripted localization entry that supplies the texture string.

properties = {
    my_icon = {
        image = "[get_my_icon_texture]"
    }
}
defined_text = {
    name = get_my_icon_texture
    
    text = {
        trigger = {
            <triggers>
        }
        localization_key = "GFX_my_texture_1"
    }
    text = {
        trigger = {
            <triggers>
        }
        localization_key = "GFX_my_texture_2"
    }
}

This is an example of using scripted localization to supply the property with the texture string.

AI

The true usefulness of scripted GUIs is that they can be used by the AI, meaning new features can be properly implemented with custom interfaces that the AI can navigate.

AI enabled

The first step is to determine when the AI is enabled for the GUI.

ai_enabled = {
    <triggers>
}

This scope is checked during initialization, and any AI that fail are never checked again.

This is the best optimization, as it means invalid AIs will never be checked again, but it is only checked once.

AI Test Attributes

These attributes determine when the AI checks the scripted GUI.

ai_test_interval determines how often the AI checks, in hours. By default set to 24.
ai_test_variance determines the variance in time (In percent) for the check, which is the specified variance divided by 2 for plus and minus. By default set to 0.2
As such, an interval of 100 with a variance of 0.2 will make the AI check every 90 to 110 hours (100±[10% of 100, which is 10]).

AI Check

This scope is checked on every tick of the interval specified by ai_test_interval. If it evaluates as false, the AI will ignore the GUI for the tick.

AI Test Scopes

The AI needs to be told who it should target when using scripted GUIs. You can imagine this as the AI clicking on the countries or states.

You can specify nothing, but this will result in the AI checking every possible country or state, which will harm performance, and so should be avoided unless required.

For player_context and decision_category, ai_test_scopes is not needed as the only checked scope is the country using the GUI.

For selected_country_context the AI will check every country in the world by default. By specifying an ai_test_scopes, the AI will be limited to the specified range of countries.

The tokens for selected_country_context:

  • test_self_country
  • test_enemy_countries
  • test_ally_countries
  • test_neighbouring_countries
  • test_neighbouring_ally_countries
  • test_neighbouring_enemy_countries

You can combine multiple ai_test_scopes by adding multiple to the definition.

For selected_state_context the AI will check every state in the world by default. By specifying an ai_test_scopes, the AI will be limited to the specified range of states.

The tokens for selected_state_context:

  • test_self_owned_states
  • test_enemy_owned_states
  • test_ally_owned_states
  • test_self_controlled_states
  • test_enemy_controlled_states
  • test_ally_controlled_states
  • test_neighbouring_states
  • test_neighbouring_enemy_states
  • test_neighbouring_ally_states
  • test_our_neighbouring_states - AIs edge states
  • test_our_neighbouring_states_against_allies - AIs edge states to allies
  • test_our_neighbouring_states_against_enemies - AIs edge states to enemies
  • test_contesded_states - states that shares provinces between enemy countries

You can combine multiple ai_test_scopes by adding multiple to the definition.

You can define test_if_only_major which will limit the other scopes to only major countries or states owned by them.

You can define test_if_only_coastal which will limit the other scopes to only coastal countries or states.

AI Check Scope

Now the AI knows what to target, it is possible to filter these with triggers.

ai_check_scope = {
    <triggers>
}

AI Weights

The AI also needs to be told how to handle the actual clickable elements within the GUI. This happens with the ai_weights scope.

You add an entry for the element and define a set of factors within the ai_will_do scope.

You can use factor or add, although only one should be present in the same scope.

Whilst the weight for an element is above 0, the AI will attempt to click it.

By default, the AI will only select one element per tick, clicking the element with the highest score. To allow the AI to click more than one element, specify ai_max_weight_taken_per_test = <int>, which sets the maximum number of actions the AI will take.

By default, each action has an AI weight of 1. You can define a higher cost by adding the weight attribute to the ai_will_do.

The ignore_lower_weights attribute is added to the ai_will_do scope to tell the AI to ignore every action below the specified weight whilst its factor is above 0.

This allows the AI to ignore other actions as it may need to save a resource to take the action. Without this, the AI would take another action that may take the same resource and so would never take the first action, even when the factor is higher, as it would never save enough resources to take it.

AI Parents

You can define an AI parent that means the AI for different scripted GUIs is processed together. This is useful if different GUIs may use resources that another would deplete, and so by processing the AI together, the actions can be properly limited.

Template

scripted_gui = {
    <name> = {
        window_name = <string>
        context_type = <type>
        parent_window_token = <string>
         
        visible = {
            <triggers>
        }
        
        effects = {
            <element>_click = {
                <effects>
            }
            <element>_<modifier>_click = {
                <effects>
            }
        }
        
        triggers = {
            <element>_click_enabled = {
                <triggers>
            }
            <element>_visible = {
                <triggers>
            }
        }
        
        properties = {
            <element> = {
                image = <string>
                frame = <var>
                x = <var>
                y = <var>
            }
        }
        
        ai_enabled = {
            <triggers>
        }
        
        ai_test_parent = <string>
        
        ai_test_interval = <int>
        ai_test_variance = <float>
        ai_test_scopes = <type>
        
        ai_check = {
            <triggers>
        }
        
        ai_check_scope = {
            <triggers>
        }
        
        ai_max_weight_taken_per_test = <int>
        
        ai_weights = {
            <element>_<modifiers>_click = {
                ai_will_do = {
                    base = <int>
                    factor = <float>
                    add = <float>
                    
                    modifier = {
                        factor = <float>
                        add = <float>
                        
                        <triggers>
                    }
                }
                ignore_lower_weights = {
                    factor = <float>
                }
            }
        }
    }
}

Scripted Localization

There is a special localization command that allows a scripted GUI element to display its effect within a tooltip.

[!<element>_<action>]

For example:

MY_TOOLTIP: "[!test_button_click]"

A similar command is available for the triggers for that particular element.

[!<element>_<action>_enabled]

For example:

MY_TOOLTIP: "[!test_button_click_enabled]"

Tips

You can use the console command human_ai to enable AI for the current human country. This writes the AI checks and the results of said checks to the scripted_ai.log.

Event targets CANNOT be used in scripted GUIs, it will break everything. Make use of variable scopes instead.

Scripted effects and triggers can be used in scripted GUIs, and can be useful to separate the GUI logic and the gameplay logic.

See also