UniqueDiplomacyUtils - A set of Lua functions to enable unique diplomacy responses

Typhlomence

Magical Tomomo
Joined
Mar 19, 2014
Messages
394
Location
Brisbane, Australia
UniqueDiplomacyUtils
A Lua addon for mods that assists in defining unique diplomacy dialogue
Version 3 download

This is a set of Lua functions I wrote for the purpose of changing diplomacy lines dynamically, depending on certain conditions.

Many of you will know of Ryoga's Unique Cultural Influence, which allows the diplomacy line that appears when you are culturally influential over another civilization from the default "blue jeans and pop music" line to something a bit more fitting for who you are playing as. This is an expansion of that concept and is intended to make it easy for modders to change lines depending on certain other conditions, primarily the leader the player is playing as.

UniqueDiplomacyUtils provides two different ways to do this: altering the entries in the Diplomacy_Responses table, or changing game text entries directly. While it is intended to be used as a VFS-imported file that is included in your own Lua script, you are of course free to copy and adapt the code into your own scripts.

Important note: Since these functions directly alter database tables, the changes you make will persist when you load a new game without returning to the main menu. It is possible to code your unique diplomacy lua so it resets to the default when a game is loaded, though there are few circumstances where this would actually be an issue so it is up to you whether you wish to put the effort in.

Functions for altering the Diplomacy_Responses table:
  • ChangeDiplomacyResponse(leaderType, targetResponseType, newResponse, targetResponse, bias)
    • Changes an existing diplomacy response to reference other entries in your game text. bias is optional, and only one of targetResponseType and targetResponse is required (though you can specify both, if you want). Passing nil to the former will replace all entries that match targetResponse regardless of their response type, while passing nil to the latter will replace all entries that match the targetResponseType. Not specifying a bias will preserve the existing one. targetResponse is moved for version 3 because for most people's purposes, there is no need to check for a specific response, and so it can be omitted (though if you want to specifiy a bias without a targetResponse, you will still need to pass nil as the fourth parameter).
  • AddDiplomacyResponse(leaderType, targetResponseType, newResponse, bias)
    • Adds a new diplomacy response that references entries in your game text. Bias is again optional, and will default to 1 if you do not pass one.
  • RemoveDiplomacyResponse(leaderType, targetResponseType, targetResponse)
    • Removes an existing diplomacy response. All parameters are required for safety. Unless you remove the leader's RESPONSE_FIRST_GREETING or RESPONSE_DEFEATED (as by default there is no generic entry for either), your leader will use the generic diplomacy lines if you remove all of their custom ones.
Functions for altering the entries in the game text database directly:
  • ChangeDiplomacyGameText(targetText, newText)
    • Overwrites the contents of targetText with the contents of newText in the current locale.
  • AddDiplomacyGameText(targetText, newText)
    • Copies the contents of newText into a new game text entry targetText in the current locale.
  • RemoveDiplomacyGameText(targetText)
    • Removes targetText from the database in the current locale. Be careful when using this as you can't get it back unless you copy the entry elsewhere first!
  • ChangeDiplomacyGameTextToString(targetText, newString, locale)
    • Overwrites the contents of targetText with the string newString. Locale is optional - it will use the current locale if you do not specify one. If you do pass a locale, it expects it in the form "Language_??_??".
  • AddDiplomacyGameTextFromString(targetText, newString, locale)
    • Creates a game text entry targetText with the contents newString. Locale is optional - it will use the current locale if you do not specify one. If you do pass a locale, it expects it in the form "Language_??_??".
Note that since game text entries are not specific to diplomacy, you can use these functions to replace any game text you might need. While it is safer to alter entries in Diplomacy_Responses (it's difficult to restore game text after you change it, after all), I've still included them for those people who know what they're doing.

New in version 3: targetResponse is now optional in ChangeDiplomacyResponse, and is moved in the list of parameters. You'll need to change your calls to the function if you wish to use the new version - removing or moving the targetResponse parameter, depending on what you need.

---------------------------------------

Examples:
JFD's Empire of Byzantium (Alexios I Komnenos) - first greeting when playing as Theodora.
Spoiler :

Vicevirtuoso's Planeptune Mk2 - first greeting when playing as Mogarane's Neptune.
Spoiler :

Typhlomence's Koopa Troop - example of a response that both is dynamic in content and changes between turns. Bowser will talk about kidnapping female leaders if they are present, and the actual line will change depending on whether the player's leader is male or female and whether Princess Peach is in the game. It will only appear if Bowser has actually met any female leaders, and if so, it will pick a random one out of those he has met to appear in the actual line.
Spoiler :

While I can't show it here due to foul language, Vicevirtuoso's Lowee is also a great example for lines changing under certain circumstances - in this case, depending on Blanc's mood towards the player.
 

Typhlomence

Magical Tomomo
Joined
Mar 19, 2014
Messages
394
Location
Brisbane, Australia
Now, I bet the next thing on your mind is how you can use this for your own civilizations. Well, why don't we have a look at a few examples?

First, however, you need to understand how diplomacy responses work. They are made up of two parts - the entries in the Diplomacy_Responses database table, and the game text entries that actually contain the lines your leader will say in diplomacy. Here's how you define a diplomacy response for your leader:
Code:
<Row LeaderType="LEADER_BOWSER">
    <ResponseType>RESPONSE_FIRST_GREETING</ResponseType>
    <Response>TXT_KEY_LEADER_BOWSER_FIRSTGREETING%</Response>
    <Bias>500</Bias>
</Row>
  • LeaderType is pretty self-explanatory - it's the internal type of the leader you wish to define a response for. Using "GENERIC" will mean that all leaders will use this response (more on that in a moment).
  • ResponseType defines what situation this response will be used for. In this case, "RESPONSE_FIRST_GREETING" means that the response will be used for the greeting the leader gives when they first meet the player. There are a huge amount of diplomacy response types you can set responses to, so if you're interested in giving your leader a comprehensive set of responses, it's a good idea to dive into the game files to see what you can use.
  • Response is the actual game text entry that will be used. As you can see above, it accepts wildcards, allowing multiple responses to be used for a single response type without requiring multiple entries in the Diplomacy_Responses table. In this case, it will use any game text entry that has a tag starting with "TXT_KEY_LEADER_BOWSER_FIRSTGREETING". If you wanted to specify a specific response, simply use the whole game text tag - e.g. "TXT_KEY_LEADER_BOWSER_FIRSTGREETING_1".
  • Bias is an optional entry, but one I recommend you include. For some reason, CiV will still pick out responses assigned to "GENERIC" even if you specify custom ones for your leader. Having a large bias is a way to avoid this - there's still a chance to see them, but it will be very rare.
While the above example is in XML, SQL is perfectly valid as well.

These are the game text entries the above code references:
Code:
<Row Tag="TXT_KEY_LEADER_BOWSER_FIRSTGREETING_1">
    <Text>Gwah ha ha ha! I am Bowser, mighty, awesome king of the Koopas, and soon to be the ruler of this world! You better not be thinking about getting in my way!</Text>
</Row>
<Row Tag="TXT_KEY_LEADER_BOWSER_FIRSTGREETING_2">
    <Text>Another leader? Bwah hah hah! I am Bowser The King! A word of warning: you'd better not be the kind of fool who would dare oppose me. Stomping fools is my business!</Text>
</Row>
<Row Tag="TXT_KEY_LEADER_BOWSER_FIRSTGREETING_3">
    <Text>Gwa ha haaaaa! My name is Bowser, King of the Koopas![NEWLINE][NEWLINE]Why are you standing there with that stupid look on your face? You never seen a burly king of evil before?</Text>
</Row>
<Row Tag="TXT_KEY_LEADER_BOWSER_FIRSTGREETING_4">
    <Text>I, Bowser, the mighty Koopa King, offer my greeting!</Text>
</Row>

Now, while those are a perfectly fine set of responses for Bowser when he's greeting a generic leader, there's a civilization for Princess Peach you can play with! Let's give him a unique first greeting for her.

First, we need to create a new game text entry or two to use as a greeting for Peach.
Code:
<Row Tag="TXT_KEY_LEADER_BOWSER_PEACH_FIRSTGREETING_1">
    <Text>Well hello there Princess Peach! Good to see you as well on this brand new world. But don't think that you won't be by my side eventually!</Text>
</Row>
<Row Tag="TXT_KEY_LEADER_BOWSER_PEACH_FIRSTGREETING_2">
    <Text>Hi there, Princess Peach! Don't worry, I'm not here to kidnap you yet! I'm just here to give you my greeting!</Text>
</Row>

(Please be careful with the tag you choose for your alternative responses - depending on the value of Response in the original Diplomacy_Responses entry, the game might accidentally pick out your alternative response under normal circumstances. The best way to be sure is to not use a tag that would match a wildcard in the original. For example, with "TXT_KEY_LEADER_BOWSER_FIRSTGREETING%", a bad choice would be "TXT_KEY_LEADER_BOWSER_FIRSTGREETING_PEACH_1", since "_PEACH_1" will still be a match for the "%" wildcard.)

Okay, now that we have this, how do you go about making that appear if you're playing Peach against an AI Bowser? We'll need to use Lua to get this working. Firstly, you will need to add UniqueDiplomacyUtilsV3.lua to your mod, and then set Import into VFS to true.

Since we want to change an existing response - in this case, the one for the first greeting - we'll use ChangeDiplomacyResponse() from UniqueDiplomacyUtilsV3.lua. Looking at the original Diplomacy_Responses entry, the leader type will be "LEADER_BOWSER", and the target response type will be "RESPONSE_FIRST_GREETING". Since there is only one normal first greeting entry in Diplomacy_Responses, we don't need to specify a target response. However, if we wanted to, the target response would be "TXT_KEY_LEADER_BOWSER_FIRSTGREETING%". Since we have multiple game text entries to choose from, we can use "TXT_KEY_LEADER_BOWSER_PEACH_FIRSTGREETING%" as the new response, and it should pick one from the two responses above (wildcards will still work if you have only specified one as well, as long as it matches the pattern). On this occasion we'll not specify a bias either, as it is already 500 in the original response entry.

To make sure this triggers only for a player controlled Peach, we need to check either the player's leader or civilization type. I personally go with the leader type, but civilization type should work in most cases unless a mod changes the leader for that type. In this case, we want to check if the player's leader type is "LEADER_PEACH".

We can use Game.GetActivePlayer() to get the ID of the currently active player and then check against the Players table to get the corresponding player object. Then, we can get the leader type as a string which can be used to compare to "LEADER_PEACH" - this method will prevent us from having to check if the leader type actually exists, which you would have to do if you were using GameInfoTypes to avoid an error. (Thank you to Vicevirutoso who came up with this method for his Leanbox and Lowee civs.)
Code:
include("UniqueDiplomacyUtilsV3.lua")

function BowserUniqueResponses()
    local pActivePlayer = Players[Game.GetActivePlayer()]
    local sLeaderType = GameInfo.Leaders[pActivePlayer:GetLeaderType()].Type
end
Now that we have the player's leader type in a string, it's a simple matter of checking if it matches the one we want:
Code:
include("UniqueDiplomacyUtilsV3.lua")

function BowserUniqueResponses()
    local pActivePlayer = Players[Game.GetActivePlayer()]
    local sLeaderType = GameInfo.Leaders[pActivePlayer:GetLeaderType()].Type
    [COLOR="Blue"]if (sLeaderType == "LEADER_PEACH") then
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_FIRST_GREETING", "TXT_KEY_LEADER_BOWSER_PEACH_FIRSTGREETING%")
    end[/COLOR]
end
Finally, we need to call the BowserUniqueResponses() function. You can simply add that to the end of the file, and it should trigger when the game starts.
Code:
include("UniqueDiplomacyUtilsV3.lua")

function BowserUniqueResponses()
    local pActivePlayer = Players[Game.GetActivePlayer()]
    local sLeaderType = GameInfo.Leaders[pActivePlayer:GetLeaderType()].Type
    if (sLeaderType == "LEADER_PEACH") then
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_FIRST_GREETING", "TXT_KEY_LEADER_BOWSER_PEACH_FIRSTGREETING%")
    end
end

[COLOR="Blue"]BowserUniqueResponses()[/COLOR]
That's it! Bowser should choose from one of the Peach-specific lines if you're playing as Princess Peach. It's easy to follow the pattern to change more lines for a specific leader, and add other leaders for Bowser to interact with:
Code:
include("UniqueDiplomacyUtilsV3.lua")

function BowserUniqueResponses()
    local pActivePlayer = Players[Game.GetActivePlayer()]
    local sLeaderType = GameInfo.Leaders[pActivePlayer:GetLeaderType()].Type
    if (sLeaderType == "LEADER_PEACH") then
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_FIRST_GREETING", "TXT_KEY_LEADER_BOWSER_PEACH_FIRSTGREETING%")
        [COLOR="Blue"]ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_DOW_WORLD_CONQUEST", "TXT_KEY_LEADER_BOWSER_PEACH_DOW_WORLD_CONQUEST%")
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_LUXURY_TRADE", "TXT_KEY_LEADER_BOWSER_PEACH_LUXURY_TRADE%")
    elseif (sLeaderType == "LEADER_TYPH_ROSALINA") then
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_FIRST_GREETING", "TXT_KEY_LEADER_BOWSER_ROSALINA_FIRSTGREETING%")
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_DOW_WORLD_CONQUEST", "TXT_KEY_LEADER_BOWSER_ROSALINA_DOW_WORLD_CONQUEST%")
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_DOW_OPPORTUNITY", "TXT_KEY_LEADER_BOWSER_ROSALINA_DOW_WORLD_CONQUEST%")[/COLOR]
    end
end

BowserUniqueResponses()
For performance reasons, you might want to run a quick check before calling the function to ensure that there is an AI player using your mod's leader in the game. You can also define all of the strings you want to check for as local variables at the start of the script to make it cleaner.
Code:
include("UniqueDiplomacyUtilsV3.lua")

[COLOR="Blue"]local sLeaderPeach = "LEADER_PEACH"
local sLeaderRosalina = "LEADER_TYPH_ROSALINA"[/COLOR]

function BowserUniqueResponses()
    local pActivePlayer = Players[Game.GetActivePlayer()]
    local sLeaderType = GameInfo.Leaders[pActivePlayer:GetLeaderType()].Type
    if (sLeaderType == sLeaderPeach) then
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_FIRST_GREETING", "TXT_KEY_LEADER_BOWSER_PEACH_FIRSTGREETING%")
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_DOW_WORLD_CONQUEST", "TXT_KEY_LEADER_BOWSER_PEACH_DOW_WORLD_CONQUEST%")
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_LUXURY_TRADE", "TXT_KEY_LEADER_BOWSER_PEACH_LUXURY_TRADE%")
    elseif (sLeaderType == sLeaderRosalina) then
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_FIRST_GREETING", "TXT_KEY_LEADER_BOWSER_ROSALINA_FIRSTGREETING%")
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_DOW_WORLD_CONQUEST", "TXT_KEY_LEADER_BOWSER_ROSALINA_DOW_WORLD_CONQUEST%")
        ChangeDiplomacyResponse("LEADER_BOWSER", "RESPONSE_DOW_OPPORTUNITY", "TXT_KEY_LEADER_BOWSER_ROSALINA_DOW_WORLD_CONQUEST%")
    end
end

[COLOR="Blue"]for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1, 1 do
	local pPlayer = Players[iPlayer]
	if pPlayer ~= nil and pPlayer:IsAlive() and not pPlayer:IsHuman() and (pPlayer:GetCivilizationType() == GameInfo.Civilizations.CIVILIZATION_KOOPA_TROOP.ID) then
		Events.SequenceGameInitComplete.Add(BowserUniqueResponses) -- Will add it to the event that triggers when the game is initialised
		break
	end
end[/COLOR]

Tutorials for more advanced ways to use UniqueDiplomacyUtilsV3.lua will come when I find time to write them.
 
Top Bottom