Im folgenden Bildschirmphoto können wir die Uhr sehen, die wir hinzugefügt haben (der rote Rahmen existiert nur, damit sie hier leichter zu finden ist, er wurde nicht von der Mod erstellt).

Anleitung: Mit Lua einen neuen Bildschirm hinzufügen
Es gibt während des Spielens keine Möglichkeit zu erfahren, welche Mods geladen sind.
In diesem Abschnitt werden wir einen neuen Bildschirm im Menü „Zusätzliche Informationen“ einfügen, der alle geladenen Mods auflistet.
Es gibt ein paar Hürden, die wir überwinden müssen, bevor wir dem Spiel einen neuen Bildschirm hinzufügen. Wir haben uns in einem früheren Abschnitt durch ein Beispiel für die Benutzung von InGameUIAddin gearbeitet. InGameUIAddin erlaubt uns, neue Benutzeroberflächenaspekte hinzuzufügen, ermöglicht es uns aber nicht, vorhandene Komponenten zu verändern. Dazu müssen wir die Dateien ersetzen. Das Problem mit dem Ersetzen von Lua-Dateien ist, dass das für Konflikte mit anderen Mods sorgt, die dieselben Dateien ersetzen.
Für diese Mod müssen wir die folgenden Dateien ersetzen:
DiploCorner.lua- Diese Lua-Datei steuert die Kontrolle in der oberen rechten Bildschirmecke. Wir werden sie verändern, um unsere neue Menüoption hinzuzufügen.
DiploCorner.xml- Diese XML-Datei steuert die XML-Aspekte der UI-Diplomatie-Kontrolle.
InGame.xml- Das ist der Hauptort, an dem Lua-Dateien registriert werden.
NotificationLogPopup.lua- Diese Datei steuert das Benachrichtigungspopupmenü, wir haben vor, auf dieses Event zurückzugreifen , um unser neues hinzuzufügen.
Und wir werden zwei neue Dateien hinzufügen, um den neuen Bildschirm zu steuern:
ModList.lua- Die Lua-Datei für den neuen Bildschirm.
ModList.xml- Die XML-Datei, die die Definitionen für den neuen Modlistenbildschirm steuert.

1.Unser erster Schritt ist, die Dateien im Lua-Ordner unserer Mod anzulegen wie in dem Bildschirmphoto. Für DiploCorner.lua, DiploCorner.xml, InGame.xml und NotificationLogPopup.lua kopiere die eigentlichen Dateien aus dem Ordner „<Civ5Installationsordner>\Assets\UI\“ in die Entsprechung in unserer Mod.
Die erste Änderung in InGame.xml ist, unsere neue Lua-Datei-Definition zu laden:
Code:
<LuaContext FileName="Assets/UI/InGame/Popups/SetCityName"
<LuaContext FileName="Assets/UI/InGame/Popups/ProductionPopup"
ID="SetCityName"
ID="ProductionPopup"
Hidden="True" />
Hidden="True" />
<!-- Added by Kael 09/17/2010 -->
<LuaContext FileName="Lua/ModList"
<!-- End Add -->
ID="ModList"
Hidden="True" />
<LuaContext FileName="Assets/UI/InGame/TopPanel" ID="TopPanel" />
Alles, was das macht, ist, unsere neue ModList.lua-Datei zu laden, wenn die Mod geladen wird. Mehr zu der Datei ModList.lua später.
2.Als nächstes pass die Dateien DiploCorner.lua und DiploCorner.xml an, um die neue Option hinzuzufügen. Folgendes ist die Ergänzung in der Datei DiploCorner.lua:
Code:
local g_MultiPullInfo = {};
g_MultiPullInfo[0] = { text="TXT_KEY_ADVISOR_SCREEN_TECH_TREE_DISPLAY",
call=function()
Events.SerialEventGameMessagePopup( { Type = ButtonPopupTypes.BUTTONPOPUP_TECH_TREE } ); end };
g_MultiPullInfo[1] = { text="TXT_KEY_DIPLOMACY_OVERVIEW",
call=function() Events.SerialEventGameMessagePopup( { Type =
ButtonPopupTypes.BUTTONPOPUP_DIPLOMATIC_OVERVIEW } ); end };
g_MultiPullInfo[2] = { text="TXT_KEY_MILITARY_OVERVIEW",
call=function() Events.SerialEventGameMessagePopup( { Type =
ButtonPopupTypes.BUTTONPOPUP_MILITARY_OVERVIEW } ); end };
g_MultiPullInfo[3] = { text="TXT_KEY_ECONOMIC_OVERVIEW",
call=function() Events.SerialEventGameMessagePopup( { Type =
ButtonPopupTypes.BUTTONPOPUP_ECONOMIC_OVERVIEW } ); end };
g_MultiPullInfo[4] = { text="TXT_KEY_VP_TT",
call=function() Events.SerialEventGameMessagePopup( { Type =
ButtonPopupTypes.BUTTONPOPUP_VICTORY_INFO} ); end };
g_MultiPullInfo[5] = { text="TXT_KEY_DEMOGRAPHICS",
call=function() Events.SerialEventGameMessagePopup( { Type =
ButtonPopupTypes.BUTTONPOPUP_DEMOGRAPHICS} ); end };
g_MultiPullInfo[6] = { text="TXT_KEY_POP_NOTIFICATION_LOG", call=function() Events.SerialEventGameMessagePopup( { Type =
ButtonPopupTypes.BUTTONPOPUP_NOTIFICATION_LOG, Data1 = Game.GetActivePlayer() } ); end };
-- Added by Kael 09/17/2010
g_MultiPullInfo[7] = { text="TXT_KEY_MOD_LIST", call=function() Events.SerialEventGameMessagePopup( { Type =
ButtonPopupTypes.BUTTONPOPUP_NOTIFICATION_LOG, Data1 = 999 } ); end };
-- End Add

Das fügt der Auswahlliste eine neue Option hinzu. Wenn die Option gewählt wird, übergibt sie BUTTONPOPUP_NOTIFICATION_LOG und 999 im Feld Data1. Später werden wir uns darum kümmern, wie man das Event auswertet und einstellt, dass sich dann das Menü öffnet.
Das funktioniert gut, um einen neuen Menüeintrag zu dem Menü hinzuzufügen. Aber wir wollen auch die Größe der Liste anpassen, um genug Platz für die neue Option zu schaffen. XML steuert Größe und Format, wir müssen also DiploCorner.xml ändern, um die Größe anzupassen:
Code:
<!-- ==========================================================================================================-->
<!-- Notification Log DropDownButtons -->
<!-- ==========================================================================================================-->
<PullDown ConsumeMouse="1" Offset="-6,0" Anchor="R,T" Size="45,45" AutoSizePopUp="0" SpaceForScroll="0" ScrollThreshold="900" ID="MultiPull" >
<ButtonData>
<Button Anchor="R,T" Size="45.45" Texture="assets\UI\Art\Notification\NotificationNotes.dds" ToolTip="TXT_KEY_DIPLO_ADDITIONAL" >
<ShowOnMouseOver>
<Image Anchor="R,T" Offset="0,0" Size="45.45" Texture="assets\UI\Art\Notification\NotificationNotes.dds" />
<AlphaAnim Anchor="R,T" Offset="0,0" Size="45.45" TextureOffset="0.0" Texture="assets\UI\Art\Notification\NotificationNotesHL.dds" Pause="0" Cycle="Bounce" Speed="2" AlphaStart="0.95" AlphaEnd="0.55"/>
</ShowOnMouseOver>
</Button>
</ButtonData>
<GridData Anchor="L,T" Offset="-24.-41" AnchorSide="O,I" Style="Grid9DetailTwo140" Padding="0,0" >
<!-- Modified by Kael 09/17/2010
<Box Color="Black.0" Size="200.260" /> -->
<Box Color="Black.0" Size="200.280" />
<!-- End Modify -->
Das vergrößert die Liste von 260 auf 280.
3.Wir haben mit unserer Änderung in DiploCorner.lua auf die BUTTONPOPUP_NOTIFICATION_LOG-Funktion zurückgegriffen. Wann immer wir also unsere neue Menüoption auswählen, tut sie so, als wäre das BUTTONPOPUP_NOTIFICATION_LOG-Popup aufgerufen worden. Hätten wir SDK-Zugang, würden wir für unsere Modliste eine neue BUTTONPOPUP-Definition erstellen. Ohne müssen wir kreativ werden. Wir rufen also BUTTONPOPUP_NOTIFICATION_LOG auf und übergeben einen Wert von 999 (normalerweise wird die Spielernummer übergeben, die niemals auch nur in die Nähe dieser Größenordnung käme).
Weil wir auf die BUTTONPOPUP_NOTIFICATION_LOG-Funktion zurückgreifen, müssen wir dafür sorgen, dass der normale BUTTONPOPUP_NOTIFICATION_LOG-Dialog nicht startet. Weshalb wir NotificationLogPopup.lua zu unserem Projekt hinzugefügt haben.
Erinnere dich daran, dass Lua-Funktionen Lua-Funktionen in anderen Dateien nicht direkt aufrufen können. Stattdessen registriert Lua Funktionen in einer zentralen Eventverwaltung und überwacht diese, um zu sehen, wann es handeln muss. In diesem Fall ist die normale OnPopup-Funktion aus NotificationLogPopup.lua:
Code:
function OnPopup( popupInfo )
if( popupInfo.Type ~= ButtonPopupTypes.BUTTONPOPUP_NOTIFICATION_LOG ) then
return;
end
CivIconHookup( Game.GetActivePlayer(), 64, Controls.CivIcon, Controls.CivIconBG, Controls.CivIconShadow, false, true );
m_PopupInfo = popupInfo;
g_NotificationInstanceManager:ResetInstances();
local player = Players[Game.GetActivePlayer()];
local numNotifications = player:GetNumNotifications();
for i = 0, numNotifications - 1
do
local str = player:GetNotificationStr((numNotifications - 1) - i);
local index = player:GetNotificationIndex((numNotifications - 1) - i);
local turn = player:GetNotificationTurn((numNotifications - 1) - i);
local dismissed = player:GetNotificationDismissed((numNotifications - 1) - i);
AddNotificationButton(index, str, turn, dismissed);
end
Controls.NotificationButtonStack:CalculateSize();
Controls.NotificationButtonStack:ReprocessAnchoring();
Controls.NotificationScrollPanel:CalculateInternalSize();
if( m_PopupInfo.Data1 == 1 ) then
if( ContextPtr:IsHidden() == false ) then
OnClose();
else
UIManager:QueuePopup( ContextPtr, PopupPriority.eUtmost );
end
else
UIManager:QueuePopup( ContextPtr, PopupPriority.NotificationLog );
end
end
Events.SerialEventGameMessagePopup.Add( OnPopup );
Darin wird die OnPopup-Funktion definiert und im SerialEventGameMessagePopup-Event registriert (in blau). Die erste Überprüfung der onPopup-Funktion ist, sicherzustellen, ob dieses Event, das durch SerialEventGameMessagePopup läuft, darauf anzuwenden ist. Sie überprüft also, ob der popupInfo.Type irgendetwas anderes als BUTTONPOPUP_NOTIFICATION_LOG ist (in rot). Wenn es etwas anderes ist, beendet sie die Funktion.
Aber wir werden BUTTONPOPUP_NOTIFICATION_LOG für eine andere Funktion aufrufen und wir wollen nicht, dass dieses Popup erscheint, wenn wir das tun. Also ist folgende Änderung der NotificationLogPopup.lua-Datei nötig:
Code:
function OnPopup( popupInfo )
if( popupInfo.Type ~= ButtonPopupTypes.BUTTONPOPUP_NOTIFICATION_LOG ) then
return;
end
-- Added by Kael 09/17/2010
if( popupInfo.Data1 == 999 ) then
return;
end
-- End Add
CivIconHookup( Game.GetActivePlayer(), 64, Controls.CivIcon, Controls.CivIconBG, Controls.CivIconShadow, false, true );
m_PopupInfo = popupInfo;
g_NotificationInstanceManager:ResetInstances();
local player = Players[Game.GetActivePlayer()];
local numNotifications = player:GetNumNotifications();
for i = 0, numNotifications - 1
do
local str = player:GetNotificationStr((numNotifications - 1) - i);
local index = player:GetNotificationIndex((numNotifications - 1) - i);
local turn = player:GetNotificationTurn((numNotifications - 1) - i);
local dismissed = player:GetNotificationDismissed((numNotifications - 1) - i);
AddNotificationButton(index, str, turn, dismissed);
end
Controls.NotificationButtonStack:CalculateSize();
Controls.NotificationButtonStack:ReprocessAnchoring();
Controls.NotificationScrollPanel:CalculateInternalSize();
if( m_PopupInfo.Data1 == 1 ) then
if( ContextPtr:IsHidden() == false ) then
OnClose();
else
UIManager:QueuePopup( ContextPtr, PopupPriority.eUtmost );
end
else
UIManager:QueuePopup( ContextPtr, PopupPriority.NotificationLog );
end
end
Events.SerialEventGameMessagePopup.Add( OnPopup );
Hier haben wir eine zusätzliche Überprüfung eingebaut. Genauso wie die Überprüfung, die die Funktion verlässt, wenn der Typ nicht BUTTONPOPUP_NOTIFICATION_LOG ist, verlässt diese Überprüfung die Funktion, wenn der Wert von Data1 999 ist. So können wir die BUTTONPOPUP_NOTIFICATION_LOG-Funktion teilen, ohne zu kollidieren.
4. Schließlich kommen wir dazu, den Code für unseren neuen Bildschirm zu erstellen. Zuerst brauchen wir die XML-Definition für den Bildschirm. Folgendes war die Definition, die ich für ModList.xml verwendet habe:
Code:
<Context Font="TwCenMT14" FontStyle="Base" Color="Beige" Color1="Black" >
<Instance Offset="0,0" Name="NotificationButton" Size="890,60" >
<Button Size="890,60" Offset="0,0" StateOffsetIncrement="0" ID="Button">
<Button Anchor="L,C" Offset="0,0" Size="64.64" Texture="assets\UI\Art\WorldView\ActionItemsButton.dds" Hidden="1" ID="GenericButton" />
<Label Anchor="R,B" Offset="5,8" Font="TwCenMT24" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" String="Turn" ID="NotificationTurnText" />
<Stack ID="TextStack" Anchor="L,T" Padding="10">
<Label Anchor="L,T" Offset="16,5" LeadingOffset="-8" WrapWidth="780" Font="TwCenMT18" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" ID="NotificationText" String="Text" />
<Image Anchor="C,B" Offset="0,0" TextureOffset="0.0" Texture="bar900x2.dds" Size="900.1" />
</Stack>
<ShowOnMouseOver>
<AlphaAnim ID="TextAnim" Anchor="C,C" Offset="10,0" Size="890,64" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="2" AlphaEnd="1">
<Grid ID="TextHL" Size="890,64" Offset="0,0" Padding="0,0" Style="Grid9FrameTurnsHL" />
</AlphaAnim>
</ShowOnMouseOver>
</Button>
</Instance>
<Box Style="BGBlock_ClearTopBar" />
<Instance Name="ListingButtonInstance">
<Button Anchor="L,T" Size="896,32" Offset="0,0" Color="Black.0" StateOffsetIncrement="0,0" ID="Button">
<ShowOnMouseOver>
<AlphaAnim Anchor="L,T" Size="896,72" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="1.5" AlphaEnd="1" ID="SelectHighlight">
<Grid Size="896,36" Offset="0,0" Padding="0,0" Style="Grid9FrameTurnsHL" />
</AlphaAnim>
</ShowOnMouseOver>
<Stack StackGrowth="Right" Offset="0,0" Padding="0">
<Box Anchor="L,T" Offset="8,8" Size="175,24" Color="Black.0">
<Label Anchor="L,T" Offset="10,0" Font="TwCenMT18" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" String="MOD TITLE" ID="Title" />
</Box>
</Stack>
<Image Anchor="L,B" Offset="0,0" TextureOffset="0.0" Texture="bar900x2.dds" Size="900.1" />
</Button>
</Instance>
<Grid Size="960,658" Anchor="C,C" Offset="0,36" Padding="0,0" Style="Grid9DetailFive140" ConsumeMouse="0">
<Image Anchor="L,T" Offset="17,90" Texture="HorizontalTrim.dds" Size="926.2" />
<ScrollPanel Anchor="L,T" ID="ListingScrollPanel" Vertical="1" Size="912,440" Offset="13,94" AutoScrollBar="1">
<ScrollBar Anchor="R,T" AnchorSide="O,I" Offset="0,18" Length="404" Style="VertSlider"/>
<UpButton Anchor="R,T" AnchorSide="O,I" Offset="0,0" Style="ScrollBarUp"/>
<DownButton Anchor="R,B" AnchorSide="O,I" Offset="0,0" Style="ScrollBarDown"/>
<Stack ID="ListingStack" StackGrowth="B" Offset="4,0" Padding="0" />
</ScrollPanel>
<Label String="TXT_KEY_POP_MOD_LIST" ID="Notifications Label" Anchor="C,T" Offset="0,19" Font="TwCenMT20" Color0="30.50.80.255" Color1="133.184.186.255" Color2="133.184.186.255" FontStyle="SoftShadow" />
<Box Anchor="C,B" AnchorSide="I.I" Offset="0,54" Size="910,56" Color="255,255,255,0" >
<GridButton Anchor="L,B" Style="SmallButton" Size="150,32" Offset="14,0" StateOffsetIncrement="0,0" ID="CloseButton" >
<Label Anchor="C,C" Offset="0,0" String="TXT_KEY_CLOSE" Font="TwCenMT18" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" />
</GridButton>
</Box>
<Image Anchor="C,B" Offset="0,110" Texture="HorizontalTrim.dds" Size="926.5" />
</Grid>
</Context>
Auf den ersten Blick kann das erschreckend aussehen, wenn es aus dünner Luft erzeugt ist. Ich bin sicher, dass es Leute gibt, die das so auf dem Reißbrett erstellen können, aber ich gehöre nicht dazu. Stattdessen nehme ich die XML-Definition eines Bildschirms, der so ähnlich aussieht wie der, den ich erstellen will, und schneide, passe an und ergänze, bis er so aussieht, wie ich ihn haben will. Ich verwende den Benachrichtigungsbildschirm als Grundlage hierfür und habe dann VML aus dem ModBrowser (dem Ort, an dem du vom Hauptmenü aus Mods auswählst), um die Teile zu bekommen, die ich brauchte. Weil die gesamte Benutzeroberfläche von Civ5 in Lua vorliegt, steht alles zur Verfügung, um Teile davon für deine eigenen Bildschirme zu borgen.
Das oben legt den Bildschirm fest, beinhaltet aber keinerlei Logik. Dafür brauchen wir Lua. Die Datei ModList.lua enthält folgendes:
Code:
include( "InstanceManager" );
g_InstanceManager = InstanceManager:new( "ListingButtonInstance", "Button", Controls.ListingStack );
function OnPopup( popupInfo )
if( popupInfo.Type ~= ButtonPopupTypes.BUTTONPOPUP_NOTIFICATION_LOG ) then
return;
end
if( popupInfo.Data1 ~= 999 ) then
return;
end
local unsortedInstalledMods = Modding.GetInstalledMods();
g_InstanceManager:ResetInstances();
for k, v in pairs(unsortedInstalledMods) do
local modinfo = v;
if(modinfo == nil) then
break;
end
if(modinfo.Enabled) then
local listing = g_InstanceManager:GetInstance();
local str = modinfo.Name;
if(modinfo.Version ~= "") then
local version = string.format("version %s (%s)", modinfo.Version or "", modinfo.Stability or "");
str = string.format("%s - %s", str, version)
end
listing.Title:SetText(str);
end
end
UIManager:QueuePopup( ContextPtr, PopupPriority.NotificationLog );
end
Events.SerialEventGameMessagePopup.Add( OnPopup );
function OnClose ()
UIManager:DequeuePopup( ContextPtr );
end
Controls.CloseButton:RegisterCallback( Mouse.eLClick, OnClose );
Beachte, dass das so ähnlich ist wie der Code des Notification Log, den wir angepasst haben. Es meldet sich beim SerialEventGameMessagePopup (in blau), genau wie das das Notification-Log-Popup getan hat. Aber in diesem Fall erlauben wir der OnPopup-Funktion nur, weiterzumachen, wenn der Typ BUTTONPOPUP_NOTIFICATION_LOG ist und Data1 999 (in rot).
Der Code ist recht leicht. Ich habe die ModBrowser-Lua-Dateien durchgelesen, um die Aufrufe und Funktionen zu finden, die ich brauchen würde (so habe ich von der Funktion Modding.GetInstalledMods() und den Rückgabewertattributen .Name und .Enabled erfahren). Im Wesentlichen fügt die OnPopup-Funktion der Bildschirmanzeige für jede installierte Mod, die in diesem speziellen Spiel aktiviert ist, neue Zeilen hinzu. Sie zeigt Namen, Version und Stabilität (experimentell, alpha, beta, final) einer jeden Mod an.
Die einzige zusätzliche Funktion ist der Code, der den Schließen-Knopf steuert.
Ein sehr einfaches Beispiel für das Hinzufügen eines neuen Bildschirms zum Spiel.

WorldBuilder

Der WorldBuilder ist ein Karteneditor und Szenarienersteller für Civ V. Mit ihm können Karten schnell erstellt werden und komplexe Szenarien mit startenden Teams entwickelt werden. Dieser Abschnitt wird die Optionen im Worldbuilder durchgehen, sodass Modder rasch anfangen können, ihre Szenarien zu erstellen.