[Civ5] Help Changing Great Work Yields

Thank you for your code.

Seeing as adding GUI elements is possible, would you happen to know of any good examples so that I might add the effects to the UI top panel and City view myself?

As I recall JFD has a premade "Dynamic Top Panel" system but I've never tried to use it yet so don't know how complicated it will be. An alternative that is simpler in many ways is to add dummy buildings to the city each adding 1 Faith -- everything else is automatically accounted for in the top panel and city view except the city view's "Yield Box" won't show the extra faith as coming from Great Works, but rather from Buildings.


Also I updated my previous with the code Bismuth would need for 2 Faith only for every Great Work of Literature. As mentioned, the code is pre-set for LitWorks -> 2 Faith but the code itself is adaptable to all three types of Great Works for adding faith.

The dummy building method could also be used by Bismuth with slight alterations to the lua code provided. The thing with dummy buildings is that you could make great works directly add, for example, Happiness, whereas with direct-lua adding Happiness and getting it to 'stick' without also creating runaway 'compounding' of the added happiness is a bit of a PITA.
 
I know that it must be possible to add new things to a city's represented yields (if you can get something like "Golden Stool Points" in the Ashanti mod to work, you can get something as simple as adding another contributor to a city's yields), so here's how I would do it.

Psuedocode:

Every turn, examine each city (FOR Loop, I suppose), and count number of Great Works in that city.

In the FOR loop, grab the number of GW and multiply by X. Store this value inside a separate variable that increments with each city.

Add each city's GW*X number (if more than zero) to the yield of the city with the string of ("+(GW*X) Faith contributed by GW). Grab the separate variable made in the next step, throw it into another variable which gets displayed in the top UI, then zero out the first variable (to make it usable by next turn's FOR loop).


Sadly, I'm not familiar with Civ5's internal code enough to know what to call in these instances. Hell, I don't even know if this pseudocode would even work logically, as I don't know if Civ5 has simple versions of this stuff.
 
I know that it must be possible to add new things to a city's represented yields (if you can get something like "Golden Stool Points" in the Ashanti mod to work, you can get something as simple as adding another contributor to a city's yields), so here's how I would do it.

Psuedocode:

Every turn, examine each city (FOR Loop, I suppose), and count number of Great Works in that city.

In the FOR loop, grab the number of GW and multiply by X. Store this value inside a separate variable that increments with each city.

Add each city's GW*X number (if more than zero) to the yield of the city with the string of ("+(GW*X) Faith contributed by GW). Grab the separate variable made in the next step, throw it into another variable which gets displayed in the top UI, then zero out the first variable (to make it usable by next turn's FOR loop).


Sadly, I'm not familiar with Civ5's internal code enough to know what to call in these instances. Hell, I don't even know if this pseudocode would even work logically, as I don't know if Civ5 has simple versions of this stuff.
You obviously have never taken a look at any of the Firaxis-supplied files for managing these UI elements. :lol: 'Simple', 'Easy', or even 'Reasonably Straightforward' are not descriptors ever used by any mod-maker when discussing the methods to alter these files. :cry:


You do have to alter and replace the entire file even if you only need a minor two-line change, which means instant incompatibility with any other mod altering the same file even if for the smallest of changes, unless someone creates a system as JFD did where multiple mods can all add their adjustments to the data shown, then that system gathers all those adjustments coming from different mods, and displays the result to the user. And I haven't even looked at whether JFD's Dynamic Top Panel system really works when multiple mods are all trying to use it.
 
You obviously have never taken a look at any of the Firaxis-supplied files for managing these UI elements. :lol: 'Simple', 'Easy', or even 'Reasonably Straightforward' are not descriptors ever used by any mod-maker when discussing the methods to alter these files. :cry:

As a novice coder with some experience in Java and C++, but none in LUA or Civ5's systems, I figured this was the answer I was going to get. You'd think it'd be simple.

You do have to alter and replace the entire file even if you only need a minor two-line change, which means instant incompatibility with any other mod altering the same file even if for the smallest of changes, unless someone creates a system as JFD did where multiple mods can all add their adjustments to the data shown, then that system gathers all those adjustments coming from different mods, and displays the result to the user. And I haven't even looked at whether JFD's Dynamic Top Panel system really works when multiple mods are all trying to use it.

I'll take a look at this Dynamic Top Panel thing, and see if I can integrate into my mod. Would you happen to know of any mods that use it so I can use them for reference?

EDIT: I just found the Dynamic Top Panel mod, and I'm not even too sure where to start digging in. This could take some time. In the meantime, I wonder if it's possible to create a dummy building that gets added to each city which uses your code to create its faith yield. You mentioned the idea of creating a dummy building for each GW, but wouldn't adding one that counts GW's be simpler? But then, what do I know?

2ND EDIT:

I've been trying my hand at fixing the problem, but to no avail. This code seems like it'd work, but I feel like I'm missing something.

Code:
function FaithPerGreatWorkOfSpecificTypes(iPlayer)
	local pPlayer = Players[iPlayer]
	if bSpecificCivRequired then
		if pPlayer:GetCivilizationType() ~= iRequiredCivilization then return end
	end
	for pCity in pPlayer:Cities() do
		for sGreatWorkType,iFaithChange in pairs(tGreatWorkTypeFaithSelections) do
			pCity:SetNumRealBuilding(iDummyBuilding, ReturnNumberGreatWorksOfTypeInCity(pCity, sGreatWorkType))
		end
	end
end

This snippet should add a number of dummy buildings to a city for each GW in that city, right?
 
....but wouldn't adding one that counts GW's be simpler? But then, what do I know?
It would if buildings were able to do such a thing, but all they can do is hold a great work. There's no column within table <Buildings> nor child table within the Buildings-Schema that is "effect per filled great work slot" except for the table <Building_DomainFreeExperiencePerGreatWork> (and that only gives units created in the same city extra XP points.

What you have to do is create a dummy building within your mod's XML (or SQL if like to work in SQL), and give that dummy building a yield of +1 or +2 Faith per turn under table <Building_YieldChanges>, like this (for +2 Faith per Turn):
Code:
<GameData>
	<Buildings>
		<Row>
			<Type>BUILDING_GREATWORK_FAITH</Type>
			<BuildingClass>BUILDINGCLASS_GREATWORK_FAITH</BuildingClass>
			<Cost>-1</Cost>
			<FaithCost>-1</FaithCost>
			<PrereqTech>NULL</PrereqTech>
			<GreatWorkCount>-1</GreatWorkCount>
			<ArtDefineTag>NONE</ArtDefineTag>
			<MinAreaSize>-1</MinAreaSize>
			<NeverCapture>true</NeverCapture>
			<HurryCostModifier>-1</HurryCostModifier>
			<IconAtlas>BW_ATLAS_1</IconAtlas>
			<PortraitIndex>19</PortraitIndex>
			<Description>TXT_KEY_GREATWORK_FAITH</Description>
			<NukeImmune>true</NukeImmune>
		</Row>
	</Buildings>
	<BuildingClasses>
		<Row>
			<Type>BUILDINGCLASS_GREATWORK_FAITH</Type>
			<DefaultBuilding>BUILDING_GREATWORK_FAITH</DefaultBuilding>
			<Description>TXT_KEY_GREATWORK_FAITH</Description>
			<NoLimit>true</NoLimit>
		</Row>
	</BuildingClasses>
	[color="blue"]<Building_YieldChanges>
		<Row>
			<BuildingType>BUILDING_GREATWORK_FAITH</BuildingType>
			<YieldType>YIELD_FAITH</YieldType>
			<Yield>2</Yield>
		</Row>
	</Building_YieldChanges>[/color]
	<Language_en_US>
		<Row Tag="TXT_KEY_GREATWORK_FAITH">
			<Text>GW Faith</Text>
		</Row>
	</Language_en_US>
</GameData>
then you would change the lua previously given to this:
Code:
local iFaithPerGreatWorkBuilding = GameInfoTypes.BUILDING_GREATWORK_FAITH
local iRequiredCivilization = GameInfoTypes.CIVILIZATION_SOMETHING_OR_OTHER

function FaithPerGreatWork(iPlayer)
	local pPlayer = Players[iPlayer]
	if pPlayer:GetCivilizationType() == iRequiredCivilization then
		for pCity in pPlayer:Cities() do
			pCity:SetNumRealBuilding(iFaithPerGreatWorkBuilding, pCity:GetNumGreatWorks())
		end
	end
end
GameEvents.PlayerDoTurn.Add(FaithPerGreatWork)
Then you have to edit the blue bits in the shown xml-code if you decide that +2 Faith / Great Work is too little or too great. As a personal 'flavoring' to the way I like to write my code, I would make the xml give 1 Faith / Turn for the building, and then add the number of desired buildings as required within the lua: this allows me to keep the adjustments I make for balance in the lua, and allows me to more-easily use the exact same code to give a different effect:
Spoiler :
XML Code:
Code:
<GameData>
	<Buildings>
		<Row>
			<Type>BUILDING_GREATWORK_EFFECTS</Type>
			<BuildingClass>BUILDINGCLASS_GREATWORK_EFFECTS</BuildingClass>
			<Cost>-1</Cost>
			<FaithCost>-1</FaithCost>
			<PrereqTech>NULL</PrereqTech>
			<GreatWorkCount>-1</GreatWorkCount>
			<ArtDefineTag>NONE</ArtDefineTag>
			<MinAreaSize>-1</MinAreaSize>
			<NeverCapture>true</NeverCapture>
			<HurryCostModifier>-1</HurryCostModifier>
			<IconAtlas>BW_ATLAS_1</IconAtlas>
			<PortraitIndex>19</PortraitIndex>
			<Description>TXT_KEY_GREATWORK_EFFECTS</Description>
			<NukeImmune>true</NukeImmune>
		</Row>
	</Buildings>
	<BuildingClasses>
		<Row>
			<Type>BUILDINGCLASS_GREATWORK_EFFECTS</Type>
			<DefaultBuilding>BUILDING_GREATWORK_EFFECTS</DefaultBuilding>
			<Description>TXT_KEY_GREATWORK_EFFECTS</Description>
			<NoLimit>true</NoLimit>
		</Row>
	</BuildingClasses>
	[color="blue"]<Building_YieldChanges>
		<Row>
			<BuildingType>BUILDING_GREATWORK_EFFECTS</BuildingType>
			<YieldType>YIELD_FAITH</YieldType>
			<Yield>1</Yield>
		</Row>
	</Building_YieldChanges>[/color]
	<Language_en_US>
		<Row Tag="TXT_KEY_GREATWORK_EFFECTS">
			<Text>GW Effects</Text>
		</Row>
	</Language_en_US>
</GameData>
And my lua becomes (note the slight differences):
Code:
local iEffectsPerGreatWork = 2
local iEffectsPerGreatWorkBuilding = GameInfoTypes.BUILDING_GREATWORK_EFFECTS
local iRequiredCivilization = GameInfoTypes.CIVILIZATION_SOMETHING_OR_OTHER

function EffectPerGreatWork(iPlayer)
	local pPlayer = Players[iPlayer]
	if pPlayer:GetCivilizationType() == iRequiredCivilization then
		for pCity in pPlayer:Cities() do
			pCity:SetNumRealBuilding(iEffectsPerGreatWorkBuilding, (pCity:GetNumGreatWorks() * iEffectsPerGreatWork))
		end
	end
end
GameEvents.PlayerDoTurn.Add(EffectPerGreatWork)
I can make the dummy building called BUILDING_GREATWORK_EFFECTS give any reasonable effect I want a Great Work to supply that is also possible for a Building to give. I could also break things up and have one building give Faith, and another Happiness, and give Faith equal to the number of Great works, and Happiness as twice the number of Great Works, or vice versa, based on how I write my lua code for adding the buildings into individual cities.
 
Code:
function FaithPerGreatWorkOfSpecificTypes(iPlayer)
	local pPlayer = Players[iPlayer]
	if bSpecificCivRequired then
		if pPlayer:GetCivilizationType() ~= iRequiredCivilization then return end
	end
	for pCity in pPlayer:Cities() do
		for sGreatWorkType,iFaithChange in pairs(tGreatWorkTypeFaithSelections) do
			pCity:SetNumRealBuilding(iDummyBuilding, ReturnNumberGreatWorksOfTypeInCity(pCity, sGreatWorkType))
		end
	end
end

This snippet should add a number of dummy buildings to a city for each GW in that city, right?
I'm guessing you are mixing and matching piece-parts of the code I presented for Bismuth with the code I originally wrote for you.
  • You would need to copy into your code the function I wrote for Bismuth's version called function ReturnNumberGreatWorksOfTypeInCity, and,
  • you would need the code that creates the lua-table called tGreatWorkTypeFaithSelections, and,
  • You would need to make sure the resulting lua script is in an lua file that has a InGameUIAddin entry within the 'Content' tab of the mod's properties within ModBuddy: whoward69's what ModBuddy setting for what file types tutorial, and,
  • You need a valid building defined within the game's XML that you are going to use as your dummy building, and you need your lua-script to reference that building correctly
  • Any one of these things done incorrectly will cause the code to fail.
  • Also, see my post # 25
 
Thank you for your patience, LeeS. I'm hope I'm not being too frustrating. :D

So, just to make sure we're on the same page, the code for the LUA file should be this:

Code:
--LeeS
--28May2016

local iFaithPerGreatWorkBuilding = GameInfoTypes.FAITH_DUMMY -- I've got the building properly set up.
local bSpecificCivRequired = true
local iRequiredCivilization = GameInfoTypes.CIVILIZATION_MYCIV --Intentionally left vague for reuse by any other noobs.


local tGreatWorkTypeFaithSelections = { ["GREAT_WORK_SLOT_LITERATURE"] = 2, ["GREAT_WORK_SLOT_ART_ARTIFACT"] = 2,
			["GREAT_WORK_SLOT_MUSIC"] = 2  }

------------------------------------------------------------------------------------------------------------------------------------------------
--GreatWorksInCityUtilities
------------------------------------------------------------------------------------------------------------------------------------------------

local tBuildingsWithGreatWorkSlots = {}
local tBuildingsWithGreatWorkSlotsOfType = {}

for Building in DB.Query("SELECT ID, BuildingClass, GreatWorkSlotType FROM Buildings WHERE GreatWorkCount > 0") do
	if not tBuildingsWithGreatWorkSlotsOfType[Building.GreatWorkSlotType] then
		tBuildingsWithGreatWorkSlotsOfType[Building.GreatWorkSlotType] = {}
	end
	tBuildingsWithGreatWorkSlotsOfType[Building.GreatWorkSlotType][Building.ID] = GameInfoTypes[Building.BuildingClass]
	tBuildingsWithGreatWorkSlots[Building.ID] = GameInfoTypes[Building.BuildingClass]
end
function ReturnNumberGreatWorksOfTypeInCity(pCity, sGreatWorkType)
	local iNumberGreatWorksOfType = 0
	if pCity:GetNumGreatWorks() > 0 then
		for Building,BuildingClass in pairs(tBuildingsWithGreatWorkSlotsOfType[sGreatWorkType]) do
			if pCity:IsHasBuilding(Building) then
				iNumberGreatWorksOfType = iNumberGreatWorksOfType + pCity:GetNumGreatWorksInBuilding(BuildingClass)
			end
		end
	end
	return iNumberGreatWorksOfType
end

------------------------------------------------------------------------------------------------------------------------------------------------
--Faith Changes Per Great Work
------------------------------------------------------------------------------------------------------------------------------------------------



function FaithPerGreatWork(iPlayer)
	local pPlayer = Players[iPlayer]
	if pPlayer:GetCivilizationType() == iRequiredCivilization then
		for pCity in pPlayer:Cities() do
			pCity:SetNumRealBuilding(iFaithPerGreatWorkBuilding, pCity:GetNumGreatWorks())
		end
	end
end
------------------------------------------------------------
---- WilliamHoward's IsCivInPlay
------------------------------------------------------------

function IsCivInPlay(iCivType)
  for iSlot = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
    local iSlotStatus = PreGame.GetSlotStatus(iSlot)
    if (iSlotStatus == SlotStatus.SS_TAKEN or iSlotStatus == SlotStatus.SS_COMPUTER) then
      if (PreGame.GetCivilization(iSlot) == iCivType) then
        return true
      end
    end
  end
  
  return false
end
------------------------------------------------------------
---- Game Event Hooks
------------------------------------------------------------
if bSpecificCivRequired then
	if IsCivInPlay(iRequiredCivilization) then
		GameEvents.PlayerDoTurn.Add(FaithPerGreatWork)
	end
else
	GameEvents.PlayerDoTurn.Add(FaithPerGreatWork)

------------------------------------------------------------
---- Loading Confirmation Print Statements
------------------------------------------------------------

print("The GreatWorksYieldsManipulations.lua loaded to the end")

The LUA is properly set to InGameUIAddin and the dummy building is properly set up in the XML.

I had also started to use Bismuth's code once I realized that what I needed was this whole dummy building thing. No offense to the previous code you wrote for me, I'm quite thankful. :)
 
  1. Because you are using this method:
    Code:
    pCity:SetNumRealBuilding(iFaithPerGreatWorkBuilding, [color="blue"]pCity:GetNumGreatWorks()[/color])
    You will not need this part of the code:
    Code:
    local tGreatWorkTypeFaithSelections = { ["GREAT_WORK_SLOT_LITERATURE"] = 2, ["GREAT_WORK_SLOT_ART_ARTIFACT"] = 2,
    			["GREAT_WORK_SLOT_MUSIC"] = 2  }
    
    ------------------------------------------------------------------------------------------------------------------------------------------------
    --GreatWorksInCityUtilities
    ------------------------------------------------------------------------------------------------------------------------------------------------
    
    local tBuildingsWithGreatWorkSlots = {}
    local tBuildingsWithGreatWorkSlotsOfType = {}
    
    for Building in DB.Query("SELECT ID, BuildingClass, GreatWorkSlotType FROM Buildings WHERE GreatWorkCount > 0") do
    	if not tBuildingsWithGreatWorkSlotsOfType[Building.GreatWorkSlotType] then
    		tBuildingsWithGreatWorkSlotsOfType[Building.GreatWorkSlotType] = {}
    	end
    	tBuildingsWithGreatWorkSlotsOfType[Building.GreatWorkSlotType][Building.ID] = GameInfoTypes[Building.BuildingClass]
    	tBuildingsWithGreatWorkSlots[Building.ID] = GameInfoTypes[Building.BuildingClass]
    end
    function ReturnNumberGreatWorksOfTypeInCity(pCity, sGreatWorkType)
    	local iNumberGreatWorksOfType = 0
    	if pCity:GetNumGreatWorks() > 0 then
    		for Building,BuildingClass in pairs(tBuildingsWithGreatWorkSlotsOfType[sGreatWorkType]) do
    			if pCity:IsHasBuilding(Building) then
    				iNumberGreatWorksOfType = iNumberGreatWorksOfType + pCity:GetNumGreatWorksInBuilding(BuildingClass)
    			end
    		end
    	end
    	return iNumberGreatWorksOfType
    end
  2. It would not have hurt anything, it just would be unused clutter in the lua script, since this
    Code:
    function FaithPerGreatWork(iPlayer)
    	local pPlayer = Players[iPlayer]
    	if pPlayer:GetCivilizationType() == iRequiredCivilization then
    		for pCity in pPlayer:Cities() do
    			pCity:SetNumRealBuilding(iFaithPerGreatWorkBuilding, pCity:GetNumGreatWorks())
    		end
    	end
    end
    Never references anything in that unneeded part of the code.
  3. The whole code you would need would be:
    Code:
    --LeeS
    --28May2016
    
    local iFaithPerGreatWorkBuilding = GameInfoTypes.FAITH_DUMMY -- I've got the building properly set up.
    local bSpecificCivRequired = true
    local iRequiredCivilization = GameInfoTypes.CIVILIZATION_MYCIV --Intentionally left vague for reuse by any other noobs.
    
    ------------------------------------------------------------------------------------------------------------------------------------------------
    --Faith Changes Per Great Work
    ------------------------------------------------------------------------------------------------------------------------------------------------
    
    function FaithPerGreatWork(iPlayer)
    	local pPlayer = Players[iPlayer]
    	if pPlayer:GetCivilizationType() == iRequiredCivilization then
    		for pCity in pPlayer:Cities() do
    			pCity:SetNumRealBuilding(iFaithPerGreatWorkBuilding, pCity:GetNumGreatWorks())
    		end
    	end
    end
    ------------------------------------------------------------
    ---- WilliamHoward's IsCivInPlay
    ------------------------------------------------------------
    
    function IsCivInPlay(iCivType)
      for iSlot = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local iSlotStatus = PreGame.GetSlotStatus(iSlot)
        if (iSlotStatus == SlotStatus.SS_TAKEN or iSlotStatus == SlotStatus.SS_COMPUTER) then
          if (PreGame.GetCivilization(iSlot) == iCivType) then
            return true
          end
        end
      end
      
      return false
    end
    ------------------------------------------------------------
    ---- Game Event Hooks
    ------------------------------------------------------------
    if bSpecificCivRequired then
    	if IsCivInPlay(iRequiredCivilization) then
    		GameEvents.PlayerDoTurn.Add(FaithPerGreatWork)
    	end
    else
    	GameEvents.PlayerDoTurn.Add(FaithPerGreatWork)
    [color="red"]end[/color]
    
    ------------------------------------------------------------
    ---- Loading Confirmation Print Statements
    ------------------------------------------------------------
    
    print("The GreatWorksYieldsManipulations.lua loaded to the end")
    • Is the dummy building actually called "FAITH_DUMMY" in the XML ?
  4. The reason Bismuth needed that extra code and you do not is that Bismuth wanted to limit the effect to only Great Works of Writing, whereas you are going for any and all Great Works with the pCity:GetNumGreatWorks() method
  5. There was also a missing "end" which I highlighted in red which crept in there somewhere along the line.
 
Ah, I was concerned that I was missing some piece of code because I felt my code didn't reference any part of Bismuth's. I've also discovered that my first attempt to correct my code left me without that "end", causing it to crash. But it all works now!

Thank you, LeeS, for your support. I never would have gotten this without your help. :)
 
Back
Top Bottom