Resources Spawn Buildings

#1) which Machiavelli mod? can't really answer #1 without knowing what the rest of the file looks like. Likely he has predefined cityID elsewhere than within that function as a global.
It looks like that whole line is not suppose to be there. CityID is nil but its result is never used so it doesn't cause a problem.
---------------
I don't know of a way to detect when a resource is connected or disconnected, you'll have to poll each turn (meaning use PlayerDoTurn to check each turn).

Ulixes, can you specify what effect you're trying to implement? If I understand it correctly you want the following:

When a player has a specific resource every city that is connected to their capital should be given a free building (that can not normally be built).

In some of the more recent code I'm not sure what the purpose of "numCottonBuildings" or "numSilkBuildings" is suppose to serve.
 
Hi Machiavelli, glad to see you again! :)

It looks like that whole line is not suppose to be there. CityID is nil but its result is never used so it doesn't cause a problem.

"CityID" mystery solved!

I don't know of a way to detect when a resource is connected or disconnected, you'll have to poll each turn (meaning use PlayerDoTurn to check each turn).

Yep, not having an event when a resource is connected/disconnected the code has to be executed every turn, this is the reason an optimization is probably advisable.
Considering on how many resources it is applied every turn, the less heavy it is on performances, the better.

Ulixes, can you specify what effect you're trying to implement? If I understand it correctly you want the following:

Precisely, more specifically:

When a player has a specific resource (owned or traded) every city that is connected to their capital should be given a free building (that can not normally be built).


In some of the more recent code I'm not sure what the purpose of "numCottonBuildings" or "numSilkBuildings" is suppose to serve.

Maybe LeeS is more qualified than me to answer but, the way I understand it, "0" is building absent (or deleted) if the resource is disconnected, "1" the building is present when the resource is connected, but you probably already know that.
 
The following (untested) code sounds like what you want then:
Code:
function AddOrRemove_ResourceBuildings(playerID)
	local player = Players[playerID];
	local hasResource = false;
	local buildingID = GameInfoTypes["BUILDING_GRANARY"];
	
	if(player:GetNumResourceAvailable(GameInfoTypes["RESOURCE_SILK"], true) > 0 or
		player:GetNumResourceAvailable(GameInfoTypes["RESOURCE_COTTON"], true) > 0) then
		hasResource = true;
	end

	for city in Players[playerID]:Cities() do
		if(hasResource and (city:IsCapital() or player:IsCapitalConnectedToCity(city))) then
			city:SetNumRealBuilding(buildingID, 1);
		else
			city:SetNumRealBuilding(buildingID, 0);
		end
	end
end
GameEvents.PlayerDoTurn.Add(AddOrRemove_ResourceBuildings);
 
They're being used as binary conditions (0 or 1) based on whether or not the player has the resource, and so that when running the loop through the player's cities it is not necessary to keep querrying the game as to whether the player has the resource for every city in the player's list.

When running through the player's list of cities, there are two possible "main conditions" apart from whether or not the player has access to any individual resource:
  1. If the city is not connected to the capital, it isn't necessary to know one way or the other whether the player has access to the resource -- the buildings are all "turned off" for that city regardless of whether or not the player has the resource available. The code as written will therefore correct conditions in any city where the "connection-route" to the capital was lost.
  2. If the city is connected to the capital, then the status as to whether each individual building within each city should be turned on or turned off is determined by the data gathered before the loop through the player's cities was executed.
My understanding has always been that City:SetNumRealBuilding(BuildingType, newValue) wants integer and not boolean for "newValue" therefore we need to set the variable for the "is connected to capital" portion of the code to an integer.

The structure allows the code to gather the player-level information at the player-level one time per turn, rather than at the city level for every city in the empire. The later method seemed like a lot of potential extra and unnessecary processing to me, hence the method used.
 
The following (untested) code sounds like what you want then:
Code:
function AddOrRemove_ResourceBuildings(playerID)
	local player = Players[playerID];
	local hasResource = false;
	local buildingID = GameInfoTypes["BUILDING_GRANARY"];
	
	if(player:GetNumResourceAvailable(GameInfoTypes["RESOURCE_SILK"], true) > 0 or
		player:GetNumResourceAvailable(GameInfoTypes["RESOURCE_COTTON"], true) > 0) then
		hasResource = true;
	end

	for city in Players[playerID]:Cities() do
		if(hasResource and (city:IsCapital() or player:IsCapitalConnectedToCity(city))) then
			city:SetNumRealBuilding(buildingID, 1);
		else
			city:SetNumRealBuilding(buildingID, 0);
		end
	end
end
GameEvents.PlayerDoTurn.Add(AddOrRemove_ResourceBuildings);
Except that you've just turned off:
  1. multiple buildings, each being tied to an individual resource
  2. multiple resources being evaluated independant of one another
And you are adding back into the mix:
  1. requirement that for every city there will need to be an independant "if then else end" block for every additional resource that he would like to add to the system even if he re-writes this portion:
    Code:
    	local hasResource = false;
    	local buildingID = GameInfoTypes["BUILDING_GRANARY"];
    	
    	if(player:GetNumResourceAvailable(GameInfoTypes["RESOURCE_SILK"], true) > 0 or
    		player:GetNumResourceAvailable(GameInfoTypes["RESOURCE_COTTON"], true) > 0) then
    		hasResource = true;
    	end
    to be more like this:
    Code:
    	local hasSilk = false;
    	local SilkBuildingID = GameInfoTypes["BUILDING_LIBRARY"];
    
    	local hasCotton = false;
    	local CottonBuildingID = GameInfoTypes["BUILDING_GRANARY"];
    	
    	if(player:GetNumResourceAvailable(GameInfoTypes["RESOURCE_SILK"], true) > 0 then
    		hasSilk = true;
    	end
    	if(player:GetNumResourceAvailable(GameInfoTypes["RESOURCE_COTTON"], true) > 0) then
    		hasCotton = true;
    	end
  2. The processing hit every turn for every player to look up the ID#s of Cotton, Silk, Granary, Library, etc., etc. instead of looking them up once upon game loading.
 
If Ulixes only wants one resource-grants-building than the previous code should work, it is the simplest case. If Ulixes wants to support multiple resource-grants-building than that code will need to be expanded to use look ups from a new XML table. If it needs to support resource-grants-building-ORs and resource-grants-building-ANDs than the hasResource section needs to be expanded.

No matter what - every city will need to be checked for every resource-grants-building each turn. But there should only need to be one pass through each city.

Once you're using an XML table to hold the requirements for resource-grants-buildings there isn't a need to cache the info in local variables on game load since the XML tables are already cached on game start and look ups are (relatively) cheap. At that point it comes down to configuring the code with an XML table verse putting the XML table in local variables.
 
To clarify my intentions:

I'm going to give every resource in my project (107, so far) a different spawned (dummy) building.

The relationship "Resource - Spawned Building" will be "1 - 1", one resources, one dummy building.
I'm not planning to make a building require 2 (or more) resources or one of more resurces to spawn (in case of an exception, I think I can easily do it with one of the previous codes).

The goal is to use all the XML Tables made for Buildings to give yields to resources.

Instead of making a code every time you want to give a bonus to a resource, this code replace them all.

If you want to give gold, culture, food, production, faith, Tourism, Experience, Promotions, Diplomatic Bonus, etc.
You give them to the spawned building.

With a code like this, almost everything that is possible for a building, is possible for a resource.
(well, not specialists, sadly)

That is pretty awesome, in my opinion. :)

Sure, the bonus is applied to all owned cities but I consider this fair.
Anyway, in case an exception would be needed, one of the previous LeeS codes with the condition "if (city:IsCapital())" would do the trick.

I hope you guys consider this a good idea too, especially considering that Firaxis didn't give us many possibilities to work with resources.
 
Code:
iResourceSilk = GameInfoTypes["RESOURCE_SILK"]
iSilkBuildings = GameInfoTypes["BUILDING_LIBRARY"]
iResourceCotton = GameInfoTypes["RESOURCE_COTTON"]
iCottonBuildings = GameInfoTypes["BUILDING_GRANARY"]

function ResourcesSpawnBuildings(playerID)
	local player = Players[playerID];
	for pCity in player:Cities() do
		if player:IsCapitalConnectedToCity(pCity) then
			pCity:SetNumRealBuilding(iSilkBuildings, ((player:GetNumResourceAvailable(iResourceSilk, true) > 0) and 1 or 0))
			pCity:SetNumRealBuilding(iCottonBuildings, ((player:GetNumResourceAvailable(iResourceCotton, true) > 0) and 1 or 0))
		else
			pCity:SetNumRealBuilding(iSilkBuildings, 0)
			pCity:SetNumRealBuilding(iCottonBuildings, 0)
		end
	end
end
GameEvents.PlayerDoTurn.Add(ResourcesSpawnBuildings);
Tested. Did not see any incorrect behaviors. The capital city is always considered by the game to be connected to itself, even if none of the "route" technologies have been discovered.
 
Thank you LeeS! :)

Anyway, after adding the game event (dumb me!) the first Bobert13's code worked (not the second)

Code:
local iResourceSilk = GameInfoTypes["RESOURCE_SILK"]
local iSilkBuildings = GameInfoTypes["BUILDING_LIBRARY"]
local iResourceCotton = GameInfoTypes["RESOURCE_COTTON"]
local iCottonBuildings = GameInfoTypes["BUILDING_GRANARY"]

--local ResourcesSpawnBuildings = function(playerID) -- this is equivalent to the line below. The latter is syntactic sugar.
local function ResourcesSpawnBuildings(playerID)
	local player = Players[playerID];
	local iNumberSilkBuildings, iNumberCottonBuildings = 0, 0
	if player:GetNumResourceAvailable(iResourceSilk, true) > 0 then
		iNumberSilkBuildings = 1
	end
	if player:GetNumResourceAvailable(iResourceCotton, true) > 0 then
		iNumberCottonBuildings = 1
	end
	for pCity in player:Cities() do
		if player:IsCapitalConnectedToCity(pCity) then
			pCity:SetNumRealBuilding(iSilkBuildings, iNumberSilkBuildings)
			pCity:SetNumRealBuilding(iCottonBuildings, iNumberCottonBuildings)
		else
			pCity:SetNumRealBuilding(iSilkBuildings, 0)
			pCity:SetNumRealBuilding(iCottonBuildings, 0)
		end
	end
end
GameEvents.PlayerDoTurn.Add(ResourcesSpawnBuildings);

So, now the only doubt I have is which code I'm supposed to use to have less performance hit.

Bobert13 said:

Global lookups are significantly slower than locals, hence why this is an optimization.

(I even tried to create the "XML table" solution Machiavelli suggested using one of his codes as model but I ended up with nothing, so, I rely on the codes in this thread).

Any opinion on this matter ?
 
Yeah, we should go back to something more like this if using my newer method of sticking the result for whether a player has a specific resource into a binary variable.
Code:
local iResourceSilk = GameInfoTypes["RESOURCE_SILK"]
local iSilkBuildings = GameInfoTypes["BUILDING_LIBRARY"]
local iResourceCotton = GameInfoTypes["RESOURCE_COTTON"]
local iCottonBuildings = GameInfoTypes["BUILDING_GRANARY"]

function ResourcesSpawnBuildings(playerID)
	local player = Players[playerID];
	local iBinaryForSilk = ((player:GetNumResourceAvailable(iResourceSilk, true) > 0) and 1 or 0)
	local iBinaryForCotton = ((player:GetNumResourceAvailable(iResourceCotton, true) > 0) and 1 or 0)
	for pCity in player:Cities() do
		if player:IsCapitalConnectedToCity(pCity) then
			pCity:SetNumRealBuilding(iSilkBuildings, iBinaryForSilk)
			pCity:SetNumRealBuilding(iCottonBuildings, iBinaryForCotton)
		else
			pCity:SetNumRealBuilding(iSilkBuildings, 0)
			pCity:SetNumRealBuilding(iCottonBuildings, 0)
		end
	end
end
GameEvents.PlayerDoTurn.Add(ResourcesSpawnBuildings);
Even if there are some performance hit issues involved in creating an lua table to hold all the correspondances between Resource_X -- Building_X, I would probably still create such a table so that it can be interated through for every city. The problem is you need to balance potential performance hits against a code so bloated with lines it is hard to follow or trouble-shoot later.

The real issue in creating a table within lua to hold all the correspondances is whether or not there will be multiple buildings given in a city for having, say, Silk. And it is only an issue in the sense that it drives how the table must be constructed and how the table must be iterated through.
 
http://www.lua.org/gems/sample.pdf

Skip to the Basic Facts section.

Thanks for the reference, I'll read it later. :)

The real issue in creating a table within lua to hold all the correspondances is whether or not there will be multiple buildings given in a city for having, say, Silk.

No, it is not in my plans.
As I mentioned before the relationship "Building - Resource" is 1:1.
Silk - Building 1
Cotton - Building 2
Sugar - Building 3
Gold - Building 4
...and so on

no multiple buildings for resource.

I usually avoid Lua functions that are not linked to a specific event, so, this will be probably the only one to repeat every turn.

Yet, given for how many resources will be used (107), I was worried about a possible big impact on performance, so I was hoping in some "prediction" on how many seconds this code could slow down the game (maybe it is negligible) but I suppose I'll discover this when I try the code on the full mod.
 
If you're doing a single-dimensional table stored at integer 1-n keys, then there literally is no performance impact between using a table and using local variables. Returning such a table out of a function has a hit but not using one internally.

When you start placing things at me.myfriend.hiscousin is when table performance really starts to degrade in Lua. This can't map out to local registers in the VM and requires a hash lookup for each level under "me". It's even worse if "me" is a Global.
 
Back
Top Bottom