Adding xml tags that work through Lua (eg, policy-dependent units & buildings)

Let's say I want a specific tile improvement (Citadel, etc) within the city borders as the prerequisite for a building, sort of the same way resources are now.

How do you do it? :confused:
 
You have to be very careful about this sort of thing. The game engine will run this test many times in a turn, which could impact game speed.

But here's some code that ought to do what you want (not tested):
Code:
local IMPROVEMENT_CITADEL_ID = GameInfoTypes.IMPROVEMENT_CITADEL
local TestCityCanConstruct = {}

local function OnCityCanConstruct(iPlayer, iCity, buildingTypeID)
	if TestCityCanConstruct[buildingTypeID] then
		return TestCityCanConstruct[buildingTypeID](iPlayer, iCity)
	end
	return true
end
GameEvents.CityCanConstruct.Add(OnCityCanConstruct)

TestCityCanConstruct[GameInfoTypes.BUILDING_MY_NEW_BUILDING] = function(iPlayer, iCity)
	local player = Players[iPlayer]
	local city = player:GetCityByID(iCity)
	local totalPlots = city:GetNumCityPlots()
	for i = 0, totalPlots - 1 do
		local plot = city:GetCityIndexPlot(i)
		if plot and plot:GetOwner() == iPlayer and plot:GetImprovementType() == IMPROVEMENT_CITADEL_ID and plot:GetWorkingCity() == city then
			return true
		end
	end
	return false
end

It's coded so that you can easily add other building tests (as separate functions) as I did for BUILDING_MY_NEW_BUILDING.

Note that this doesn't run exactly like Civ5 resource checks. The problem is that "working city" does not equal "owning city". There is actually no way to get owning city in Lua, unfortunately. If you want the building to be allowed for all cities that have the cititel in their 3-plot radius, just remove "and plot:GetWorkingCity() == city" from above code.
 
Pazyryk, would you be willing to update this with DLL information as well (adding tags through SQL, then loading them using the DLL)? If I wrote the section?
 
Pazyryk, would you be willing to update this with DLL information as well (adding tags through SQL, then loading them using the DLL)? If I wrote the section?
Sure thing. Although we'll have to ask Gedemon to change the title of the Tutorial.

It may be worth writing a separate tutorial, however, since Lua -> DLL modding is a big step for many, just like XML -> XML+Lua is a big step. If you do feel free to copy anything from this thread.

But I'll be happy to paste in a DLL section here if you prefer that.
 
It may be worth writing a separate tutorial, however, since Lua -> DLL modding is a big step for many, just like XML -> XML+Lua is a big step. If you do feel free to copy anything from this thread.

I wanted to make a separate table in the database to hold the stacking classes in my DLL, but when I've started to look at how to implement that, I've sundenly decided that 2 columns in an existing table was more than enough ! :D
 
I wanted to make a separate table in the database to hold the stacking classes in my DLL, but when I've started to look at how to implement that, I've sundenly decided that 2 columns in an existing table was more than enough ! :D

I've made a separate table (two actually) in the database and got it in DLL without crashing (and it loads and saves), and it even loads into the game. Except until I do a ton of other stuff, I can't test it.

Okay! I'll start writing a tutorial sometime. Maybe by next week I'll get it done.
 
Here's a REALLY REALLY REALLY rough draft. Obviously this isn't a tutorial but a more of "here's what I did"

Part One: Adding a new column to an existing table using SQL and loading it into the DLL (Difficulty: Medium)

Step 1) Create a new column into an existing table using SQL.

Code:
-- Add VassalageEnabled (boolean), default false, into the Eras table
ALTER TABLE Eras ADD VassalageEnabled	boolean DEFAULT 0;
-- Update the Medieval Era to now include VassalageEnabled. This is equivalent to <VassalageEnabled>true</VassalageEnabled>
UPDATE Eras SET	'VassalageEnabled' = 1 WHERE Type = 'ERA_MEDIEVAL';

Step 2) Altering the DLL

CvInfos.h, in class CvEraInfo
Code:
// public:
bool getVassalageEnabled() const;
// protected:
bool m_bVassalageEnabled;

CvInfos.cpp
Code:
// CvEraInfo::CacheResults()
// This code reads in from your XML value and stores it in the m_bVassalageEnabled variable
m_bVassalageEnabled = kResults.GetBool("VassalageEnabled");

// This function returns the VassalageEnabled
bool CvEraInfo::getVassalageEnabled() const
{
	return m_bVassalageEnabled;
}

Step 3) Using the newly read value

Code:
// This code will grant the user the ability to use vassalage upon entering the era which grants Vassalage (meaning all you have to do is change which era does this in the XML. 
if(pEraInfo->getVassalageEnabled())
{
	if(!GC.getGame().isOption(GAMEOPTION_NO_VASSALAGE))
	{
	        changeVassalageTradingAllowedCount(1);
		// Send notification to team
                PlayerTypes ePlayer;
		for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++)
		{
			ePlayer = (PlayerTypes) iPlayerLoop;
			if(GET_PLAYER(ePlayer).getTeam() != GetID())
			{
				continue;
			}

			if(GET_PLAYER(ePlayer).GetID() == GC.getGame().getActivePlayer())
			{
				CvNotifications* pNotifications = GET_PLAYER(ePlayer).GetNotifications();
			        if(pNotifications)
				{
					CvEraInfo* pkEraInfo = GC.getEraInfo(eNewValue) ;
					const char* szEra = pkEraInfo->GetTextKey();
					CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_VASSALAGE_ALLOWED", szEra);
					CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_SUMMARY_VASSALAGE_ALLOWED");
					pNotifications->Add(NOTIFICATION_GENERIC, strBuffer, strSummary, -1, -1, 0);
				}
			}
		}
	}
}

Part Two: Adding a new table into the game using XML and loading it into the DLL (Difficulty: Very High)

Unfinished
 
You have to be very careful about this sort of thing. The game engine will run this test many times in a turn, which could impact game speed.

But here's some code that ought to do what you want (not tested):
Code:
local IMPROVEMENT_CITADEL_ID = GameInfoTypes.IMPROVEMENT_CITADEL
local TestCityCanConstruct = {}

local function OnCityCanConstruct(iPlayer, iCity, buildingTypeID)
	if TestCityCanConstruct[buildingTypeID] then
		return TestCityCanConstruct[buildingTypeID](iPlayer, iCity)
	end
	return true
end
GameEvents.CityCanConstruct.Add(OnCityCanConstruct)

TestCityCanConstruct[GameInfoTypes.BUILDING_MY_NEW_BUILDING] = function(iPlayer, iCity)
	local player = Players[iPlayer]
	local city = player:GetCityByID(iCity)
	local totalPlots = city:GetNumCityPlots()
	for i = 0, totalPlots - 1 do
		local plot = city:GetCityIndexPlot(i)
		if plot and plot:GetOwner() == iPlayer and plot:GetImprovementType() == IMPROVEMENT_CITADEL_ID and plot:GetWorkingCity() == city then
			return true
		end
	end
	return false
end

It's coded so that you can easily add other building tests (as separate functions) as I did for BUILDING_MY_NEW_BUILDING.

Note that this doesn't run exactly like Civ5 resource checks. The problem is that "working city" does not equal "owning city". There is actually no way to get owning city in Lua, unfortunately. If you want the building to be allowed for all cities that have the cititel in their 3-plot radius, just remove "and plot:GetWorkingCity() == city" from above code.


To test this out, I went ahead and copied the above code into a new SQL file, then replaced BUILDING_MY_NEW_BUILDING to BUILDING_MONUMENT.

I went to start a new game, and I'm still able to build the Monument immediately, without the prerequisite Citadel in the city radius.

Any ideas on why it's not doing it? :confused:
 
@Grave, First step, add this:
Code:
print("OnCityCanConstruct: ", iPlayer, iCity, buildingTypeID)
...right after the function call. Then add more print statements here and there. In fact, put this:
Code:
print("Loading my Lua file XXX")
...at the top of the Lua file. Then run game. This is a general rule for Lua coding. Even I do it.

You should see "Loading my Lua file XXX" at game init (after all the map generation printout). You will see "OnCityCanConstruct: ..." many many many times per turn. In fact, so many times that it may slow the game noticeably. (You'll have to disable this print after you are done debugging.)

If you don't see one or the other prints (or others you add), then you know where the failure occurred.


@Putmalk, It will be useful to finally see some C++/DLL modification in the Tutorials section. I can certainly learn from it.
 
Success!

As was expected, I botched up the code a little. Those minor attention to details will get you every time. But, I figured out what I did wrong, and corrected it. I did learn a little bit from this, so thank you for sharing. Lua is completely new to me... I'm used to the old Python from Civ IV still.

Thanks for your help! :D
 
The DLL component this tutorial is up here. I request you link it from this tutorial so we have a quick cross reference between DLL and Lua methods.

My tutorial will down the road also contain how to create a new XML table (i.e. Unit_FreePromotions) and load and use them, but it's difficult and I haven't mastered it yet.
 
Hi,

I was looking over some of the Building bonuses from BNW and noticed that the Royal Library UB gives an exp bonus to units trained if a Great Work is housed there.

Unfortunately I found that in the XML there was a specific table just for the exp bonus per great work. I was wondering how would a bonus dependent on a great work being present in X building work in Lua? I tried searching but I haven't found anything.
 
Top Bottom