Some assistance with Civilization Traits in Lua

Harkodos

Warlord
Joined
Feb 9, 2016
Messages
197
I'm currently in the process of programming a wide variety of unique abilities for Civilizations I plan to make, and unfortunately some of the ideas I've come up with are still a bit out of my league when it comes to Lua programming. So, I was wondering if anybody would like to help program a few of the Civilization traits for me. Some of course, are more complex than others, but I don't think any should be on the verge of ridiculously difficult to program in.

If anyone would like to help, these are the Civilization UAs I need help with:
Code:
[I]Too Stupid to Fail:
Finishing a Policy Tree decreases the cost of future Social Policies by 5%.[/I]
(I get how to add Policies and how to add things onto Policies, but not how to add a Policy Finisher to a specific Civ).
Code:
[I]Arch For Life:
Receive a +20% Attack Bonus when at war with a Civilization that you have Denounced. Receive a +20% Defense Bonus when at War with a Civilization that has Denounced you.[/I]
(Not sure how to program a trait that activates off of Denouncements).
Code:
[I]Legends of Awesomeness:
Melee, Ranged, and Recon units earn Points toward Great Writers in the Capital for each Enemy Unit killed.[/I]
(I wouldn't know where to begin in programming this one).
Code:
[I]The Peacekeepers:
Receive a +5% Combat Bonus when at War with a Major Civilization for each of your Allies who are also at War with that Civilization.[/I]
(Not too sure how to program stuff for friendly or major civilizations).
Code:
[I]To Fill the Emptiness Inside...:
+1 Food in the Capital for every 2 Citizens in the cities of other Major Civilizations.[/I]
(Wouldn't know where to start with programming in this one, either).
Code:
[I]Mewnipendence Day:
For every 10 Faith that your empire earns per turn your military units receive a +1% Combat Bonus (to a maximum of 20%).[/I]
(Yeah, I got nothin').

There is more to each of these Civilization Traits, but these are the only parts I need help with (for instance, To Fill the Emptiness Inside... will receive 2 penalties to balance out what is obviously a ridiculously powerful starting ability).

If anyone would be willing to help me with these, that'd be great! If not, then I guess these Civs would wind up on the back-burner until such a time as they could be programmed properly.
 
  • Too Stupid to Fail - There's no GameEvent for finishing a branch as far as I can tell, though you could check
    Code:
    pPlayer:GetNumPolicyBranchesFinished()
    after a PlayerAdoptPolicy event. Then just add some dummy buildings or dummy policies with PolicyCostModifier of -5. (Note, though: the game does some rounding and other adjustments of policy costs after modifiers from buildings and policies are applied, so your policy costs may look like they're bugged even when they're actually working fine.)
  • Arch For Life - You can check if a given player has denounced another given player with
    Code:
    pPlayer:IsDenouncedPlayer(iOtherPlayer)
    . Then just give your all your units a promotion that gives +20% bonus when attacking/defending.
  • Legends of Awesomeness - Not quite codeable without a modded DLL; there's no way to determine what unit killed what other unit, and therefore no way to determine if the killer is Melee/Ranged/Recon. However, if you broadened that to "Earn Points toward Great Writers in the Capital for each enemy Unit killed" (regardless of what unit type killed them, or even if they were killed by city bombardment), you could then use the UnitPrekill or UnitKilledInCombat hooks - you can get which player killed the unit, just not the specific unit that did the killing.
  • The Peacekeepers - If by allies you mean civilizations with which you have a Declaration of Friendship, you can check that with
    Code:
    pPlayer:IsDoF(iOtherPlayer)
    . To ensure the enemy is a major civilization, just make sure both
    Code:
    pPlayer:IsMinorCiv()
    and
    Code:
    pPlayer:IsBarbarian()
    are false.
  • To Fill the Emptiness Inside... - On a PlayerDoTurn hook, if the player taking the turn is your civ, loop through all major civs (
    Code:
    for i = 0, GameDefines.MAX_MAJOR_CIVS, 1 do
    ) and then loop through all of their cities, then add a number of dummy buildings that yields +1 food to the capital equal to half those cities' population:
    Code:
    local iYourCiv = GameInfoTypes.CIVILIZATION_PLACEHOLDER
    local iDummy = GameInfoTypes.BUILDING_PLACEHOLDER
    
    function FillTheEmptiness(iPlayer)
    	local pPlayer = Players[iPlayer]
    	if pPlayer:GetCivilizationType() == iYourCiv then
    		local pCapital = pPlayer:GetCapitalCity()
    		if pCapital then
    			local iBuildingsToAdd = 0
    			for i = 0, GameDefines.MAX_MAJOR_CIVS, 1 do
    				local pOtherPlayer = Players[i]
    				if i ~= iPlayer and pOtherPlayer:IsEverAlive() then
    					for pCity in pOtherPlayer:Cities() do
    						iBuildingsToAdd = iBuildingsToAdd + (pCity:GetPopulation() / 2)
    					end
    				end
    			end
    			pCapital:SetNumRealBuilding(iDummy, iBuildingsToAdd)
    		end
    	end
    end
    
    GameEvents.PlayerDoTurn.Add(FillTheEmptiness)
  • You can get the civ's faith per turn with
    Code:
    pPlayer:GetTotalFaithPerTurn()
    . Then add promotions to each of the military units.
 
Hmmm.

I'm probably too sleepy, but the one of those that I can understand the most is To Fill The Emptiness Inside...'s answer (as I've already dealt a lot with adding buildings for triggering a Civilization's UA). I may need some time for the rest to register with my brain. Though the terms to use are a nice starting point.

Also, one other thing: I was working on a Civ Trait today, and it seems it will require some Lua to work as well (as the DOFGreatPersonModifier doesn't allow the amount to go to negatives).

Anyways, I'd also need an Lua script that does the following:
Code:
[I]Toxic Love:
Cannot earn Great People. Declaring Friendship with another Civilization decreases their Great Person generation by 50%.[/I]
(I thought I could get the desired result by using DOFGreatPersonModifier and by using the Trait_NoTrain to achieve the desired result, but that assumption seemed to be incorrect).
 
An alternate for the Fill The Emptiness would be as this:
Code:
local iYourCiv = GameInfoTypes.CIVILIZATION_PLACEHOLDER
local iDummy = GameInfoTypes.BUILDING_PLACEHOLDER

--------------------------------------------------------------
-- GetWorldPopulation
-- Author: LeeS
-- DateCreated: 5/29/2016
--------------------------------------------------------------

function GetWorldPopulation(iPlayer, bSkipDesignatediPlayer, bCountMinors, bTruncateEachCivPop, bTruncateTotalReturned)
	local iTotalCount = 0
	local iCivsNumber = (bCountMinors and (GameDefines.MAX_PLAYERS - 2) or (GameDefines.MAX_MAJOR_CIVS - 1))
	for iOtherPlayer = 0, iCivsNumber do
		local pOtherPlayer = Players[iOtherPlayer]
		if pOtherPlayer:IsEverAlive() and pOtherPlayer:IsAlive() then
			local bCountThisPlayer = true
			if iOtherPlayer == iPlayer and bSkipDesignatediPlayer then
				bCountThisPlayer = false
			end
			if bCountThisPlayer then
				if bTruncateEachCivPop then
					iTotalCount = iTotalCount + math.floor(pOtherPlayer:GetTotalPopulation())
				else
					iTotalCount = iTotalCount + pOtherPlayer:GetTotalPopulation()
				end
			end
		end
	end
	if bTruncateTotalReturned then
		return math.floor(iTotalCount)
	else
		return iTotalCount
	end
end
--------------------------------------------------------------
-- FillTheEmptiness
-- Author: Klisz (with edits by LeeS)
-- DateCreated: 5/29/2016
--------------------------------------------------------------

function FillTheEmptiness(iPlayer)
	local pPlayer = Players[iPlayer]
	if pPlayer:GetCivilizationType() == iYourCiv then
		local pCapital = pPlayer:GetCapitalCity()
		if pCapital then
			pCapital:SetNumRealBuilding(iDummy, math.floor(GetWorldPopulation(iPlayer, true, false, false, true) / 2))
		end
	end
end

GameEvents.PlayerDoTurn.Add(FillTheEmptiness)
 
For the Policy Branch Finishing effect
Code:
local iBuildingForCompletedPolicyBranches = GameInfoTypes.BUILDING_POLICY_BRANCH_FINISHED_EFFECT
local tPolicyAndBranchCorrespondances = {}
for Policy in DB.Query("SELECT ID, Type, PolicyBranchType FROM Policies WHERE Level = '0'") do
	local iPolicyId, sPolicyType, sPolicyBranchName, iPolicyBranchID = Policy.ID, Policy.Type, Policy.PolicyBranchType, -1
	if (sPolicyBranchName ~= nil) then
		for PolicyBranch in GameInfo.PolicyBranchTypes("Type='" .. sPolicyBranchName .. "'") do
			iPolicyBranchID = PolicyBranch.ID
		end
	end
	if iPolicyBranchID ~= -1 then
		if (GameInfo.PolicyBranchTypes[iPolicyBranchID].FreePolicy ~= sPolicyType) and (GameInfo.PolicyBranchTypes[iPolicyBranchID].FreeFinishingPolicy ~= sPolicyType) then
			local bPolicyIsAPreReq = false
			for row in GameInfo.Policy_PrereqPolicies("PrereqPolicy = '" .. sPolicyType .. "'") do
				bPolicyIsAPreReq = true
				break
			end
			if not bPolicyIsAPreReq then
				tPolicyAndBranchCorrespondances[iPolicyId] = iPolicyBranchID
			end
		end
	end
end

function PolicyBranchFinished(PlayerID, policyID)
	if tPolicyAndBranchCorrespondances[policyID] then
		local pPlayer = Players[PlayerID]
		if pPlayer:IsPolicyBranchFinished(tPolicyAndBranchCorrespondances[policyID]) then
			local pCapitalCity = pPlayer:GetCapitalCity()
			if pCapitalCity ~= nil then
				print("We Should have added " .. pPlayer:GetNumPolicyBranchesFinished() .. " Dummy Buildings in the city of " .. pCapitalCity:GetName())
				pCapitalCity:SetNumRealBuilding(iBuildingForCompletedPolicyBranches, pPlayer:GetNumPolicyBranchesFinished())
			end
		end
	end
end
GameEvents.PlayerAdoptPolicy.Add(PolicyBranchFinished);
This keys off the Policy Adopted event, and therefore only needs to fire as part of Policy Adoption, and as coded can only adjust the number of dummy buildings in the capital when a policy branch has been completed.

You need to add into your xml a Building called BUILDING_POLICY_BRANCH_FINISHED_EFFECT and give it the reduction in future policy costs via column <PolicyCostModifier> in table <Buildings>, with your setting of <PolicyCostModifier> as a negative integer value.

The problem with all dummy buildings in a capital city method is that the capital city can be captured and thereby mess-up the UA ability, as it is being used with dummy buildings in the capital. The problem with giving free social policies is that you have to give additional policies (or swap policies) every time the number of completed branches changes, and if not carefully done can cause destruction of the social policy cost system, and incorrect evaluation of the 'extra' happiness for any wonder (like Prora Resort) that uses column <HappinessPerXPolicies> from table <Buildings>.

The better method is probably to stick to buildings and avoid policies wherever possible, and to cure the capital capturing issue with a CityCaptureComplete event. I have part of the code needed for such a system, but there are still some outstanding issues that would need to be thought through, which I can take another look at during the coming week if this whole dummy buildings added to the capital issue is a killer for you.

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

edit on 20June2016: fixed the issues in the code
 
@LeeS
Thanks for the FillTheEmptiness routine, it works wonderfully! (Now I just have to decide whether it's overpowered or not, even taking into consideration the rest of the ability which doubles unhappiness from citizens, and disables the ability to annex cities and build settlers).
 
I recently implemented the PolicyFinishingEffect Building, but it doesn't seem to want to activate. Searching through the .Lua log, I found this:

Code:
[443141.088] Runtime Error: C:\...\Sid Meier's Civilization 5\MODS\Harkodos' Clan Bravo (BNW)\Lua/Johnny_UA.lua:10: bad argument #2 to '?' (Key must be of type 'number' or 'string'.)
[443141.088] Runtime Error: Error loading C:\...\Sid Meier's Civilization 5\MODS\Harkodos' Clan Bravo (BNW)\Lua/Johnny_UA.lua.
I believe it references this line:
Code:
if (GameInfo.PolicyBranchTypes[GameInfoTypes[sPolicyBranchName]].FreePolicy ~= sPolicyType) and (GameInfo.PolicyBranchTypes[GameInfoTypes[sPolicyBranchName]].FreeFinishingPolicy ~= sPolicyType) then
 
I would need your mod

edit -- I won't be able to look at it tomorrow though. I have all-day real life commitments until probably very late in the day and I expect my brain to be pretty much fried by the time I get home tomorrow evening/night.
 
Perfectly understandable.

When you do have the time though, the mod package should be waiting.

I had time to look into the issue this evening (which I thought I would not have time to do given the extra real life family-fun for tomorrow). I've updated the code in Post #7 with the fixes needed.

Long story short: I forgot about the possibility of all those dummy policies for E&D and such, and forgot to account for some of the stuff Firaxis does in the definitions of Policies in the Policies xml-table. So there were nil errors when attempting to access data for the policy-branch to which a policy belonged.
 
Well, I no longer get the errors in the .Lua log, but I still can't receive a copy of the Dummybuilding in the Capital upon finishing a Policy Tree for some reason (it never seems to appear on its own).

Also, I have another problem, unrelated to this one:
For one of the other Civs I'm making, I'm trying to add a Pantheon where they can purchase military buildings with Faith once they're founded a Religion (since that seems to be a requirement to trigger the ability). The problem is that I've created the Belief and everything that should account for allowing the buildings to be bought with Faith, but they never seem to appear in-game, even though I get no error messages.

I'll post my code and leave it for the time being. This is a problem that can wait, no rush or hurry.
 

Attachments

Well, I no longer get the errors in the .Lua log, but I still can't receive a copy of the Dummybuilding in the Capital upon finishing a Policy Tree for some reason (it never seems to appear on its own).

Also, I have another problem, unrelated to this one:
For one of the other Civs I'm making, I'm trying to add a Pantheon where they can purchase military buildings with Faith once they're founded a Religion (since that seems to be a requirement to trigger the ability). The problem is that I've created the Belief and everything that should account for allowing the buildings to be bought with Faith, but they never seem to appear in-game, even though I get no error messages.

I'll post my code and leave it for the time being. This is a problem that can wait, no rush or hurry.
Even more "duh" on my part. I've fixed it in Post #7 (as I want to avoid multiple different copies of the code floating around on the thread). It's embarrassing enough to have all these errors for stupid mistakes creeping up everywhere in that code without having different 'versions' of the same code running around on the same thread. 'pCapitalCity' and 'pCapital' : these are not the same, and the code was failing to give the buildings because pCapital was never defined anywhere.

Also, you wanted this as a unique ability for CivX ? Currently there's nothing in the code of Post #7 to look at whether the civilization (player) is the correct one, so everybody would get the benefit.

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

Pantheons cannot be used to allow building-purchases. The game simply won't do it, even if the building-class and the belief are registered under table "Belief_BuildingClassFaithPurchase".
 
Well, I tried the updated code on post #7 and I'm still getting zip.

Also for the Pantheon, is it possible to turn it into a Founder, or if not that, a Follower or Reformation Belief, and then make it so that only that particular Civ can choose that belief? (though when others are following it, they can still use it unless it's a Founder Belief)
 
Well, I tried the updated code on post #7 and I'm still getting zip.

Also for the Pantheon, is it possible to turn it into a Founder, or if not that, a Follower or Reformation Belief, and then make it so that only that particular Civ can choose that belief? (though when others are following it, they can still use it unless it's a Founder Belief)
repost the mod as it currently is. I'll look at it this evening or tomorrow morning.

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

edit --> also, how are you testing?

  • If you give yourself policiies directly through IGE or LiveTuner (for example) this does not cause the Policy Adoption event to fire, and the code does not run.
  • You need to give yourself a mountain of culture, then use the normal policy selection screen to adopt policies. The code as currently posted in #7 is working fine for me for a test-building I used.

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

I have a couple of hours yet this morning before I have to leave to run my mother to have an outpatient surgery so I'll keep an eye out as possible on this thread until then.
 
Oh, yes I was giving myself the policies through the IGE. Doing the normal Culture acquisition fixed that problem, lickety-split. Thanks for the tip!
 
I've recently been trying to finish the Legends of Awesomeness Civilization trait which I've bogged down to just earning points towards Great Writers for each enemy unit killed. To achieve this, I tried modding a code segment I found in Maximus' Microworld Mod which suggested that that effect could be achieved, however whenever I try the code, I get an error regarding some thing as "Vector" coming back as a nil value. reading around the forums, this may be due to the mod requiring whoward69's modified .dll, but seeing as I don't want to rely upon .dll's for my mods, I was wondering if there was a way to convert the code I have below so that it can be used without said .dll.

Code:
local defUnitID;
function GetVoPDeadUnit(player, unitID)
   defUnitID = unitID;
   return false;
end
GameEvents.CanSaveUnit.Add(GetVoPDeadUnit);

function AwardGreatPersonPoint(attPlayerID,defPlayerID,defUnitTypeID)
    local attPlayer	= Players[attPlayerID];
	local defPlayer	= Players[defPlayerID];
	local defUnit	= defPlayer:GetUnitByID(defUnitID);
    local attPlayer	= Players[attPlayerID];
	local gpModifier = 0;
	local gp = 0;
	local tempCount = 0;
	local maxCount = 0;
	local gpType = "SPECIALIST_WRITER";
	local gpSpecialist = GameInfo.Specialists["SPECIALIST_WRITER"];
	local gpID = gpSpecialist.ID;

	if not defUnit then 
	   print("AwardCombatGPPNodefUnit!");
	   return;
	end
	
	local gpCity = attPlayer:GetCapitalCity();
	local HexPos = ToHexFromGrid(Vector2( defUnit:GetX(), defUnit:GetY()));
	if not defUnit:IsCombatUnit() then    --Uncombat units do not count.
		print("AwardCombatGPPNotCombatUnit!");
	    return;
	end
	if not IsMajorCiv(defPlayer) or not IsMajorCiv(attPlayer) then return;  -- City-States and Barbarian do not count;
	end
	
    print("AwardCombatGPPStartCalculate");
	
	--local Trait = GetPlayerTrait(attPlayer);
    local gpModifier = 50; --local gpModifier = Trait.CombatGPP;
	if gpModifier == 0 then
       return;
	end
	local defCombat = defUnit:GetBaseCombatStrength();
    gp = defCombat*gpModifier;    
	for pCity in attPlayer:Cities() do
		for tempSpecialist in  GameInfo.Specialists() do
	        local tempType = tempSpecialist.Type;
	        if tempType == "SPECIALIST_WRITER" then
	           local tempID = tempSpecialist.ID;
		       tempCount =pCity:GetSpecialistGreatPersonProgress(tempID);
			   print("AwardCombatGPPSpecialistGPP="..tempCount);
		       if tempCount > maxCount then
			      maxCount = tempCount;
				  gpType = tempType;
				  gpID = tempID;
				  gpCity = pCity;
			   end
		    end
        end		
	end
print("AwardCombatGPPAddGPP");
local TextType=" ";
local name = gpCity:GetName();
if gpType ==  "SPECIALIST_WRITER" then
   TextType = TextType..Locale.ConvertTextKey("TXT_KEY_COMBAT_GREAT_WRITER_POINT");
end
local Fin=" ";
Fin = Fin..Locale.ConvertTextKey("TXT_KEY_IN");
local rand = Game.Rand(100,"Valley of Peace Promotion");
if gpCity then
    gpCity:ChangeSpecialistGreatPersonProgressTimes100(gpID,gp);
end
print("AwardCombatGPPComplete");
local popupstr = "[COLOR:255:87:155:255]+".. math.floor(gp/100).."[ENDCOLOR] [ICON_GREAT_PEOPLE] " ..TextType..Fin.." "..name.."." ;
Events.AddPopupTextEvent(HexToWorld(HexPos),popupstr, 2);
end

GameEvents.UnitKilledInCombat.Add(AwardGreatPersonPoint); --Now works in quick combat mode.
The modifications I made are only slight, as you can see by the --'d out segments. Some of this stuff is still over my head, so if the problem itself is not a .dll, and in the code, I wouldn't be able to tell either way.
 
Ah, yes. I didn't consider adding that since I couldn't locate such a file among those in the MicroWorld's folder, so I initially decided against adding it. Turns out I needed that and one other file to make it work correctly (one: ModTools.lua). It works perfectly now.

The only thing left that I need help with is making it so it only works for a single Civilization (in this case: America).
 
Back
Top Bottom