Lua request: Choose free great person upon entering a new era.

Hiram

XML Plebian
Joined
Dec 14, 2006
Messages
1,088
Location
Where The Streets Have No Name
I'm in the process of updating Celtic Civilizations and Ireland is in sore need of a buff to its UA. I was wondering if anyone would be able to put together some lua that allows Ireland to choose a free great person upon entering a new era? It would be pretty simple I hope but I'm clueless when it comes to Lua.
 
I'm assuming you want a free choice as opposed to the limits on the choice like you get with the Maya(*) Baktun thing ?

I think giving and then taking away (via lua) a building that has <FreeGreatPeople>1</FreeGreatPeople> in it will work. This should also create a notification for the player to choose a great person.

What I am not 100% sure of is the "giving the building with lua and then immediately removing the building again" part. Not sure if that will cancel back out the free great person choice.

Unless someone else has a better solution I could whomp up the code (lua and building) but I won't have a chance to do so until tomorrow morning my time (USA).

(*) I think it's the Maya.
 
What I am not 100% sure of is the "giving the building with lua and then immediately removing the building again" part. Not sure if that will cancel back out the free great person choice.

Maybe a dumb question: If the building is free/invisible/unbuildable, and if it's set to NoLimit, is there a reason to take it away again? Once the free great person is granted, wouldn't it just sit there, doing nothing?
 
Maybe a dumb question: If the building is free/invisible/unbuildable, and if it's set to NoLimit, is there a reason to take it away again? Once the free great person is granted, wouldn't it just sit there, doing nothing?
The only reason I was thinking give and then take away is that some building effects the game will only add for the 1st building within the class that is added to any one city. Multiple buildings present within the same city, even when <NoLimit> is stated for the building class, do not create additive effects for certain things: Tourism is the basic example of this. I am not sure whether <FreeGreatPeople> shares this limitation or not.

bouncymischa,
The only reason I would favor a building as opposed to the policy that would give a free great person is that I think in terms of buildings much easier than I do in terms of policies.

[edit]anyone else have a more elegant solution, or have code they know would work? That is, before I make something based on the building concept?
 
What I am not 100% sure of is the "giving the building with lua and then immediately removing the building again" part. Not sure if that will cancel back out the free great person choice.
Use lua to detect when the player changes era, which there's probably some kind of work-around to detect like firing when the player acquires a tech or something. If the player changes era, add a building. Make a separate function that fires on PlayerDoTurn and which, if it detects the building in the capital, removes it.

So basically, remove the building a turn after. That should solve it.
 
Well, there was no issue with adding and then taking away the building.

I tried to use an lua 'event' called 'Events.SerialEventEraChanged' but that gave me seriously erratic behavior and I kept getting errors I couldn't figure an easy way around.

I ended up using 'GameEvents.PlayerDoTurn'.

The problem here is that the player has to progress to the next turn before the lua fires and gives the free great person. If a player saves their game before doing turn processing, the likely result is the player will not get the free great person when they re-load the saved game, and then do 'NEXT TURN'. It may be necessary to persist the data for the era using SaveUtils.lua or the newer version called NewSaveUtils.lua (I think) that JFD had a link to some time back. I'd have to think through that a bit more to figure out when/how is best to save the info.

New method shown below cures the troubles with data persistence. The player still has to progress to the next turn, but the building acts as its own counter (data storage bit) for whether the new era has been processed or not. As a side note to calcul8or, <FreeGreatPeople> does not suffer the limitation that applies to Tourism. Multiple copies of the same building continue to give the free great person choice. I verified before changing the info in this post.

Hiram will still need to get back as to whether the mod is for BNW only, but I don't think it will make much difference in terms of lua code. The difference is that for BNW, the <!-- <GreatWorkCount>-1</GreatWorkCount> --> in the XML can be activated by removing the comments wraps.

Anyway, here is the lua, which needs to be an InGameUIAddin:
Spoiler :
You'll need to change CIVILIZATION_AMERICA to the correct civilization name for your mod.
Code:
-- FreeGreatPerson_per_Era
-- Author: Lee
-- DateCreated: 10/16/2014 4:02:24 PM
--------------------------------------------------------------
gBuildingFreeGreatPerson = GameInfoTypes.BUILDING_FREE_GREAT_PERSON
gRequiredCivilization = GameInfoTypes.CIVILIZATION_AMERICA

--------------------------------------------------------------
-- check for a change in era during PlayerDoTurn
--------------------------------------------------------------
function PlayerEraChanged(iPlayer)
	local pPlayer = Players[iPlayer]
	if not pPlayer:GetCivilizationType() == gRequiredCivilization then return end
	if not pPlayer:IsAlive() then return end
	local iNewPlayerEra = pPlayer:GetCurrentEra()
	--print("The value for player era was " .. tostring(iNewPlayerEra))
	local iNumBuildingFreeGreatPerson = 0
	local pCapitalCity = pPlayer:GetCapitalCity()
	if pCapitalCity:IsHasBuilding(gBuildingFreeGreatPerson) then
		iNumBuildingFreeGreatPerson = pCapitalCity:GetNumRealBuilding(gBuildingFreeGreatPerson)
		--print("The number of gBuildingFreeGreatPerson buildings was " .. tostring(iNumBuildingFreeGreatPerson))
	end
	if iNewPlayerEra > iNumBuildingFreeGreatPerson then
		--print("The era value was found to be greater than the number of Free Great Person buildings")
		pCapitalCity:SetNumRealBuilding(gBuildingFreeGreatPerson, iNewPlayerEra)
		--print("The number of gBuildingFreeGreatPerson buildings was changed from " .. tostring(iNumBuildingFreeGreatPerson) .. " to " .. tostring(iNewPlayerEra))
	end
end
GameEvents.PlayerDoTurn.Add(PlayerEraChanged);
And here is the Building, which just needs OnModActivated like any other XML. The code should work as-is for both G&K and BNW, since the <GreatWorkCount> line is commented out. But the building will not work for Vanilla because of the <FaithCost> line.
Spoiler :
Code:
<GameData>
	<Buildings>
		<Row>
			<Type>BUILDING_FREE_GREAT_PERSON</Type>
			<BuildingClass>BUILDINGCLASS_FREE_GREAT_PERSON</BuildingClass>
			<Cost>-1</Cost>
			<FaithCost>-1</FaithCost>
			<PrereqTech>NULL</PrereqTech>
			[COLOR="Blue"]<!--	<GreatWorkCount>-1</GreatWorkCount>	-->[/COLOR]
			<ArtDefineTag>NONE</ArtDefineTag>
			<MinAreaSize>-1</MinAreaSize>
			<NeverCapture>true</NeverCapture>
			<HurryCostModifier>-1</HurryCostModifier>
			<IconAtlas>BW_ATLAS_1</IconAtlas>
			<PortraitIndex>19</PortraitIndex>
			<Description>TXT_KEY_FREE_GREAT_PERSON</Description>
			<NukeImmune>true</NukeImmune>
			<FreeGreatPeople>1</FreeGreatPeople>
		</Row>
	</Buildings>

	<BuildingClasses>
		<Row>
			<Type>BUILDINGCLASS_FREE_GREAT_PERSON</Type>
			<DefaultBuilding>BUILDING_FREE_GREAT_PERSON</DefaultBuilding>
			<Description>TXT_KEY_FREE_GREAT_PERSON</Description>
			<NoLimit>true</NoLimit>
		</Row>
	</BuildingClasses>

	<Language_en_US>
		<Row Tag="TXT_KEY_FREE_GREAT_PERSON">
			<Text>Free Great Person</Text>
		</Row>
	</Language_en_US>
</GameData>
 
Use lua to detect when the player changes era...
I tried to use an lua 'event' called 'Events.SerialEventEraChanged' but that gave me seriously erratic behavior and I kept getting errors I couldn't figure an easy way around.
I've used SerialEventEraChanged and despite being an Event it does work 99% of the time. It fires for human and AI players. The 1% where it doesn't work is when a player goes up an era in the middle of a turn (usually due to a Great Scientist bulbing) it won't trigger until the start of the next turn. It seems to be tied to when the art-style for their cities change.

If you want a way to detect when a player changes you can use "TeamTechResearched(TeamID team, TechType tech, int change)". Whenever a player discovers a tech of era 3 and TeamObj:GetCurrentEra() > 3 than you know they didn't enter a new era. If the current era matches than for loop over all techs of era 3. If you find no other techs of era 3 that they know then you know this was the first tech of era 3 that they got so fire off an era changed.
 
I've used SerialEventEraChanged and despite being an Event it does work 99% of the time. It fires for human and AI players. The 1% where it doesn't work is when a player goes up an era in the middle of a turn (usually due to a Great Scientist bulbing) it won't trigger until the start of the next turn. It seems to be tied to when the art-style for their cities change.

If you want a way to detect when a player changes you can use "TeamTechResearched(TeamID team, TechType tech, int change)". Whenever a player discovers a tech of era 3 and TeamObj:GetCurrentEra() > 3 than you know they didn't enter a new era. If the current era matches than for loop over all techs of era 3. If you find no other techs of era 3 that they know then you know this was the first tech of era 3 that they got so fire off an era changed.
I am sure I was doing something bone-headed, since I kept getting those darn 'attempts to index a field : a nil value' errors.
 
Well, there was no issue with adding and then taking away the building.

I tried to use an lua 'event' called 'Events.SerialEventEraChanged' but that gave me seriously erratic behavior and I kept getting errors I couldn't figure an easy way around.

I ended up using 'GameEvents.PlayerDoTurn'.

The problem here is that the player has to progress to the next turn before the lua fires and gives the free great person. If a player saves their game before doing turn processing, the likely result is the player will not get the free great person when they re-load the saved game, and then do 'NEXT TURN'. It may be necessary to persist the data for the era using SaveUtils.lua or the newer version called NewSaveUtils.lua (I think) that JFD had a link to some time back. I'd have to think through that a bit more to figure out when/how is best to save the info.

New method shown below cures the troubles with data persistence. The player still has to progress to the next turn, but the building acts as its own counter (data storage bit) for whether the new era has been processed or not. As a side note to calcul8or, <FreeGreatPeople> does not suffer the limitation that applies to Tourism. Multiple copies of the same building continue to give the free great person choice. I verified before changing the info in this post.

Hirem will still need to get back as to whether the mod is for BNW only, but I don't think it will make much difference in terms of lua code. The difference is that for BNW, the <!-- <GreatWorkCount>-1</GreatWorkCount> --> in the XML can be activated by removing the comments wraps.

Anyway, here is the lua, which needs to be an InGameUIAddin:
Spoiler :
You'll need to change CIVILIZATION_AMERICA to the correct civilization name for your mod.
Code:
-- FreeGreatPerson_per_Era
-- Author: Lee
-- DateCreated: 10/16/2014 4:02:24 PM
--------------------------------------------------------------
gBuildingFreeGreatPerson = GameInfoTypes.BUILDING_FREE_GREAT_PERSON
gRequiredCivilization = GameInfoTypes.CIVILIZATION_AMERICA

--------------------------------------------------------------
-- check for a change in era during PlayerDoTurn
--------------------------------------------------------------
function PlayerEraChanged(iPlayer)
	local pPlayer = Players[iPlayer]
	if not pPlayer:GetCivilizationType() == gRequiredCivilization then return end
	if not pPlayer:IsAlive() then return end
	local iNewPlayerEra = pPlayer:GetCurrentEra()
	--print("The value for player era was " .. tostring(iNewPlayerEra))
	local iNumBuildingFreeGreatPerson = 0
	local pCapitalCity = pPlayer:GetCapitalCity()
	if pCapitalCity:IsHasBuilding(gBuildingFreeGreatPerson) then
		iNumBuildingFreeGreatPerson = pCapitalCity:GetNumRealBuilding(gBuildingFreeGreatPerson)
		--print("The number of gBuildingFreeGreatPerson buildings was " .. tostring(iNumBuildingFreeGreatPerson))
	end
	if iNewPlayerEra > iNumBuildingFreeGreatPerson then
		--print("The era value was found to be greater than the number of Free Great Person buildings")
		pCapitalCity:SetNumRealBuilding(gBuildingFreeGreatPerson, iNewPlayerEra)
		--print("The number of gBuildingFreeGreatPerson buildings was changed from " .. tostring(iNumBuildingFreeGreatPerson) .. " to " .. tostring(iNewPlayerEra))
	end
end
GameEvents.PlayerDoTurn.Add(PlayerEraChanged);
And here is the Building, which just needs OnModActivated like any other XML. The code should work as-is for both G&K and BNW, since the <GreatWorkCount> line is commented out. But the building will not work for Vanilla because of the <FaithCost> line.
Spoiler :
Code:
<GameData>
	<Buildings>
		<Row>
			<Type>BUILDING_FREE_GREAT_PERSON</Type>
			<BuildingClass>BUILDINGCLASS_FREE_GREAT_PERSON</BuildingClass>
			<Cost>-1</Cost>
			<FaithCost>-1</FaithCost>
			<PrereqTech>NULL</PrereqTech>
			[COLOR="Blue"]<!--	<GreatWorkCount>-1</GreatWorkCount>	-->[/COLOR]
			<ArtDefineTag>NONE</ArtDefineTag>
			<MinAreaSize>-1</MinAreaSize>
			<NeverCapture>true</NeverCapture>
			<HurryCostModifier>-1</HurryCostModifier>
			<IconAtlas>BW_ATLAS_1</IconAtlas>
			<PortraitIndex>19</PortraitIndex>
			<Description>TXT_KEY_FREE_GREAT_PERSON</Description>
			<NukeImmune>true</NukeImmune>
			<FreeGreatPeople>1</FreeGreatPeople>
		</Row>
	</Buildings>

	<BuildingClasses>
		<Row>
			<Type>BUILDINGCLASS_FREE_GREAT_PERSON</Type>
			<DefaultBuilding>BUILDING_FREE_GREAT_PERSON</DefaultBuilding>
			<Description>TXT_KEY_FREE_GREAT_PERSON</Description>
			<NoLimit>true</NoLimit>
		</Row>
	</BuildingClasses>

	<Language_en_US>
		<Row Tag="TXT_KEY_FREE_GREAT_PERSON">
			<Text>Free Great Person</Text>
		</Row>
	</Language_en_US>
</GameData>

Follow-up Question for Hirem:
Is your mod to be compatible to all expansions or is it to be BNW-only? I have a work-around in mind that won't require saving data between save games and re-loading of saved games, but I would need to know whether you are planning for BNW-only.

Thanks heaps! It'll be BNW only (the mod contains other civs which use BNW features).
 
Thanks heaps! It'll be BNW only (the mod contains other civs which use BNW features).
Then you can remove the comment wraps from that <GreatWorkCount> line and everything will be all mysterious modder trickery so far as the user is concerned.
 
I think there is special thread/subforum for Lua requests.

Building method not consider Capital lost/recapture.

Tech logic should consider that if player discover all techs from certain era he enters next era as well.

Anyway, to attach SaveUtils, just copy the file from other mod (you can Ctrl-F Mods folder, I bet at least one of your mod will have it) and "VFS = true".
I think checking era every turn is enough (if you truely want it trigger instantly, I can dig my old version of Scotland).

Code:
WARN_NOT_SHARED = false; include( "SaveUtils" ); MY_MOD_NAME = "HirIreGPpE";
local HirCivIre = GameInfoTypes.[COLOR="Red"]YOUR_CIV[/COLOR];

GameEvents.PlayerDoTurn.Add(function(iPlayer)
	if Players[iPlayer]:GetCivilizationType() == HirCivIre then
		pPlayer = Players[iPlayer];
		local pEra = load(pPlayer, "HirIrePE") or 0;
		local cEra = pPlayer:GetCurrentEra();
		if cEra > pEra then
			save(pPlayer, "HirIrePE", cEra)
			pPlayer:ChangeNumFreeGreatPeople(cEra - pEra)
		end
	end
end)
I think Poland gets extra Policies even if starting in further eras, therefore this code acts in the same way.
Iirc it will be impossible to choose Prophet so you don't have to worry about religion found once player reach Classical era.
 
I would use LastSword's method. It's cleaner than mine.

I ran a test on his method. Prophets do not show in the choice when progressing to the Classical Era, but they do show in the choice when progressing to the Medieval Era.

I attached my test mod. It has the SaveUtils file included within it. It's a zip of the built version of the mod.
 

Attachments

  • Era_Change_Effects (v 4).zip
    7.6 KB · Views: 50
Top Bottom