Using citycantrain and failed.

Craig_Sutter

Deity
Joined
Aug 13, 2002
Messages
2,773
Location
Calgary, Canada
I am trying to get a city able to train a specific unit if the city has a palace or a specific building (Burgh Court) and the number of units is less than the number Burgh Courts plus the palace. Later I will add more units and civilizations with or statements added to the parts outlined in green...

In testing, when I get to the build screen, no units show up at all. Something is not working with my logic, but I can't figure it out.



Code:
--allows Northumbria to build anglo-saxon units equal to its number of Burgh Court plus one for the capital

function NorthumbriaUnit (iPlayer, iCity, iUnit)
	
	local ASunit = false
	local ASciv = false
	local iCeorl = GameInfoTypes.UNIT_CEORL
	local iBuilding = GameInfoTypes.BUILDING_BURGH_COURT
	local iPalace = GameInfoTypes.BUILDING_PALACE
	local player = Players[iPlayer]
	local city = player:GetCityByID(iCity);

	-- determine if unit is Anglo-Saxon

	[COLOR="SeaGreen"]if iUnit == iCeorl then[/COLOR]
	ASunit = true
	end

	-- determine if civ is Anglo-Saxon

	[COLOR="SeaGreen"]if iPlayer == GameInfoTypes.CIVILIZATION_RUSSIA then[/COLOR]
	ASciv = true
	end

	-- check for unit, civ and building

	if ASunit and ASciv and city:IsHasBuilding(iBuilding) or ASunit and ASciv and city:IsHasBuilding(iPalace) then
		
		local BuildingCount = 0		
		
		for pCity in player:Cities() do			

			if pCity:IsHasBuilding(iBuilding) then

			BuildingCount = BuildingCount + 1

			end
		end	

		return (BuildingCount+1 > player:GetUnitClassCount(iCeorl))

		else
			return false
		
	end
	
	return true
	
end

GameEvents.CityCanTrain.Add(NorthumbriaUnit)

I think I am close, but no cigar.

Thanks for advice.


PS My newest try came with similar results... must be using faulty logic...

Code:
--allows Northumbria to build anglo-saxon units equal to its number of Burgh Courts plus one for the capital

function NorthumbriaUnit (iPlayer, iCity, iUnit)	
	
	local iCeorl = GameInfoTypes.UNIT_CEORL
	local iBuilding = GameInfoTypes.BUILDING_BURGH_COURT
	local iPalace = GameInfoTypes.BUILDING_PALACE
	local iNorthumbria = GameInfoTypes.CIVILIZATION_RUSSIA
	local player = Players[iPlayer]
	local city = player:GetCityByID(iCity);

	-- determine if unit is Anglo-Saxon

	if iUnit == iCeorl and iPlayer == iNorthumbria and city:IsHasBuilding(iBuilding) or
	iUnit == iCeorl and iPlayer == iNorthumbria and city:IsHasBuilding(iPalace) then	
		
		local BuildingCount = 0		
		
		for pCity in player:Cities() do			

			if pCity:IsHasBuilding(iBuilding) then

			BuildingCount = BuildingCount + 1

			end
		end	

		return (BuildingCount+1 > player:GetUnitClassCount(iCeorl))

		else
			return false
		
	end
	
	return true
	
end

GameEvents.CityCanTrain.Add(NorthumbriaUnit)

Another attempt, but this time I can build a unit... but no restrictions on numbers. :(

Code:
--allows Northumbria to build anglo-saxon units equal to its number of Burgh Courst plus one for the capital

function NorthumbriaUnit (iPlayer, iCity, iUnit)	
	
	local iCeorl = GameInfoTypes.UNIT_CEORL
	local iBuilding = GameInfoTypes.BUILDING_BURGH_COURT
	local iPalace = GameInfoTypes.BUILDING_PALACE
	local iNorthumbria = GameInfoTypes.CIVILIZATION_RUSSIA
	local player = Players[iPlayer]
	local city = player:GetCityByID(iCity);

	-- determine if unit is Anglo-Saxon

	if iUnit == iCeorl and iPlayer == iNorthumbria and city:IsHasBuilding(iBuilding) or
	iUnit == iCeorl and iPlayer == iNorthumbria and city:IsHasBuilding(iPalace) then	
		
		local BuildingCount = 0		
		
		for pCity in player:Cities() do			

			if pCity:IsHasBuilding(iBuilding) then

			BuildingCount = BuildingCount + 1

			end
		end	

		if (BuildingCount+1 > player:GetUnitClassCount(iCeorl)) then
			return true
		else
			return false
		end		
		
	end	
	return true
	
end

GameEvents.CityCanTrain.Add(NorthumbriaUnit)
 
Ahhh... not able to test now, but iCeorl is not a unitclass. I need to derive the unit class to get the number...

Have to wait until after work to try.

PS: Is there a function to call number of units by unittype rather than unitclass? I ask this because, though the Ceorl in my mod has its own unitclass, this may not always be the case... and I may wish to determine the numbers of a specific unit.

PPS: google searches have turned up a couple of ways I can count unit numbers via iteration. Does not appear to be an existing function unless added during the last patch (which the modding wiki does not show).

I will go over my code when I get home, but it looks like I'd best use a counting method for the units instead of the easier way of using the class as it is more flexible (oddly, as it is also narrower).
 
  1. Obviously you will need to change the designations for UNITCLASS_CEORL, BUILDINGCLASS_BURGH_COURT, etc., if they aren't quite correct.
  2. You would want to change the designation of UNITCLASS_CEORL to the correct one in any case if you are defining it as, for example, a unit that fits within UNITCLASS_SWORDSMAN
  3. The code as written would need some adjustments if you are going to try to have multiple units from within the same UnitClass being limited by the number of your special buildings.
    • player:GetUnitClassCount(tNorthumbriaUnits[iUnit]) would count all units belonging to the player that also belong to the designated unit-class, so it all depend on how your UNIT_CEORL is defined, what class it fits into, etc., and if the player can be gifted units within the class from city-states, for example.
  4. Adding additional unit-to-class correspondances to table tNorthumbriaUnits will allow the code to adapt without any other changes anywhere.
Code:
--allows Northumbria to build anglo-saxon units equal to its number of Burgh Courst plus one for the capital

local tNorthumbriaUnits = { [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.UNITCLASS_CEORL }
local iNorthumbria = GameInfoTypes.CIVILIZATION_RUSSIA
local iBurghCourtBuilding = GameInfoTypes.BUILDING_BURGH_COURT
local iBurghCourtBuildingClass = GameInfoTypes.BUILDINGCLASS_BURGH_COURT
local iPalaceBuilding = GameInfoTypes.BUILDING_PALACE

function NorthumbriaUnit (iPlayer, iCity, iUnit)	
	if tNorthumbriaUnits[iUnit] then
		local player = Players[iPlayer]
			-- determine if unit is Anglo-Saxon
		if player:GetCivilizationType() ~= iNorthumbria then
			return false
		else
			local city = player:GetCityByID(iCity)
			if city:IsHasBuilding(iBurghCourtBuilding) or city:IsHasBuilding(iPalaceBuilding) then	
				return ((player:GetBuildingClassCount(iBurghCourtBuildingClass) + 1) > player:GetUnitClassCount(tNorthumbriaUnits[iUnit]))
			else		
				return false		
			end
		end
	else	
		return true
	end
end
GameEvents.CityCanTrain.Add(NorthumbriaUnit)
 
Okay, I've looked over your code extensively. It was very instructive.

I've tinkered with it quite a bit as I think I really need to get away from unitclasses to unit IDs... Though not the case for these particular units, I have to differentiate units within the same class as some will be affected by CityCanTrain while others within the class will not be.

I also wanted different civs with different unique buildings to be covered, so I made additional modifications to that affect.

However, my knowledge is very limited... so I took you code and came up with an altered one. But I ended up not being able to construct a unit at all.

The code is moved away from reliance on unitclass and building classes to unitids and buildingids. This necessitated the counting iterations I've added.

My major problem is that this is the first time I have used tables. I am afraid I am misusing them and this is why I am getting a "false" at the end of the function. Either that, or I am misusing "return true" and "return false". Or it could be a combination of both....

Here is my altered code taking into account what I learned from your example...

Code:
--allows building of anglo-saxon units equal to the number of unique buildings plus one for the capital

function ASUnit (iPlayer, iCity, iUnit)

local tAngloSaxonUnits = {"[GameInfoTypes.UNIT_CEORL] ","[GameInfoTypes.UNIT_FYRDMAN]","[GameInfoTypes.UNIT_HUSCARL]"}
local tAngloSaxonCivs = {"[GameInfoTypes.CIVILIZATION_RUSSIA]","[GameInfoTypes.CIVILIZATION_ENGLAND]","[GameInfoTypes.CIVILIZATION_CHINA]"}
local tAngloSaxonBuildings = {"[GameInfoTypes.BUILDING_BURGH_COURT]","[GameInfoTypes.BUILDING_BURH]","[GameInfoTypes.BUILDING_MOOT_HILL]"}
local iPalaceBuilding = GameInfoTypes.BUILDING_PALACE

	--differentiate Anglo-Saxon units
	
	if tAngloSaxonUnits[iUnit] then

		-- determine if unit is Anglo-Saxon
		local player = Players[iPlayer]
		if player:GetCivilizationType() ~=  tAngloSaxonCivs then
			return false

		else
--------------------------------------------------------------------------------------------
		
			-- check for Anglo-Saxon building or palace

			local city = player:GetCityByID(iCity)
			if city:IsHasBuilding(tAngloSaxonBuildings) or city:IsHasBuilding(iPalaceBuilding) then
			
				-- loop cities to count buildings that allow training		
						
				for pCity in player:Cities() do
					local BuildingCount = 0			
					if city:IsHasBuilding(tAngloSaxonBuildings) then
					BuildingCount = BuildingCount + 1
					end
				end

				-- loop units to find Anglo-Saxon units				
					
				for unit in player:Units() do
					local numUnits = 0	
					if unit:GetUnitType() == tAngloSaxonUnits then				
					numUnits = numUnits + 1
					end
				end

			-- check if building plus palace is greater than current units

				return BuildingCount+1 > numUnits
			else
				return false
			end	
-------------------------------------------------------------------------------		
			
		end

	return true		

	else	
		return false
	end
end

GameEvents.CityCanTrain.Add(ASUnit)

It's very late at night here, so I am going to get back to this tomorrow afternoon (my time).

I thank you for the coding model... my abilities are improving marginally over time, and seeing the examples given to accomplish my intentions has been a learning experience and causes me to search further into the methods used. The codes provided gave me some new ideas... as I said, I have no idea how to create tables in lua but can see their usefulness once I have learned to create and use them properly.

Thanks again for your advice and patience.

PSS Should I be using tAngloSaxonCivs[iPlayer] to check the Anglosaxon civilizations... I seem to think this is short for iPlayer = {civ table} being true?


PPS I think my table need to be redone in this format:

Spoiler :


local tAngloSaxonUnits = {GameInfoTypes["UNIT_CEORL"],GameInfoTypes["UNIT_FYRDMAN"],GameInfoTypes["UNIT_HUSCARL"]}



So the tables become:

Code:
local tAngloSaxonUnits = {GameInfoTypes["UNIT_CEORL"],GameInfoTypes["UNIT_FYRDMAN"],GameInfoTypes["UNIT_HUSCARL"]}
local tAngloSaxonCivs = {GameInfoTypes["CIVILIZATION_RUSSIA"],GameInfoTypes["CIVILIZATION_ENGLAND"],GameInfoTypes["CIVILIZATION_CHINA"]}
local tAngloSaxonBuildings = {GameInfoTypes["BUILDING_BURGH_COURT"],GameInfoTypes["BUILDING_BURH"],GameInfoTypes["BUILDING_MOOT_HILL"]}
local iPalaceBuilding = GameInfoTypes.BUILDING_PALACE


So my brand new code failed as well... but here it is. I'm going to return to this in the morning :)

Here is the latest try...

Code:
--allows building of anglo-saxon units equal to the number of unique buildings plus one for the capital

function ASUnit (iPlayer, iCity, iUnit)

local tAngloSaxonUnits = {GameInfoTypes["UNIT_CEORL"],GameInfoTypes["UNIT_FYRDMAN"],GameInfoTypes["UNIT_HUSCARL"]}
local tAngloSaxonCivs = {GameInfoTypes["CIVILIZATION_RUSSIA"],GameInfoTypes["CIVILIZATION_ENGLAND"],GameInfoTypes["CIVILIZATION_CHINA"]}
local tAngloSaxonBuildings = {GameInfoTypes["BUILDING_BURGH_COURT"],GameInfoTypes["BUILDING_BURH"],GameInfoTypes["BUILDING_MOOT_HILL"]}
local iPalaceBuilding = GameInfoTypes.BUILDING_PALACE

	--differentiate Anglo-Saxon units
	
	if tAngloSaxonUnits[iUnit] then

		-- determine if unit is Anglo-Saxon
		local player = Players[iPlayer]
		if player:GetCivilizationType() ~=  tAngloSaxonCivs then
			return false

		else
--------------------------------------------------------------------------------------------
		
			-- check for Anglo-Saxon building or palace

			local city = player:GetCityByID(iCity)
			if city:IsHasBuilding(tAngloSaxonBuildings) or city:IsHasBuilding(iPalaceBuilding) then
			
				-- loop cities to count buildings that allow training		
						
				for pCity in player:Cities() do
					local BuildingCount = 0			
					if city:IsHasBuilding(tAngloSaxonBuildings) then
					BuildingCount = BuildingCount + 1
					end
				end

				-- loop units to find Anglo-Saxon units				
					
				for unit in player:Units() do
					local numUnits = 0	
					if unit:GetUnitType() == tAngloSaxonUnits then				
					numUnits = numUnits + 1
					end
				end

			-- check if building plus palace is greater than current units

				return BuildingCount+1 > numUnits
			else
				return false
			end	
-------------------------------------------------------------------------------		
			
		end

	return true		

	else	
		return false
	end
end

GameEvents.CityCanTrain.Add(ASUnit)
 
If you are assigning each of the units to the civ in question by using the <Civilization_UnitClassOverrides> table, then the following code will work and all you need to do is add or remove unit->building assignments in the code for table tAngloSaxonUnits:
Spoiler using civ override table :
Code:
local tAngloSaxonUnits = { [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.BUILDING_BURGH_COURT , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.BUILDING_BURH , [GameInfoTypes.UNIT_HUSCARL] = GameInfoTypes.BUILDING_MOOT_HILL }
local tAngloSaxonCivs = {}
local tAngloSaxonBuildingClasses = {}
local iPalaceBuilding = GameInfoTypes.BUILDING_PALACE

for k,v in pairs(tAngloSaxonUnits) do
	tAngloSaxonBuildingClasses[k] = GameInfoTypes[GameInfo.Buildings[v].BuildingClass]
	for row in GameInfo.Civilization_UnitClassOverrides() do
		if GameInfoTypes[row.UnitType] == k then
			tAngloSaxonCivs[k] = GameInfoTypes[row.CivilizationType]
		end
	end
end

--allows building of anglo-saxon units equal to the number of unique buildings plus one for the capital

function ASUnit(iPlayer, iCity, iUnit)
	--differentiate Anglo-Saxon units
	if tAngloSaxonUnits[iUnit] then
		print("Unit is " .. iUnit)
		-- determine if unit is Anglo-Saxon
		local player = Players[iPlayer]
		if tAngloSaxonCivs[iUnit] == player:GetCivilizationType() then
			print("Player # is " .. iPlayer .. " and the Player's Civilization ID# is " .. player:GetCivilizationType())
			-- check for Anglo-Saxon building or palace
			local city = player:GetCityByID(iCity)
			if city:IsHasBuilding(tAngloSaxonUnits[iUnit]) or city:IsHasBuilding(iPalaceBuilding) then
				print("City was seen as having either the required building for the unit or as having the Palace")
				local numUnits = 0	
				for unit in player:Units() do
					if unit:GetUnitType() == iUnit then				
						numUnits = numUnits + 1
					end
				end
				print("numUnits was calaculated as " .. numUnits .. " for a unit of type " .. iUnit .. " " .. GameInfo.Units[iUnit].Type)
				print("the calculated number of required " .. GameInfo.Buildings[tAngloSaxonUnits[iUnit]].Type .. " in the empire was " .. player:GetBuildingClassCount(tAngloSaxonBuildingClasses[iUnit]) .. " plus 1 for the palace for a total of " .. (player:GetBuildingClassCount(tAngloSaxonBuildingClasses[iUnit]) + 1))
				return ((player:GetBuildingClassCount(tAngloSaxonBuildingClasses[iUnit]) + 1) > numUnits)
			else		
				print("City was seen as NOT having either the required building for the unit or as having the Palace, so FALSE is returned")
				return false	--needed when the city does not have the palace or the building that allows the unit		
			end
		else
			print("Player # is " .. iPlayer .. " and the Player's Civilization ID# is " .. player:GetCivilizationType() .. " which is seen as not being in the required table of civilizations, so FALSE is returned")
			return false	--default needed for units that ARE in the table tAngloSaxonUnits but for civs that ARE NOT in the table tAngloSaxonCivs
		end
	else	
		return true	--default needed for units NOT in the table tAngloSaxonUnits
	end
end
GameEvents.CityCanTrain.Add(ASUnit)
If all of the units are not registered for the correct civ under the <Civilization_UnitClassOverrides> table, then use the following slightly different method (note that I've just used randomly chosen civ names to show how the code might look for the three unit->civilization correspondances):
Spoiler no civ override table :
Code:
local tAngloSaxonUnits = { [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.BUILDING_BURGH_COURT , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.BUILDING_BURH , [GameInfoTypes.UNIT_HUSCARL] = GameInfoTypes.BUILDING_MOOT_HILL }
local tAngloSaxonCivs = { [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.CIVILIZATION_RUSSIA , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.CIVILIZATION_NETHERLANDS , [GameInfoTypes.UNIT_HUSCARL] = GameInfoTypes.CIVILIZATION_BABYLON }
local tAngloSaxonBuildingClasses = {}
local iPalaceBuilding = GameInfoTypes.BUILDING_PALACE

for k,v in pairs(tAngloSaxonUnits) do
	tAngloSaxonBuildingClasses[k] = GameInfoTypes[GameInfo.Buildings[v].BuildingClass]
end

--allows building of anglo-saxon units equal to the number of unique buildings plus one for the capital

function ASUnit(iPlayer, iCity, iUnit)
	--differentiate Anglo-Saxon units
	if tAngloSaxonUnits[iUnit] then
		print("Unit is " .. iUnit)
		-- determine if unit is Anglo-Saxon
		local player = Players[iPlayer]
		if tAngloSaxonCivs[iUnit] == player:GetCivilizationType() then
			print("Player # is " .. iPlayer .. " and the Player's Civilization ID# is " .. player:GetCivilizationType())
			-- check for Anglo-Saxon building or palace
			local city = player:GetCityByID(iCity)
			if city:IsHasBuilding(tAngloSaxonUnits[iUnit]) or city:IsHasBuilding(iPalaceBuilding) then
				print("City was seen as having either the required building for the unit or as having the Palace")
				local numUnits = 0	
				for unit in player:Units() do
					if unit:GetUnitType() == iUnit then				
						numUnits = numUnits + 1
					end
				end
				print("numUnits was calaculated as " .. numUnits .. " for a unit of type " .. iUnit .. " " .. GameInfo.Units[iUnit].Type)
				print("the calculated number of required " .. GameInfo.Buildings[tAngloSaxonUnits[iUnit]].Type .. " in the empire was " .. player:GetBuildingClassCount(tAngloSaxonBuildingClasses[iUnit]) .. " plus 1 for the palace for a total of " .. (player:GetBuildingClassCount(tAngloSaxonBuildingClasses[iUnit]) + 1))
				return ((player:GetBuildingClassCount(tAngloSaxonBuildingClasses[iUnit]) + 1) > numUnits)
			else		
				print("City was seen as NOT having either the required building for the unit or as having the Palace, so FALSE is returned")
				return false	--needed when the city does not have the palace or the building that allows the unit		
			end
		else
			print("Player # is " .. iPlayer .. " and the Player's Civilization ID# is " .. player:GetCivilizationType() .. " which is seen as not being in the required table of civilizations, so FALSE is returned")
			return false	--default needed for units that ARE in the table tAngloSaxonUnits but for civs that ARE NOT in the table tAngloSaxonCivs
		end
	else	
		return true	--default needed for units NOT in the table tAngloSaxonUnits
	end
end
GameEvents.CityCanTrain.Add(ASUnit)
  • Note that I left a bunch of print statements in the code for debugging and confirmation purposes, which you would not want to leave in the end product because printing to the lua.log really slows execution.
  • Note that all the assignments and basic info is stated above and before rather than within the code of function ASUnit(iPlayer, iCity, iUnit). This is on-purpse and more desirable because it means the basic data and table construction is occuring once and once only.
  • Also note the different placement of the line
    Code:
    local numUnits = 0
    This is necessary because otherwise when the loop through the player's units on the following line terminates, variable numUnits will terminate along with it.
    • This is good because the code within the loop will update the data in the variable and the variable with its updated data will still be 'remembered' after the termination of the loop:
      Code:
      local numUnits = 0
      for unit in player:Units() do
      	if unit:GetUnitType() == iUnit then
      		numUnits = numUnits + 1
      	end
      end
    • This is bad because at the termination of the loop the variable and its accumulated data will be 'forgotten':
      Code:
      for unit in player:Units() do
      	local numUnits = 0
      	if unit:GetUnitType() == iUnit then
      		numUnits = numUnits + 1
      	end
      end
The only thing that hasn't been dealt with in either code method is whether or not another city is in the process of 'training' (ie, producing) one of the units in question. That would be something that would require a bit more thought. It would also be good to know exactly how all these units are being defined in terms of unit-class they are being inserted into.

There are also methods that can be used to build all the info needed by the lua into one giant does-all lua-table, but I decided not to go that route because it would probably be too much of a muchness until you get more comfortable with table-creating and table-using in lua.
 
Thank-you for your great overveiw of things.

I suppose I should clarify the situation I am trying to reproduce. The buildings are in fact uniques for their respective civilizations. The units are not. Each has its own unit class... for example, UNITCLASS_CEORL (forgive me if the writing is not exact as I am on a phone and not at home). There is not a one-to-one relationship between units and civilizations... Fyrdman is similar to Spearman; Ceorl to Swordsman; Huscarl is Ceorl upgrade and equivalent to Longswordsman. They differ from mundane units in that they do not require support or maintenance. They are restricted in total numbers amongst the three. So, for example, Northumbria with three Burghcourts and a palace, could construct any combination of Fyrdman/Ceorl/Huscarl up to 4 total. This is to be considered as a benefit resulting from the unique building of each civ and will be reflected in the civpedia as such. A benefit of the UB is production of these special supportless units (Fyrd levies). I may later adjust the number of special units available up or down depending on balance. For example, instead of 1 to 1, the relationship could be 2 units to each building, or 2 allocated for palace as examples.

So that is my intent in this case... however I expect to use this code in other situations. Unitclass count may serve in most situations, however I also have a case in another situation where I wish to exclude construction of a particular unit while allowing construction of another unit though they both share the same unitclass... therefore making counting units based on id preferable in that case. The code will also prove useful in further cultural based feudal levies and units based upon possession of religious buildings.

I will continue in the next post...
 
Looking at the code, I am beginning to have an inkling about how much I do not know about tables.

At first glance of the buildings array, it appears a one to one correspondence is being set up between units and buildings for the tanglosaxonunits table, and between units and civs in the tanglsaxoncivs table (again, forgive the spelling... phones are cumbersome). I do not know enough about tables to be certain, and I suspect I am very wrong about that suposition. Also I suspect the items on either side of the = sign within the table is not one of equivalency but of relationship. I am guessing something like {redapple=greenorange, blueapple=pinkorange, etc.} which can be reshuffled as {ra=po,ba=go, etc.} and act as effectively the same table with = acting to compartmentalize boxes of apples and oranges. Yet I could be very wrong :).

Which leads me to wonder what would happen with less or more apples than oranges... so, { ...etc....yellowapple=0}? In my case, another cultural civ, the Vikings, will have two special units and four civilizations with their own unique buildings from which the Unique Building benefit will be derived. How do I match 2 different coloured apples to 4 different coloured oranges? Similarly, religious buildings (which I have set to be removed and be replaced depending upon religion) will allow every civ who has them to construct religious based units (with upgraded versions) in numbers dependent upon the numbers of religious buildings the civ has (although buildingclass can be used to count them in this case).

Anyhow, I will lack opportunity to test your code for a number of hours. I am extremely interested to see what it does.

I have am really curious as to mechanisms behind array/table construction. I think I have a lot of clunky, though functional, code that I can tighten up a bit.

I may have to have a "pimp my code" event later to see how it can be made more efficiently as I have a lot of iterative processes that I have repeated for individual civs that I am certain could be done by combining things in arrays/tables.

Thanks... I am going to go back and reread your comments more carefully...

PS you mentioned putting the locals outside of the function. I moved them inside because I have a superstitious dread that they will be picked up and used by other (unrelated) functions written further along in the file... I take it that this is not the case as these are local, not global.

Thanks.
 
I did some testing... the function as written only allowed me to build one type of unit. I guess there is a one to one relationship established between the two items on either side of the "=" symbol within the table.

I proceeded to change the table by allocating each unit to every other building and civilization in the two tables... I then became unable to construct any of the units in question.

Code:
local tAngloSaxonUnits = { [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.BUILDING_BURGH_COURT , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.BUILDING_BURH , [GameInfoTypes.UNIT_CRAIG_HUSCARL] = GameInfoTypes.BUILDING_MOOT_HILL , [GameInfoTypes.UNIT_CRAIG_HUSCARL] = GameInfoTypes.BUILDING_BURGH_COURT , [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.BUILDING_BURH , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.BUILDING_MOOT_HILL , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.BUILDING_BURGH_COURT , [GameInfoTypes.UNIT_CRAIG_HUSCARL] = GameInfoTypes.BUILDING_BURH , [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.BUILDING_MOOT_HILL }
local tAngloSaxonCivs = { [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.CIVILIZATION_RUSSIA , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.CIVILIZATION_ENGLAND , [GameInfoTypes.UNIT_CRAIG_HUSCARL] = GameInfoTypes.CIVILIZATION_CHINA , [GameInfoTypes.UNIT_CRAIG_HUSCARL] = GameInfoTypes.CIVILIZATION_RUSSIA , [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.CIVILIZATION_ENGLAND , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.CIVILIZATION_CHINA , [GameInfoTypes.UNIT_FYRDMAN] = GameInfoTypes.CIVILIZATION_RUSSIA , [GameInfoTypes.UNIT_CRAIG_HUSCARL] = GameInfoTypes.CIVILIZATION_ENGLAND , [GameInfoTypes.UNIT_CEORL] = GameInfoTypes.CIVILIZATION_CHINA }
local tAngloSaxonBuildingClasses = {}
local iPalaceBuilding = GameInfoTypes.BUILDING_PALACE

I am guessing that I will need to do a separate table for each civilization and building aligning them to each unit in turn. I will try that tonight and then try to get the rest of the function to reflect that change... might be about 7 hours or so until I get to it.

Thanks, as always, for your help.
 
This appears to do the trick... although it lacks elegance.


Code:
--allows building of anglo-saxon units equal to the number of unique buildings plus one for the capital

local iCeorl = GameInfoTypes.UNIT_CEORL
local iFyrdman = GameInfoTypes.UNIT_FYRDMAN
local iHuscarl =  GameInfoTypes.UNIT_CRAIG_HUSCARL
local iNorthumbria = GameInfoTypes.CIVILIZATION_RUSSIA
local iWessex = GameInfoTypes.CIVILIZATION_ENGLAND
local iMercia = GameInfoTypes.CIVILIZATION_CHINA
local iBurghcourt = GameInfoTypes.BUILDING_BURGH_COURT
local iBurh = GameInfoTypes.BUILDING_BURH
local iMoothill = GameInfoTypes.BUILDING_MOOT_HILL
local tAngloSaxonBuildingClasses = {}
local iPalaceBuilding = GameInfoTypes.BUILDING_PALACE

function ASUnit(iPlayer, iCity, iUnit)
	--differentiate Anglo-Saxon units
	if iUnit == iCeorl or iUnit == iFyrdman or iUnit == iHuscarl then
		print("Unit is " .. iUnit)
		-- determine if unit is Anglo-Saxon
		local player = Players[iPlayer]
		if player:GetCivilizationType() == iNorthumbria or player:GetCivilizationType() == iWessex or player:GetCivilizationType() == iMercia then
			print("Player # is " .. iPlayer .. " and the Player's Civilization ID# is " .. player:GetCivilizationType())
			-- check for Anglo-Saxon building or palace
			local city = player:GetCityByID(iCity)			
			if city:IsHasBuilding(iBurghcourt) or city:IsHasBuilding(iBurh) or city:IsHasBuilding(iMoothill) or city:IsHasBuilding(iPalaceBuilding) then
				print("City was seen as having either the required building for the unit or as having the Palace")
				local numUnits = 0	
				for unit in player:Units() do
					if unit:GetUnitType() == iCeorl or unit:GetUnitType() == iFyrdman or unit:GetUnitType() == iHuscarl then				
						numUnits = numUnits + 1
					end
				end
				print("numUnits was calaculated as... ", numUnits)

				local BuildingCount = 0
				for pCity in player:Cities() do			

					if pCity:IsHasBuilding(iBurghcourt) or pCity:IsHasBuilding(iBurh) or pCity:IsHasBuilding(iMoothill) then

					BuildingCount = BuildingCount + 1

					end
				end	

				print("BuildingCount was calaculated as... ", BuildingCount)
				--print("the calculated number of required " .. GameInfo.Buildings[tAngloSaxonUnits[iUnit]].Type .. " in the empire was " .. player:GetBuildingClassCount(tAngloSaxonBuildingClasses[iUnit]) .. " plus 1 for the palace for a total of " .. (player:GetBuildingClassCount(tAngloSaxonBuildingClasses[iUnit]) + 1))
				
				
				return (BuildingCount + 1) > numUnits
			else		
				print("City was seen as NOT having either the required building for the unit or as having the Palace, so FALSE is returned")
				return false	--needed when the city does not have the palace or the building that allows the unit		
			end
		else
			print("Player # is " .. iPlayer .. " and the Player's Civilization ID# is " .. player:GetCivilizationType() .. " which is seen as not being in the required table of civilizations, so FALSE is returned")
			return false	--default needed for units that ARE in the table tAngloSaxonUnits but for civs that ARE NOT in the table tAngloSaxonCivs
		end
	else	
		return true	--default needed for units NOT in the table tAngloSaxonUnits
	end
end

GameEvents.CityCanTrain.Add(ASUnit)

I had to replace your table data with brute force. Thankfully, it gave me a good structure to work through.

Thank-you.
 
I think you are struggling with basic understanding of how tables work in lua. This is not anything to be embarrassed over -- everything I've learned in that regard I've learned by intensive experimentation and very large dollops of advice and help from members of the forum, the very helpful whoward69 being at the top of the list.

If you go to the Lua Demo Page you can write short test programs and have the demo page run them for you. You can't use any of the stuff specific to CIV5 because outside of CIV5 lua has no earthly idea what pCity:IsHasBuilding(iX) means. But it can be very useful to learn how tables especially work.

Paste this code into the lua demo page as the code is, then hit the little "run' button on the page. Scroll down the page to see the results. Then change the code so that iAnimalNumber is anywhere between 1 and 4, and redo the "run" button on the demo page. Add more animal [number] = "text" pairs if you want and see the different outputs. Then as you make small changes compare the code that is being executed with the results given in the print-out.
Code:
iAnimalNumber = 5
tAnimals = {}
tAnimals[1] = "Mouse"
tAnimals[2] = "Cat"
tAnimals[3] = "Snake"
tAnimals[4] = "Alligator"

print("The contents of the table called tAnimals is as follows:")
for k,v in pairs(tAnimals) do print(k,v) end
print("....")
print("....")

if tAnimals[iAnimalNumber] then
	print("true : the number " .. iAnimalNumber .. " is in the table called Animals")
	print("the value associated with this item number in the table called Animals is: " .. tAnimals[iAnimalNumber])
else
	print("false: the number " .. iAnimalNumber .. " is NOT in the table called Animals")
end

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

This:
Code:
tAnimals = { [1] = "Mouse", [2] = "Cat", [3] = "Snake", [4] = "Alligator" }
is the same in lua as this:
Code:
tAnimals = {}
tAnimals[1] = "Mouse"
tAnimals[2] = "Cat"
tAnimals[3] = "Snake"
tAnimals[4] = "Alligator"
I just find it easier to read the second method sometimes.

Note that you can also do this and the end-result will be the same as the 1st example:
Code:
tAnimals = { "Mouse", "Cat", "Snake", "Alligator" }
 
I've been refining my code... I am using pPlayer:CanTrain and pPlayer:CountNumBuildings.

However, I am now getting an error from switching from a city loop method of counting to pPlayer:CountNumBuildings.

Here is the error from the log:

Code:
[502761.020] NewTraits: Player # is 0 and the Player's Civilization ID# is 49 which is seen as being the required civilization, so TRUE is returned
[502761.020] NewTraits: numUnits was calculated as... 	0
[502761.020] NewTraits: BuildingCount was calculated as... 	nil
[502761.036] Runtime Error: C:\Users\doms\Documents\My Games\Sid Meier's Civilization 5\MODS\The Viking Age for BNW (v 2)\LUA/NewTraits.lua:960: attempt to perform arithmetic on global 'BuildingCount' (a nil value)

Here is the code (excluding the PlayerCanTrain event which is working fine)

Code:
--allows building of Viking levy units equal to the number of unique buildings

local iLongship = GameInfoTypes.UNIT_LONGSHIP
local iFelagi = GameInfoTypes.UNIT_FELAGI
local iDenmark = GameInfoTypes.CIVILIZATION_DENMARK
local iNorway = GameInfoTypes.CIVILIZATION_INDIA
local iHladir = GameInfoTypes.CIVILIZATION_CARTHAGE
local iSweden = GameInfoTypes.CIVILIZATION_SWEDEN
local iTrelleborg = GameInfoTypes.BUILDING_TRELLEBORG
local iVik = GameInfoTypes.BUILDING_VIK
local iRunestone = GameInfoTypes.BUILDING_RUNESTONE
local iByrck = GameInfoTypes.BUILDING_BJORKEY

function VikUnit(iPlayer, iCity, iUnit)
	--differentiate Viking units
	if iUnit == iLongship or iUnit == iFelagi then
		
		-- determine if unit is Viking
		local player = Players[iPlayer]	
			
			-- check for Viking building
			local city = player:GetCityByID(iCity)			
			if city:IsHasBuilding(iTrelleborg) or city:IsHasBuilding(iVik) or city:IsHasBuilding(iRunestone) or city:IsHasBuilding(iByrck) then
				
				-- check number of Viking units
				local numUnits = 0	
				for unit in player:Units() do
					if unit:GetUnitType() == iLongship or unit:GetUnitType() == iFelagi then				
						numUnits = numUnits + 1
					end
				end
				print("numUnits was calculated as... ", numUnits)

				-- find number of buildings of applicable type
				Buildingcount = player:CountNumBuildings(iTrelleborg) + player:CountNumBuildings(iVik) + player:CountNumBuildings(iRunestone) + player:CountNumBuildings(iByrck)

				print("BuildingCount was calculated as... ", BuildingCount)
				
				[COLOR="Red"]return math.ceil(BuildingCount/2) > numUnits[/COLOR] --compare fraction of building count to number of units... return TRUE if Buildingcount/X > numUnits
			else		
				print("City was seen as NOT having the required number and type of buildings for the unit, so FALSE is returned")
				return false	--needed when the city does not have a building that allows the unit		
			end
		
	else	
		return true	--default needed for units NOT in Viking Units
	end
end

GameEvents.CityCanTrain.Add(VikUnit)

The red coloured text is the error portion; however, from the log, you can also see that Buildingcount = nil whereas it should be a number. I'm puzzled because player:CountNumBuildings(iRunestone) is supposed to be an integer value and I cannot see how Buildingcount would not be.

I have tried making Buildingcount a local and also moved it higher in the code (below where player = Players(iPlayer) is defined). But nothing seems to work.

It seems likely that the various iBuildings being counted should not be provided as types but in some other form, but I am not certain how to derive those.

Sorry to be a bother. I am hoping it is a relatively simple newbie error.

Thanks in advance.
 
I am hoping it is a relatively simple newbie error.

Code:
Building[B][COLOR="Red"]c[/COLOR][/B]ount = player:CountNumBuildings(iTrelleborg) + ...

print("BuildingCount was calculated as... ", Building[B][COLOR="Red"]C[/COLOR][/B]ount)
 
Back
Top Bottom