Unhappiness manipulation

PawelS

Ancient Druid
Joined
Dec 11, 2003
Messages
2,811
Location
Poland
What's the best way to add an arbitrary amount of unhappiness using Lua?

For my mod's happiness system I need to have non-linear (progressive) unhappiness from number of cities. So instead of simply using the UNHAPPINESS_PER_CITY define I need a way to set the unhappiness every turn for every player depending on the number of cities.
 
I thought about this for a civ I was building (might still want to incorporate it, never finished the civ). The method I planned to attempt was to use the XML tag for "unhappiness from the number of cities" to make that number very high, then add back happiness buildings through lua, depending on the number of cities. Didn't get far on the lua script,though.
 
I'd prefer to manipulate unhappiness directly through Lua, using functions like Player.SetHappiness or Player.ChangeExtraHappinessPerCity. But I'm not sure how to use them, so I need help here.

Adding happiness to limited numbers of cities through buildings can be done even without using Lua. To do it, you need buildings with FreeStartEra = ERA_ANCIENT, and limited MaxPlayerInstances.

For example:

Unhappiness per city is 20.
Building 1 gives +10 happiness, and has MaxPlayerInstances 12.
Building 2 gives +5 happiness, and has MaxPlayerInstances 8.
Building 3 gives +5 happiness, and has MaxPlayerInstances 4.

So cities 1-4 are "free" (cost 0 happiness), cities 5-8 cost 5 happiness, cities 9-12 cost 10 happiness, and cities 13+ cost 20 happiness.

This is the alternative solution I'm going to use if using Lua to change happiness directly doesn't turn out to be a good option. The problem with this method is that I'm not sure if these buildings are placed in captured cities, I need to test it. Also I'd prefer to adjust unhappiness more smoothly, which would require a large number of buildings in this case.
 
I think I have found a good solution: I created similar buildings as described in the previous post, but belonging to a MutuallyExclusiveGroup (if anyone wants to use this idea, make sure you, or any mod you use, doesn't put any other buildings in the same group). It allows setting happiness individually for every city, and only one such building will appear per city. Here is the code I use:

Code:
	<!-- HAPPINESS BONUS (CITY 1) -->
	<Language_en_US>
		<Row Tag="TXT_KEY_COABLDG_HAPPINESS_BONUS_1">
			<Text>Happiness Bonus (city #1) </Text>
		</Row>
	</Language_en_US>
	<BuildingClasses>
		<Row>
			<Type>BUILDINGCLASS_HAPPINESS_BONUS_1</Type>
			<DefaultBuilding>BUILDING_HAPPINESS_BONUS_1</DefaultBuilding>
			<Description>TXT_KEY_COABLDG_HAPPINESS_BONUS_1</Description>
			<MaxPlayerInstances>1</MaxPlayerInstances>
			<NoLimit>true</NoLimit>
		</Row>
	</BuildingClasses>
	<Buildings>
		<Row>
			<BuildingClass>BUILDINGCLASS_HAPPINESS_BONUS_1</BuildingClass>
			<Type>BUILDING_HAPPINESS_BONUS_1</Type>
			<Description>TXT_KEY_COABLDG_HAPPINESS_BONUS_1</Description>
			<Civilopedia>TXT_KEY_COA_EMPTY</Civilopedia>
			<Strategy>TXT_KEY_COA_EMPTY</Strategy>
			<Help>TXT_KEY_COA_EMPTY</Help>
			<Cost>0</Cost>
			<FaithCost>0</FaithCost>
			<GoldMaintenance>0</GoldMaintenance>
			<PrereqTech>NULL</PrereqTech>
			<HurryCostModifier>-1</HurryCostModifier>
			<MinAreaSize>-1</MinAreaSize>
			<NeverCapture>true</NeverCapture>
			<ConquestProb>0</ConquestProb>
			<IconAtlas>EXPANSION_BW_ATLAS_2</IconAtlas>
			<PortraitIndex>13</PortraitIndex>
			<FreeStartEra>ERA_ANCIENT</FreeStartEra>
			<UnmoddedHappiness>20</UnmoddedHappiness>
			<MutuallyExclusiveGroup>11</MutuallyExclusiveGroup>
		</Row>
	</Buildings>
	<Building_Flavors>
		<Row>
			<BuildingType>BUILDING_HAPPINESS_BONUS_1</BuildingType>
			<FlavorType>FLAVOR_HAPPINESS</FlavorType>
			<Flavor>20</Flavor>
		</Row>
	</Building_Flavors>
	<!-- HAPPINESS BONUS (CITY 2) -->
	<Language_en_US>
		<Row Tag="TXT_KEY_COABLDG_HAPPINESS_BONUS_2">
			<Text>Happiness Bonus (city #2) </Text>
		</Row>
	</Language_en_US>
	<BuildingClasses>
		<Row>
			<Type>BUILDINGCLASS_HAPPINESS_BONUS_2</Type>
			<DefaultBuilding>BUILDING_HAPPINESS_BONUS_2</DefaultBuilding>
			<Description>TXT_KEY_COABLDG_HAPPINESS_BONUS_2</Description>
			<MaxPlayerInstances>1</MaxPlayerInstances>
			<NoLimit>true</NoLimit>
		</Row>
	</BuildingClasses>
	<Buildings>
		<Row>
			<BuildingClass>BUILDINGCLASS_HAPPINESS_BONUS_2</BuildingClass>
			<Type>BUILDING_HAPPINESS_BONUS_2</Type>
			<Description>TXT_KEY_COABLDG_HAPPINESS_BONUS_2</Description>
			<Civilopedia>TXT_KEY_COA_EMPTY</Civilopedia>
			<Strategy>TXT_KEY_COA_EMPTY</Strategy>
			<Help>TXT_KEY_COA_EMPTY</Help>
			<Cost>0</Cost>
			<FaithCost>0</FaithCost>
			<GoldMaintenance>0</GoldMaintenance>
			<PrereqTech>NULL</PrereqTech>
			<HurryCostModifier>-1</HurryCostModifier>
			<MinAreaSize>-1</MinAreaSize>
			<NeverCapture>true</NeverCapture>
			<ConquestProb>0</ConquestProb>
			<IconAtlas>EXPANSION_BW_ATLAS_2</IconAtlas>
			<PortraitIndex>13</PortraitIndex>
			<FreeStartEra>ERA_ANCIENT</FreeStartEra>
			<UnmoddedHappiness>18</UnmoddedHappiness>
			<MutuallyExclusiveGroup>11</MutuallyExclusiveGroup>
		</Row>
	</Buildings>
	<Building_Flavors>
		<Row>
			<BuildingType>BUILDING_HAPPINESS_BONUS_2</BuildingType>
			<FlavorType>FLAVOR_HAPPINESS</FlavorType>
			<Flavor>18</Flavor>
		</Row>
	</Building_Flavors>

And so on. The NoLimit part is needed because I have limited the number of national wonders per city - it makes these buildings not count towards the limit. The buildings are not hidden, as I don't like to hide things from the player.

Edit: It doesn't appear in captured cities though...

Btw, does anyone know the answer to my original question? (about manipulating unhappiness directly using Lua)
 
Good solution, PawelS. I'm hoping someone can answer the original question as well, but this looks workable. I'd like to poke around with the Lua codes, but I'm a little backed up and not sure when i could get to it.
 
I did some testing with Player.ChangeExtraHappinessPerCity. I hooked it to PlayerDoTurn, but that causes Happiness to increment each turn - in other words, happiness increases each turn based on the number of cities. I suppose you could hook to city founding and city capture, but I think that would get complicated to account for how many cities you had. Until someone comes up with something better, and if I go back to my civ, I may use your system above.
 
Hmm, so it seems to do what the name suggests - it changes extra happiness per city by the specified value, so a negative value should decrease it. There is also a GetExtraHappinessPerCity function, so together they can be used every turn to set it to a specific value depending on number of cities, by "clearing" it first (changing by the opposite of the current value), and then setting it to the desired level. But I don't think it allows negative extra happiness per city, so (like the solution with buildings), it probably needs a large base unhappiness per city, so it can be "relaxed" by the Lua script.

I need to do some testing myself, if it's successful then chances are that I will use these functions instead of the buildings, as it makes it easier to control things when a city is captured. A possible issue is interference with happiness per city from other sources, like wonders and policies (but it's not a big problem for me, as I will probably not use these effects in my mod).
 
It works :) Although the happiness is actually updated two turns after founding a new city. I will post my Lua code after making some adjustments to it - probably tomorrow.
 
OK, so this simple Lua script (used as InGameUIAddin), together with setting UNHAPPINESS_PER_CITY to 20, will make the unhappiness from number of cities equal to the number of cities squared (when the player has no more than 20 cities). This is what I'm going to use in my mod.

Code:
function AdjustHappiness (iPlayer)

	local pPlayer = Players [iPlayer]
	
	if pPlayer and pPlayer:IsAlive () and (not pPlayer:IsBarbarian ()) then
		
		-- clear extra happiness from number of cities
		pPlayer:ChangeExtraHappinessPerCity (-pPlayer:GetExtraHappinessPerCity ())
		
		-- set it to new value
		local iCities = pPlayer:GetNumCities ()
		if iCities < 20 then
			pPlayer:ChangeExtraHappinessPerCity (20 - iCities)
		end
	end
end

GameEvents.PlayerDoTurn.Add (AdjustHappiness)

-- adjust at the beginning of the game (or when loading a saved game)

for iPlayer = 0, GameDefines.MAX_PLAYERS-1, 1 do
	AdjustHappiness (iPlayer)
end

Edit: One small correction to avoid possible crashes
 
Improved version of the script - now it updates happiness correctly the next turn after a city is acquired or lost. (The previous version caused temporary miscalculation of happiness for one turn, which caused happiness from difficulty level to display incorrectly in the top panel tooltip.) Perhaps it's possible to make it update immediately, but it would require creating events that happen when a city is founded, razed, captured, lost, changes hands in a diplomatic deal... a bit too complicated for me. I think the next turn is good enough.

Code:
function AdjustHappiness (iPlayer)

	local pPlayer = Players [iPlayer]
	if pPlayer and pPlayer:IsAlive () and (not pPlayer:IsBarbarian ()) then

		local iOldValue = pPlayer:GetExtraHappinessPerCity ()
		local iCities = pPlayer:GetNumCities ()
		local iNewValue = 0

		if iCities < 20 then
			iNewValue = 20 - iCities
		end

		local iChange = iNewValue - iOldValue
		if iChange ~= 0 then
			pPlayer:ChangeExtraHappinessPerCity (iChange)
			pPlayer:SetHappiness (pPlayer:GetHappiness () + iChange*iCities)
		end
	end
end

GameEvents.PlayerDoTurn.Add (AdjustHappiness)

-- adjust happiness at the beginning of the game
if Game.GetGameTurn () == 0 then
	for iPlayer = 0, GameDefines.MAX_PLAYERS-1, 1 do
		pPlayer = Players [iPlayer]

		if pPlayer and pPlayer:IsAlive () and (not pPlayer:IsBarbarian ()) and pPlayer:GetNumCities () == 0 then
			AdjustHappiness (iPlayer)
		end

	end
end

Edit: Also changed the last part so it only adjusts happiness at the beginning of the game, not when loading a saved game. Previously when saving and reloading game after founding a city it would get updated immediately, which is a different behavior than when not reloading the game. If it's updated in the next turn, it should work like this without exceptions.
 
Back
Top Bottom