Lua snippets

Craig_Sutter

Deity
Joined
Aug 13, 2002
Messages
2,773
Location
Calgary, Canada
Recently, I've been getting in Lua programming (at its most basic level). I've been working at doing code for various little tasks that my scenario necessitates. Most of this involves going through the various scenarios that come with the game and seeing how it is being used.

I've run across a bit of a difficulty doing this, though, because the code used is often used in a very complex way that involves multiple functions and is not easily transported to my own scenario or mod.

What I would like to initiate is a thread that involves little bits of code that people may use to do limited tasks. For instance, I have (with help) created a code that will add holy cities and religions to a scenario at game start. I think it might be useful for others to have. Rather than letting such snippets disappear into the archives, I would like to see them collected in one place so that people could see them and use them in their scenarios.

I'm hoping that anyone looking at this thread might quickly cut and paste a bit of code, with a brief explanation, that accomplished for them a very limited but useful task. Then I, and others, can take that and customize it for our own scenario if it is something we find of use.

I'll begin by posting my own contribution in the next post of this thread and see if this idea has legs or dies a slow death...
 
This bit of code creates a religion and adds a holy city. It will work for "any" city... provided it has been preplaced on the scenario map. Customize it by filling in your city name for "Paris" and your religion's name and beliefs within the appropriate quotation marks. If you delete the final two beliefs, the religion will be founded and not yet enhanced... I don't recommend doing this if you make a minor civ capital the holy city as it will likely never be completed. You can rename the function names to reflect your religion, but it is not necessary. (I used and altered original code by Donquiche and Whoward69.

This code has been edited from March 7, 2014... it corrects some problems in the original.

Code:
-- Find Berlin player and found Christianity in their city, returning the city

function EstablishChristian()
if Game.GetElapsedGameTurns() == 0 then

function CreateChristianHolyCity(pCity, religion, pantheonBelief, founderBelief, followerBelief, followerBelief2, enhancerBelief)
  local iPlayer = pCity:GetOwner()
  local iReligion = GameInfoTypes[religion]

  -- Optional extra beliefs
  local iBelief4 = followerBelief2 and GameInfoTypes[followerBelief2] or -1
  local iBelief5 = enhancerBelief and GameInfoTypes[enhancerBelief] or -1

  Game.FoundPantheon(iPlayer, GameInfoTypes[pantheonBelief])
  Game.FoundReligion(iPlayer, iReligion, nil, GameInfoTypes[founderBelief], GameInfoTypes[followerBelief], -1, -1, pCity)
  Game.EnhanceReligion(iPlayer, iReligion, GameInfoTypes[followerBelief2], GameInfoTypes[enhancerBelief]);
end

function FoundChristianity()
  for iPlayer=0, GameDefines.MAX_CIV_PLAYERS - 1 do
    local pPlayer = Players[iPlayer]
    if (pPlayer:IsEverAlive()) then

	for pCity in pPlayer:Cities() do

				-- City exists and has the proper name?
    			if pCity ~= nil and pCity:GetName() == "Berlin" then

		             CreateChristianHolyCity(pCity, "RELIGION_CHRISTIANITY", "BELIEF_GOD_CRAFTSMEN",
					"BELIEF_INITIATION_RITES", "BELIEF_CATHEDRALS1",  "BELIEF_STATE_CHURCH", "BELIEF_RELIGIOUS_TEXTS");
					print ("okay, Paris is the Catholic holy city")
				return pCity
				end
			end
		end
	end
end

local pChristianPlot = FoundChristianity():Plot()

end
end

Events.SequenceGameInitComplete.Add(EstablishChristian)
 
This code will add a previously created religion to any city. It goes well with the previous code. Customize it by changing the religion and cities as needed.

Code:
-- Enumerate Playable Civ Cities with Confucianism

function FollowConfucianism()
if Game.GetGameTurn() == 0 then

for iPlayer=0, GameDefines.MAX_CIV_PLAYERS - 1 do
	local pPlayer = Players[iPlayer];
		if pPlayer:IsEverAlive() then 

		print (pPlayer:GetName())
			
			-- Enumerate cities
			for cityIndex = 0, pPlayer:GetNumCities() - 1, 1 do
    			local pCity = pPlayer:GetCityByID(cityIndex)
				 
				print (pCity:GetName());

				-- City exists and has the proper name?
    			if pCity ~= nil and city:GetName() == "Uppsala" then 
					pCity:AdoptReligionFully(GameInfoTypes["RELIGION_CONFUCIANISM"]);
				elseif pCity ~= nil and pCity:GetName() == "Oslo" then
					pCity:AdoptReligionFully(GameInfoTypes["RELIGION_CONFUCIANISM"]);
				elseif pCity ~= nil and pCity:GetName() == "Nidaros" then
					pCity:AdoptReligionFully(GameInfoTypes["RELIGION_CONFUCIANISM"]);
				elseif pCity ~= nil and pCity:GetName() == "Roskilde" then
					pCity:AdoptReligionFully(GameInfoTypes["RELIGION_CONFUCIANISM"]);
				elseif pCity ~= nil and pCity:GetName() == "Bergen" then
					pCity:AdoptReligionFully(GameInfoTypes["RELIGION_CONFUCIANISM"]);
					return pCity							
    			end
			end
		end
end

end
end

Events.ActivePlayerTurnEnd.Add(FollowConfucianism)
 
Nice thread idea. I should have started something like this with my PlotToRadiusItorator(), rather than a separate component thread, but I'll link it here anyway. Breifly, it's an iterator function that returns plot x, y coordinates out to a supplied radius. It accounts for map edges and wrapping correctly.
 
Here is a bit of code that forces declarations of war between two players. It is set for game turn 0. The players are a major civilization and a city state; however, editing the text could allow for major-major or citystate-citystate wars. As well, if you do not want permanent war, simply edit out the appropriate lines of code.

I want to set it out that the dow's only occur if the player declaring war is not human, but haven't got to that state. I assume if anyone wishes to do so, they can figure it out (I'll edit this code should I do it first). ...Done

I'd like to thank Machiavilli24 and Gedemon for their advice on making this... could not have done it otherwise.

Code:
--At the end of the first turn, Denmark declares war on the Angles.  We want TeamIDs and Teams, not PlayerIDs.
--Then set permanent war, both ways.		
	
function  DenmarkAnglesWar()

if Game.GetGameTurn() == 0 then

local DenmarkTeam
local DenmarkTeamID
local AnglesTeam
local AnglesTeamID
local Denmark
local Angles

print(">>>> Set up Denmark");

for iPlayer=0, GameDefines.MAX_MAJOR_CIVS-1 do 

local pDenmark = Players[iPlayer]

	if (GameInfo.Civilizations.CIVILIZATION_DENMARK.ID == pDenmark:GetCivilizationType()) then
	
	Denmark = pDenmark
	DenmarkTeamID = Denmark:GetTeam();
	DenmarkTeam= Teams[ Denmark:GetTeam() ]

	end	
end

print(">>>> Set up Angles");

for iPlayer=GameDefines.MAX_MAJOR_CIVS, GameDefines.MAX_CIV_PLAYERS-1, 1 do 

local pAngles = Players[iPlayer]

   	if (GameInfo.MinorCivilizations.MINOR_CIV_ANGLES.ID == pAngles:GetMinorCivType()) then
	
	Angles = pAngles	
	AnglesTeamID = Angles:GetTeam();
	AnglesTeam= Teams[ Angles:GetTeam() ]
	
	end
end

print(">>>> DeclareWar");

if(Angles:IsAlive() and Denmark:IsAlive() and not Denmark:IsHuman()) then
DenmarkTeam:DeclareWar( AnglesTeamID, true );
DenmarkTeam:SetPermanentWarPeace(AnglesTeamID, true);
AnglesTeam:SetPermanentWarPeace(DenmarkTeamID, true);
end
	
	if (DenmarkTeam:IsAtWar(AnglesTeamID)) then

print ("Denmark is at war with the Angles");

	end
end
end

Events.ActivePlayerTurnEnd.Add(DenmarkAnglesWar)

PS You may want to edit out the print statements as they were for debugging and are not necessary.
 
This code adjusts diplomatic relations between to major civilizations.

It was created by Whoward69. I should note at the same time, Gedemon came up with a nearly identical code. I thank them for showing me the way to do this and improving the quality of my scenario (which will be coming out in a month or so)

Code:
GameEvents.GetScenarioDiploModifier1.Add(function(ePlayer1, ePlayer2)
  -- This is what we are trying to achieve
  -- if ePlayer1 is France (or Greece) and ePlayer2 is Greece (or France), return -25 otherwise 0

  -- We don't want player ids, we need civ ids
  local eCiv1 = Players[ePlayer1]:GetCivilizationType()
  local eCiv2 = Players[ePlayer2]:GetCivilizationType()

  if ((eCiv1 == GameInfoTypes.CIVILIZATION_FRANCE and eCiv2 == GameInfoTypes.CIVILIZATION_GREECE) or 
      (eCiv1 == GameInfoTypes.CIVILIZATION_GREECE and eCiv2 == GameInfoTypes.CIVILIZATION_FRANCE)) then
    
	--[[print(Players[ePlayer1]:GetName())
	print(Players[ePlayer2]:GetName())]]	
	
	return -25
  else
    return 0
  end
end)

and associated XML for the text:

Code:
<Language_en_US>
		<Row Tag="TXT_KEY_SPECIFIC_DIPLO_STRING_1">
			<Text>---put your text here---</Text>
		</Row>
</Language_en_US>
 
This will add .67 influence every turn to a civilizations influence over a CS. Essentially, this will mean it is a puppet once you get it allied. In scenarios, I preset the influence in Worldbuilder, and this can maintain that influence over time. You can change the major civ and minor civ and variables to conform to your own ideas. You can make it one time only. The value is added to current influence.

Code:
function SetCsFriendships()

if Game.GetGameTurn() >= 0 then

print(">>>> Friendship");

local pWiht
local pWessex
local Wessex

for iPlayer=0, GameDefines.MAX_MAJOR_CIVS-1 do 

local pWessex = Players[iPlayer]
	
  if (pWessex:IsAlive()) then
		if (GameInfo.Civilizations.CIVILIZATION_ENGLAND.ID == pWessex:GetCivilizationType()) then
     
	   	Wessex = iPlayer
		

for iWiht = GameDefines.MAX_MAJOR_CIVS, GameDefines.MAX_CIV_PLAYERS-2, 1 do

local pWiht = Players[iWiht]

   if (pWiht:IsAlive()) then
		if (GameInfo.MinorCivilizations.MINOR_CIV_WIHT.ID == pWiht:GetMinorCivType()) then
										
      pWiht:ChangeMinorCivFriendshipWithMajor(Wessex, .67)

		end
    end
  end
end
end
end
end
end

Events.ActivePlayerTurnEnd.Add(SetCsFriendships)
 
If your code is littered with 100s of print statements like mine, here is an easy way to disable them all. Put this line at the top of every Lua file:
Code:
local print = DISABLE_PRINT and function() end or print
If DISABLE_PRINT is false or nil, then all you've done is localize the print function to your file (which is good because it will run faster).

If you set DISABLE_PRINT=true as a global in some code that runs before this, then all print statements in your code will be changed to an empty function that does nothing (and does nothing very quickly because it is also a localized function).

If you want print to work from one file but not others, set DISABLE_PRINT=true but comment out that line at the top of the particular file you are troubleshooting.

In case you are not familiar with the
Code:
local x = arg1 and arg2 or arg3
idiom, the way it works is this. If arg1 is false or nil, x takes the value after the "and" (arg2). If arg1 has any other value, x takes the value after the "or" (arg3). It's handy and runs a tiny bit faster than an "if then else" construction.
 
Wow, I search for a script to set up religions in G&K and here it is. Thanks Craig, great work!

Khan
 
When Modbuddy was still working on this PC, I tried using the "AI can't build world wonders" snippet in a mod by itself...only to watch my nine AI competitors beat me to finishing them. I guess that one goes back to the proverbial drawing board!
 
Craig, I am having a hard time getting the set religion script to fire. I am new to lua and modding civ5, but have modded previous version quite a bit.

Any pointers? I have already verified the script is included in the content of the mod as "custom" and that it is set TRUE to load.

Thanks, Khan
 
Some possibilities:

Does Paris exist in your scenario? Or the city that you have changed the script to. The code is made for premade maps... no "Paris" or whatever, and it will not work. So no map scripts.

Do the beliefs you added exist?

Are you creating too many religions... more than exist (greater than 11?).

Is the lua file being added via action properties... I don't think VFS true will work.

Did you do a copy/paste to a lua script in modbuddy? I've had instances where I copied code from the net to a text file, then imported that file into modbuddy... doesn't work, copy and paste the code into a blank lua file created within modbuddy.

Those are some suggestions as to possible errors.

What does the database log say? It'll likely say if things are loading correctly or not, and give the line in the code that is failing.

Otherwise, if you post your changed code, I can likely see or find the problem.
 
Craig,

Thank you for trying to help. I think the problem is that the lua is not loading into the mod correctly and so not firing at all. I think this because there are no errors in the log.

I have the code loading under content but not actions as you suggest. The reason I have not done so is, when I try to select it under the actions tab, only xml files are offered for selection. What am I missing?

Here is the code in any case. The only changes from your snippet is Paris to Rome.

function CreateChristianHolyCity(pCity, religion, pantheonBelief, founderBelief, followerBelief, followerBelief2, enhancerBelief)
local iPlayer = pCity:GetOwner()
local iReligion = GameInfoTypes[religion]

-- Optional extra beliefs
local iBelief4 = followerBelief2 and GameInfoTypes[followerBelief2] or -1
local iBelief5 = enhancerBelief and GameInfoTypes[enhancerBelief] or -1

Game.FoundPantheon(iPlayer, GameInfoTypes[pantheonBelief])
Game.FoundReligion(iPlayer, iReligion, nil, GameInfoTypes[founderBelief], GameInfoTypes[followerBelief], iBelief4, iBelief5, pCity)
end

function FoundChristianity()
for iPlayer=0, GameDefines.MAX_CIV_PLAYERS - 1 do
local pPlayer = Players[iPlayer]
if (pPlayer:IsEverAlive()) then

for cityIndex = 0, pPlayer:GetNumCities() - 1, 1 do
local pCity = pPlayer:GetCityByID(cityIndex)

-- City exists and has the proper name?
if pCity ~= nil and pCity:GetName() == "Rome" then

CreateChristianHolyCity(pCity, "RELIGION_CHRISTIANITY", "BELIEF_FORMAL_LITURGY",
"BELIEF_PAPAL_PRIMACY", "BELIEF_CATHEDRALS", "BELIEF_HOLY_ORDER", "BELIEF_MISSIONARY_ZEAL");
return pCity
end
end
end
end
end

local pChristianPlot = FoundChristianity():Plot()
 
The code is good from what I can tell...

You are right in loading the file via content and not actions... I was not home when I typed that and forgot the exact tab to use.

Have you loaded a lua file in this manner before? If not, it is quite possible you did not type in the code's file name correctly. You need the exact path. For example, if your file , let us call it FoundChristianity.lua, is located in a folder called LUA within your mod, you need to set file name as: LUA/FoundChristianity.lua . If it is even further down in the file structure, say in a folder called Religion under LUA, then: LUA/Religion/FoundChristianity.lua

Under actions tab, you should have type:InGameUIAddin... name and description are up to you, and FileName as stated above. Do not set VFS true.

The above all assumes your default path is set up correctly within ModBuddy. If other mods have been working correctly for you, then that is likely the case.

Let me know if it is working.
 
Working fine now thanks. It was either an issue in the Content tab listing or the VFS being TRUE. I'm now off to clone it and create all the other religions I need.

Thanks again! Khan
 
I have noticed a problem with my found religion and create followers snippets. They run whenever a saved game is started up again. So the holycity and followers are reestablished every time a saved game is loaded.

I have created a fix for the problem... I have made the religions and followers functions related to an event... they will not be established until the end of the active player turn.

The snippet has been altered to reflect this.
 
Craig

I cannot thank you enough for this I have been trying to get to Civs to declare war in a mod for HOURS and HOURS. Now with your code I got it working in 20 mins.

I have come across one issue....when my civs declares war there is no notification to the player..do you have any code that would add this notification when war is declared?

Also...I am trying to declare war when a specific civ has control of 2 specific cities instead of on a specific turn number....Any ideas?
 
I don't yet know how to print text to screen. I'm sure there must be some sort of command to do it, something similar to the generic print command that you can insert into lua code. I have seen the "Dprint" command in some mods and that may do it, but I am not certain.

As for DOWs when certain cities are owned by a civilization, look at the snippet above that has specific cities adopting religions... most of the code likely could be adapted, I think, to enable a DOW rather than to adopt a religion.

I'm behind in adding snippets... I'll be adding a few more in the near future. I'm really glad you found some use from the ones posted. I encourage you to add some if you find something useful (ie printing text to the player).
 
This snippet acts when a city is captured. If a city state captures the city, and it is not the original owner, if it is allied to a player civilization, the city will be auto traded to the allied civilization.

Code:
-- when a city is captured by a city state, and that city was not originally owned by the city state
-- It will be transferred to a major power ally if such exists.

function ChangeOwner (oldPlayerID, bCapital, iX, iY, newPlayerID, bConquest)

local newPlayer = Players[newPlayerID];
local oldPlayer = Players[oldPlayerID]; --not needed
local plot = Map.GetPlot(iX, iY);
local pCity = plot:GetPlotCity();


-- find major civilization

	for iPlayer=0, GameDefines.MAX_MAJOR_CIVS-1 do 

	local pAlly = Players[iPlayer]

		if pAlly:IsAlive() and newPlayer:IsAllies(iPlayer) then
	
		Ally = pAlly
				
		end	
	end

-- City exists, is not originally owned by city state and city state is allied with Ally?

    if newPlayer:IsMinorCiv() and pCity ~= nil  and (pCity:GetOriginalOwner() ~= newPlayerID) then
			
			print("Transferring...", pCity:GetName(), "to" , Ally:GetName(), "from", newPlayer:GetName() )
						
			Ally:AcquireCity(pCity, false, true)
			pCity:DoTask(TaskTypes.TASK_UNRAZE);
	end

end


GameEvents.CityCaptureComplete.Add(ChangeOwner)
 
This code will move the player capital to the named city. I use this when I want to have a civilization choose the historic capital or move the capital during the course of a game (for example, when Wessex moved its capital to London from Winchester upon formation of the Kingdom of England).

Code:
-- for player civ Normandy, loop through the players
-- for each player loop through their cities to see if the the historic capital is the capital
-- if not, build the palace in the historic capital.

function ResetNormandyCapital()

for iPlayer=0, GameDefines.MAX_MAJOR_CIVS-1 do

	local pPlayer = Players[iPlayer];
		if pPlayer:IsEverAlive() then 

			if (GameInfo.Civilizations.CIVILIZATION_GREECE.ID == pPlayer:GetCivilizationType()) then
			
			-- Enumerate cities
			for cityIndex = 0, pPlayer:GetNumCities() - 1, 1 do

    			local pCity = pPlayer:GetCityByID(cityIndex)
				local oldCapital = pPlayer:GetCapitalCity()
				
				-- City exists and and is the historic capital and is not the current capital?
				if pCity ~= nil and pCity:GetName() == "Rouen" and not pCity:IsCapital() then 
					
					pCity:SetNumRealBuilding(GameInfoTypes["BUILDING_PALACE"], 1);
					oldCapital:SetNumRealBuilding(GameInfoTypes["BUILDING_PALACE"], 0);
					
    			
					return pCity							
    			end
			end
		end
		end
end
end

Events.ActivePlayerTurnEnd.Add(ResetNormandyCapital)
 
Back
Top Bottom