This post will show you how to properly setup GUI buttons and popups. The most common (and easiest) way that people are setting up GUI elements, is to edit existing files such as InGame.xml and other GUI element files (such as TopPanel.lua). This is the bad way to do it, because it will create Mod incompatibility with the next modder who comes along and edits InGame.xml or the same file you're using.
The proper way separates your GUI elements from the existing core GUI stuff, so that if a player enables a mod which utilises InGame.xml (incorrectly) it does not impact on your mod.
Civ 5 GUI explained:
Firstly an explanation of how Civ 5's GUI works. The game's GUI works through a layered system. You have a base layer (WorldView) and then place other GUI layers on top (TopPanel, DiploList, etc). See the below diagram.
Notice how layer 1 is the main view (or WorldView as it's called in Civ 5). Then on top of that a number of other layers are placed on top to form the screen. This is how we separate our GUI elements, we create a new layer and place our GUI elements on it.
For this how to, I will demonstrate how to create a new button on the WorldView up with the diplomacy, policies and notifications buttons, and open two screens based on user interaction.
Creating a GUI layer:
The first thing one must do is create a new GUI layer. This is the foundation for letting us place GUI elements on screen, activating them correctly with player interaction, and processing commands based on the GUI element interacted with. I am then going to place a round button on the created layer for the player to interact with to open up my screens.
Code:
<?xml version="1.0" encoding="utf-8"?>
<Context ColorSet="Beige_Black_Alpha" FontStyle="Shadow" Font="TWCenMT16" >
<LuaContext FileName="CTCGSplash" ID="CivTheCardGameSplash" Hidden="True" />
<Container Size="80,80" Padding="0,0" Anchor="R,T" Offset="265,30" ID="CTCGButton" Hidden="1" >
<!--=======================================================================================================================-->
<!-- Civ The Card Game Button -->
<!--=======================================================================================================================-->
<Button ConsumeMouseOver="1" Anchor="C,C" Size="80,80" Offset="0,0" Texture="assets\UI\Art\Notification\NotificationFrameBase.dds" ID="CivTheCardGameButton" ToolTip="Civilization: The Card Game" Hidden="0" >
<ShowOnMouseOver>
<Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\NotificationFrameBase.dds" />
<AlphaAnim Anchor="C,C" AnchorSide="O.O" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\NotificationFrameGlow2.dds" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="1" AlphaEnd="0" Hidden="0"/>
</ShowOnMouseOver>
<Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="Art\cardsbutton.dds" />
<!-- ACTIVE STATE -->
<AlphaAnim Anchor="C,C" AnchorSide="O.O" Offset="0,0" Size="80,80" Texture="Art\cardsbutton.dds" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="1" AlphaEnd="0" Hidden="1"/>
</Button>
</Container>
</Context>
Firstly you'll notice I have not defined a Grid. Grids are used to create outlined windows, but for this element I want transparency around it so that it blends into the interface. Thus I use a simple container.
The next important thing is the LuaContext line. A LuaContext is a Lua defined and controlled object. For LuaContexts these are defined in files. LuaContexts also remove the requirement to define the GUI object in the mod's Content properties as an InGameUIAddin. For this example I am using LuaContext to define the FIRST popup screen of my mod. The name of the file containing the XML definition for the GUI element is called CTCGSplash.xml, and for the LuaContext you set the FileName to the filename of the XML definition (without the extension). I have also given this LuaContext the ID of CivTheCardGameSplash, which is how Lua scripts call that object. The rest of the code defines my animated button, so that the outer rings flashes in the same way the policy and diplomacy buttons do.
The Lua scripting that sits behind this GUI layer definition is extremely simple. All we want this element to do is show in the WorldView and then call a popup window when the button is clicked on.
Code:
-- CTCGButton
-- Author: Thesh
-- DateCreated: 12/9/2010 5:34:24 AM
--------------------------------------------------------------
----------------------------------------------------------------
-- REQUIRED FUNCTIONS - DO NOT DELETE
----------------------------------------------------------------
-- Default show/hide call
function ShowHideHandler( bIsHide, bIsInit )
Controls.CTCGButton:SetHide(false);
end
ContextPtr:SetShowHideHandler( ShowHideHandler );
-- Player click New Game button
Controls.CivTheCardGameButton:RegisterCallback(Mouse.eLClick, function()
UIManager:QueuePopup( Controls.CivTheCardGameSplash, PopupPriority.BarbarianCamp );
end);
In the ShowHideHandler you'll see that the very simple turning on of our container is performed. The ShowHideHandler is called when the player clicks on the "Begin My Journey" button after the Dawn of Man text.
The code for the button click does one simple thing, it queues our popup into the game system, with a priority of BarbarianCamp. The element to be queued up in the system to show, is our LuaContext object.
Little known fact: The priority BarbarianCamp is the highest priority, so will occur instantly. We cannot define our own priorities as yet.
So that is all to our GUI Layer! Now we must create our popups.
Creating Popups:
Creating popup windows is also similarly easy. Once again we will have an XML definition and a behind the scenes Lua script to process the actions and events. Here is the XML for the first screen of the popup:
Code:
<?xml version="1.0" encoding="utf-8"?>
<Context ColorSet="Beige_Black" Font="TwCenMT20" FontStyle="Shadow" >
<LuaContext FileName="CTCG" ID="CivTheCardGameTable" Hidden="True" />
<Container ID="MainSelection" Size="1000,740" Anchor="C,C" >
<Grid Size="1000,740" Anchor="C,C" Offset="0,0" Padding="0,0" Style="Grid9DetailFive140" >
<Label Anchor="C,T" Offset="0,15" Font="TwCenMT24" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" String="Civilization the Card Game" />
<Label Anchor="C,T" Offset="0,60" Font="TwCenMT18" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" String="A game by Soren Johnson and Dale Kent" />
<Image Anchor="C,C" Offset="0,0" ID="Splash" Texture="splash.dds" Size="500,360" />
<Stack Anchor="L,C" Offset="0,0" Padding="12" StackGrowth="Bottom" ID="MainMenuStack" >
<GridButton Style="BaseButton" ID="OKButton" Size="200,40" Anchor="C,C" Offset="20,0" String="New Game" />
<GridButton Style="BaseButton" ID="InstructionButton" Size="200,40" Anchor="C,C" Offset="20,0" String="Instructions" />
<GridButton Style="BaseButton" ID="WPCButton" Size="200,40" Anchor="C,C" Offset="20,0" String="Visit WePlayCiv.com" />
<GridButton Style="BaseButton" ID="BackButton" Size="200,40" Anchor="C,C" Offset="20,0" String="Back" />
</Stack>
</Grid>
</Container>
</Context>
This time we are loading a window (a Grid is defined) which will contain a DDS image and four buttons. These four buttons will do various things, but the two we will focus on for this example are the "New Game" and the "Back" buttons. Also note the LuaContext near the top of the definition which is pointing to the second screen of the popup, a file called CTCG.xml with a Lua definition of CivTheCardGameTable.
And here is the backend Lua script for this popup window:
Code:
-- CTCGSplash
-- Author: Thesh
-- DateCreated: 12/9/2010 7:28:49 AM
--------------------------------------------------------------
----------------------------------------------------------------
-- Includes
----------------------------------------------------------------
include( "IconSupport" );
include( "InstanceManager" );
----------------------------------------------------------------
-- Globals
----------------------------------------------------------------
----------------------------------------------------------------
-- REQUIRED FUNCTIONS - DO NOT DELETE
----------------------------------------------------------------
-- Default show/hide call
function ShowHideHandler( bIsHide, bIsInit )
end
ContextPtr:SetShowHideHandler( ShowHideHandler );
-- Default "OnBack" destructor for this popup window
function OnBack()
UIManager:DequeuePopup( ContextPtr );
end
-- This is the default key mapping function. We only need to capture
-- the ESC key being pressed and map it to OnBack to exit the screen.
ContextPtr:SetInputHandler( function(uiMsg, wParam, lParam)
if uiMsg == KeyEvents.KeyDown then
if wParam == Keys.VK_ESCAPE then
OnBack();
end
end
return true;
end);
-----------------------------------------------------------------
-- Process the main menu buttons
-----------------------------------------------------------------
-- Player clicked the Back button
Controls.BackButton:RegisterCallback(Mouse.eLClick, function()
UIManager:DequeuePopup( ContextPtr );
end);
-- Player click New Game button
Controls.OKButton:RegisterCallback(Mouse.eLClick, function()
UIManager:QueuePopup( Controls.CivTheCardGameTable, PopupPriority.BarbarianCamp );
end);
This script has more content, due to the buttons on the screen. The first button we examine is the Back button. When the player clicks the Back button we want the popup to close and return to the WorldView. And so it is with the OKButton ("New Game" button) when the player clicks on it we want the second screen of our popup to open. So in the Lua script we make a call to QueuePopup to make a popup show on screen, and we make a call to DequeuePopup to close it again.
The ShowHideHandler is completely empty because we do not want to process anything when this window is opened/closed, we simply want to view it.
Mod Properties:
You may now be thinking that all you need to define is LuaContexts to call your GUI elements. This is correct for child GUI elements on your layer, but how do you actually tell the game to load your initial GUI element (the WorldView button in this example). Well, for the inital, base level, parent GUI element on your layer, you must still define a InGameUIAddin on the Content tab of the mod properties window in ModBuddy. But at least now you only need to define your parent elements instead of every single element, and you no longer need to hack InGame.xml.
Closing.....
So that is all there is to creating proper GUI elements in Civ 5. By using this method not only do you retain separate control from the rest of the Civ 5 GUI, but no other mod can interfere with your GUI elements.