Repeat lua?

DJSHenninger

Megas Basileus
Joined
Mar 8, 2013
Messages
949
Location
The Netherlands
So, for a trait of a certain civilization, I've got some lua code.. The trait is: Worked Farms and Plantations automatically contribute Production towards National Wonders. Basically, the code randomly chooses a National Wonder and adds Production, but only if that National Wonder can be built. Regardless, if it chooses a National Wonder that can't be built, no Production is added and the function ends. What I'd like is some kind of way to 're-fire' the lua, or the random bit, if the code chose a National Wonder that can't be built (yet), until it chooses a Wonder that can be built. So, for example, if the National Epic is the only buildable Wonder, all worked Farms/Plantations add Production to the National Epic that turn. Any idea how to do this.. easily? Because I'm not that familiar with lua and the terminology and all that.. :lol:

CODE:

Code:
function DJSH_KhmerAutoProdNationalWonders(PlayerID)
	local player = Players[PlayerID]
	if (player:GetCivilizationType() == GameInfoTypes["CIVILIZATION_DJSH_KHMER"] and player:IsAlive()) then
		for city in player:Cities() do
			for cityPlot = 0, city:GetNumCityPlots() - 1, 1 do
				local plot = city:GetCityIndexPlot(cityPlot)
				if plot ~= nil then
					if plot:GetOwner() == city:GetOwner() then
						if (plot:GetImprovementType() == GameInfoTypes["IMPROVEMENT_FARM"] or plot:GetImprovementType() == GameInfoTypes["IMPROVEMENT_PLANTATION"]) then
							if city:IsWorkingPlot(plot) then
								local random = Map.Rand(11, "Random National Wonder Bonuses Lua")
								if random == 0 then
									if city:CanConstruct(GameInfoTypes["BUILDING_NATIONAL_EPIC"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_NATIONAL_EPIC"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_NATIONAL_EPIC"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards National Epic!", iProdValue))
										end
									end
								end
								if random == 1 then
									if city:CanConstruct(GameInfoTypes["BUILDING_NATIONAL_COLLEGE"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_NATIONAL_COLLEGE"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_NATIONAL_COLLEGE"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards National College!", iProdValue))
										end
									end
								end
								if random == 2 then
									if city:CanConstruct(GameInfoTypes["BUILDING_NATIONAL_TREASURY"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_NATIONAL_TREASURY"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_NATIONAL_TREASURY"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards East India Company!", iProdValue))
										end
									end
								end
								if random == 3 then
									if city:CanConstruct(GameInfoTypes["BUILDING_HEROIC_EPIC"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_HEROIC_EPIC"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_HEROIC_EPIC"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards Heroic Epic!", iProdValue))
										end
									end
								end
								if random == 4 then
									if city:CanConstruct(GameInfoTypes["BUILDING_CIRCUS_MAXIMUS"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_CIRCUS_MAXIMUS"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_CIRCUS_MAXIMUS"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards Circus Maximus!", iProdValue))
										end
									end
								end
								if random == 5 then
									if city:CanConstruct(GameInfoTypes["BUILDING_GRAND_TEMPLE"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_GRAND_TEMPLE"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_GRAND_TEMPLE"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards Grand Temple!", iProdValue))
										end
									end
								end
								if random == 6 then
									if city:CanConstruct(GameInfoTypes["BUILDING_IRONWORKS"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_IRONWORKS"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_IRONWORKS"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards Ironworks!", iProdValue))
										end
									end
								end
								if random == 7 then
									if city:CanConstruct(GameInfoTypes["BUILDING_HERMITAGE"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_HERMITAGE"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_HERMITAGE"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards Hermitage!", iProdValue))
										end
									end
								end
								if random == 8 then
									if city:CanConstruct(GameInfoTypes["BUILDING_OXFORD_UNIVERSITY"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_OXFORD_UNIVERSITY"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_OXFORD_UNIVERSITY"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards Oxford University!", iProdValue))
										end
									end
								end
								if random == 9 then
									if city:CanConstruct(GameInfoTypes["BUILDING_TOURIST_CENTER"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_TOURIST_CENTER"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_TOURIST_CENTER"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards National Visitor Center!", iProdValue))
										end
									end
								end
								if random == 10 then
									if city:CanConstruct(GameInfoTypes["BUILDING_INTELLIGENCE_AGENCY"]) then
										local iProdValue = math.floor(city:GetBuildingProductionNeeded(GameInfoTypes["BUILDING_INTELLIGENCE_AGENCY"]) / 50)
										city:ChangeBuildingProduction(GameInfoTypes["BUILDING_INTELLIGENCE_AGENCY"], -iProdValue)
										if iProdValue > 0 then
											Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY()))), Locale.ConvertTextKey("+{1_Num} [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards National Intelligence Agency!", iProdValue))
										end
									end
								end
							end
						end
					end
				end
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(DJSH_KhmerAutoProdNationalWonders)
 
You can probably make use of a while loop.

A 'while' loop is similar to the 'for' loop that is often used for iterating. It executes the code inside the loop repeatedly until its condition changes.

In your case, you could set a marker that will be changed once your code actually successfully changes something valid.

Code:
local bProductionAdded = false
while not bProductionAdded do
  -- your conditions here
  if someCondition == true then
    -- do stuff
    bProductionAdded = true
  end
end

You just need to be careful of infinite loops when you use them; in this case, if 'someCondition' never returns true, it will execute this code over and over, because the while condition is never toggled to stop execution.
 
That Events.AddPopupTextEvent...

Code:
local IsValidImpro = {}
IsValidImpro[GameInfoTypes["IMPROVEMENT_FARM"]] = "muchvalid";
IsValidImpro[GameInfoTypes["IMPROVEMENT_PLANTATION"]] = "morevalid";
local PreTable = {}

for row in GameInfo.BuildingClasses() do
	if row.MaxPlayerInstances == 1 then
		PreTable[GameInfo.Buildings[row.DefaultBuilding].ID] = Locale.Lookup(GameInfo.Buildings[row.DefaultBuilding].Description;
	end
end
PreTable[GameInfoTypes.BUILDINGCLASS_WRITERS_GUILD] = nil;
--Note: Do the same for other guilds and Palace (Technically palace should never be able to be build)

function DJSH_KhmerAutoProdNationalWonders(PlayerID)
	local player = Players[PlayerID]
	if (player:GetCivilizationType() == GameInfoTypes["CIVILIZATION_DJSH_KHMER"] and player:IsAlive()) then
		for city in player:Cities() do
			local hTable = {}
			for iType, int in pairs(PreTable) do
				if city:CanConstruct(iType)  then
					--Note: I assume CanConstruct works correctly
					table.insert(hTable, {Type = iType, Point = 0})
				end
			end
			if #hTable > 0 then
				local tBonus = 0;
				for cityPlot = 0, city:GetNumCityPlots() - 1, 1 do
					local plot = city:GetCityIndexPlot(cityPlot)
					if plot ~= nil then
						if plot:GetOwner() == PlayerID then
							if (IsValidImpro[plot:GetImprovementType()] ) then
								if city:IsWorkingPlot(plot) then
									tBonus = tBonus + 1;
								end
							end
						end
					end
				end
				for i = 1, tBonus do
					local cR = math.random(1, #hTable);
					hTable[cR].Point = hTable[cR].Point + 1;
				end
				local delay = 0;
				for int, pData in pairs(hTable) do
					local iProdValue = pData.Point * math.floor(city:GetBuildingProductionNeeded(pData.Type) / 50)
					if iProdValue > 0 then
						city:ChangeBuildingProduction(pData.Type, -iProdValue)
						--Note: I bet it should be without "-", just iProdValue
						if Game.GetActivePlayer() == PlayerID then
							Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(city:GetX(), city:GetY()))), "+" .. iProdValue .. [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards " .. PreTable[pData.Type] .. "!", iProdValue, delay)
							delay = delay + 0.5;
						end
					end
				end
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(DJSH_KhmerAutoProdNationalWonders)

You should handle it on your own.
 
So far I have not seen any incorrect performances while using pCity:CanConstruct(iBuilding). If the city already has the building or cannot otherwise build it because of terrain restrictions or what-have-you it has always so far returned 'false'.
 
You can probably make use of a while loop.

A 'while' loop is similar to the 'for' loop that is often used for iterating. It executes the code inside the loop repeatedly until its condition changes.

In your case, you could set a marker that will be changed once your code actually successfully changes something valid.

Code:
local bProductionAdded = false
while not bProductionAdded do
  -- your conditions here
  if someCondition == true then
    -- do stuff
    bProductionAdded = true
  end
end

You just need to be careful of infinite loops when you use them; in this case, if 'someCondition' never returns true, it will execute this code over and over, because the while condition is never toggled to stop execution.

Yeah, that was my concern as well, it would probably go on forever :p

That Events.AddPopupTextEvent...

Code:
local IsValidImpro = {}
IsValidImpro[GameInfoTypes["IMPROVEMENT_FARM"]] = "muchvalid";
IsValidImpro[GameInfoTypes["IMPROVEMENT_PLANTATION"]] = "morevalid";
local PreTable = {}

for row in GameInfo.BuildingClasses() do
	if row.MaxPlayerInstances == 1 then
		PreTable[GameInfo.Buildings[row.DefaultBuilding].ID] = Locale.Lookup(GameInfo.Buildings[row.DefaultBuilding].Description;
	end
end
PreTable[GameInfoTypes.BUILDINGCLASS_WRITERS_GUILD] = nil;
--Note: Do the same for other guilds and Palace (Technically palace should never be able to be build)

function DJSH_KhmerAutoProdNationalWonders(PlayerID)
	local player = Players[PlayerID]
	if (player:GetCivilizationType() == GameInfoTypes["CIVILIZATION_DJSH_KHMER"] and player:IsAlive()) then
		for city in player:Cities() do
			local hTable = {}
			for iType, int in pairs(PreTable) do
				if city:CanConstruct(iType)  then
					--Note: I assume CanConstruct works correctly
					table.insert(hTable, {Type = iType, Point = 0})
				end
			end
			if #hTable > 0 then
				local tBonus = 0;
				for cityPlot = 0, city:GetNumCityPlots() - 1, 1 do
					local plot = city:GetCityIndexPlot(cityPlot)
					if plot ~= nil then
						if plot:GetOwner() == PlayerID then
							if (IsValidImpro[plot:GetImprovementType()] ) then
								if city:IsWorkingPlot(plot) then
									tBonus = tBonus + 1;
								end
							end
						end
					end
				end
				for i = 1, tBonus do
					local cR = math.random(1, #hTable);
					hTable[cR].Point = hTable[cR].Point + 1;
				end
				local delay = 0;
				for int, pData in pairs(hTable) do
					local iProdValue = pData.Point * math.floor(city:GetBuildingProductionNeeded(pData.Type) / 50)
					if iProdValue > 0 then
						city:ChangeBuildingProduction(pData.Type, -iProdValue)
						--Note: I bet it should be without "-", just iProdValue
						if Game.GetActivePlayer() == PlayerID then
							Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(city:GetX(), city:GetY()))), "+" .. iProdValue .. [COLOR_POSITIVE_TEXT][ICON_PRODUCTION]Production[ENDCOLOR] towards " .. PreTable[pData.Type] .. "!", iProdValue, delay)
							delay = delay + 0.5;
						end
					end
				end
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(DJSH_KhmerAutoProdNationalWonders)

You should handle it on your own.

Thanks a lot, will test that! As for the Events.AddPopupTextEvent, I was hoping it wouldn't cause that much of a lagg.. xD Will have to see and find out ;)
EDIT: turns out it does need the "", otherwise the SDK won't treat it as a function.
 
It needs also ) ending to Locale.Lookup at the top, but I was editing this in Notepad.

And it should be:
Code:
PreTable[GameInfoTypes.BUILDING_WRITERS_GUILD] = nil;
instead class.
 
It needs also ) ending to Locale.Lookup at the top, but I was editing this in Notepad.

And it should be:
Code:
PreTable[GameInfoTypes.BUILDING_WRITERS_GUILD] = nil;
instead class.

Ah, yeah, I noticed the missing ).

Thanks, will use the updated code for the Palace :) Decided to include the guilds anyway.
 
Interesting idea; I've never used the City:ChangeBuildingProduction() method.

Too bad your code won't support modded National Wonders. You could cache all the NWs from the DB on game load, and just iterate through that cache instead of hard-coding them. NWs are generally those with a BuildingClass where MaxPlayerInstances = 1.

On while loops it's often a good idea to add in a delimiter that will guarantee the code will break after a reasonable number of iterations.

Code:
local i = 100
while ThisValueIsTrue and (i > 0) do
    -- do logic here
    i = i - 1
end
 
Glad I reread your message, I was literally going to post the exact same idea as a response as though I was the first to think of it. That heuristic holds true for the base game, though it is possible that a modded NW might break from that convention. Anyway, that's what I would do if I were OP.

Too bad your code won't support modded National Wonders. You could cache all the NWs from the DB on game load, and just iterate through that cache instead of hard-coding them. NWs are generally those with a BuildingClass where MaxPlayerInstances = 1.
 
Top Bottom