Help resolving this paradox.

JFD

Kathigitarkh
Joined
Oct 19, 2010
Messages
9,132
Location
The Kingdom of New Zealand
So my latest problem is this: I want to send a notification to the player when a policy has just been disabled. The problem is that - if this policy has been disabled, how can I check if the player ever had this policy so that I might send the notification? An example is thus:

The Player has the policy Slavery. If the Player adopts the Freedom or Order Ideologies, the Slavery Policy is disabled. This part works fine. But then I would like a notification to be sent to remind the Player that this would occur. The HasPolicy conditional doesn't work, because if the Player has just adopted one of those Ideologies, they no longer "has" that policy. So is there any way around this, or, rather, another way I can check if the player had the policy in question?

Also, this event: PlayerAdoptPolicyBranch - does not seem to work. Can someone confirm? It's not listed on the wiki, but it is in the patchnotes of one of the older patches. Thanks.

EDIT: It looks like SetHasPolicy allows for the removal of policies as well, so I will test that and report back.

Indeed, that works, but then the Player can just re-adopt Slavery; so that's no good. Giving a dummy policy seems to work; and then removing that dummy if the player adopts the opposing Ideology later. But if anyone has any betters ideas, or any better ideas that checking for any of the level 1 policies for the relevant ideologies, please let me know.
 
There is a method IsPolicyBlocked(). Presumably, you entered "blocked policy branch" column for slavery when you created it, so you can find that by looping through the policies table when you select the branch.

Code:
-- game event called whatever when we adopt a policy branch
-- i assume policybranch ID is passed through somehow

condition = "PolicyBranchDisable = '" .. GameInfo.PolicyBranchTypes[iPolicyBranchID].Type .. "'";
for row in Policies(conditions) do
    if pPlayer:IsPolicyBlocked(row.ID) then
        -- row.ID i.e. "Slavery" is blocked
        -- send notification
    end
end

If you forcibly disabled it through Lua, you're gonna have a hell of a time. Do it through XML. Easier. And if you didn't block it but disable it...good luck!
 
What I have done is give the player a dummy policy when they adopt Freedom/Order, which disables Slavery via the XML tag: Policy_Disables. That seems to work without problems; am I wrong? I don't really understand how to implement what you're proposing. I thought IsBlocked() could only be used to check if a policy is blocked or not, not to actually block it.

Spoiler :

Code:
function OnPlayerAdoptIdeology(playerID, policyID)
	local player = Players[playerID]

	if (policyID == GameInfo.Policies["POLICY_CREATIVE_EXPRESSION"].ID or policyID == GameInfo.Policies["POLICY_OPEN_SOCIETY"].ID or policyID == GameInfo.Policies["POLICY_CIVIL_SOCIETY"].ID or policyID == GameInfo.Policies["POLICY_COVERT_ACTION"].ID or policyID == GameInfo.Policies["POLICY_CAPITALISM"].ID or policyID == GameInfo.Policies["POLICY_ECONOMIC_UNION"].ID or policyID == GameInfo.Policies["POLICY_UNIVERSAL_HEALTHCARE_F"].ID or policyID == GameInfo.Policies["POLICY_HERO_OF_THE_PEOPLE"].ID or policyID == GameInfo.Policies["POLICY_SOCIALIST_REALISM"].ID or policyID == GameInfo.Policies["POLICY_SKYSCRAPERS"].ID or policyID == GameInfo.Policies["POLICY_PATRIOTIC_WAR"].ID or policyID == GameInfo.Policies["POLICY_DOUBLE_AGENTS"].ID or policyID == GameInfo.Policies["POLICY_YOUNG_PIONEERS"].ID or policyID == GameInfo.Policies["POLICY_UNIVERSAL_HEALTHCARE_O"].ID) then
	print("Player has adopted Freedom or Order")
		if (player:IsHuman() and not player:HasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID)) then
		FreeSlaves(player)
		Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "Your slaves have been emancipated!");
		player:SetHasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID, true)
		elseif (player:HasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID) and not player:IsHuman()) then
		FreeSlaves(player)
		Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "A foreign civilization has emancipated their slaves!");
		player:SetHasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID, true)
		end
	elseif (policyID == GameInfo.Policies["POLICY_FORTIFIED_BORDERS"].ID or policyID == GameInfo.Policies["POLICY_INDUSTRIAL_ESPIONAGE"].ID or policyID == GameInfo.Policies["POLICY_FUTURISM"].ID or policyID == GameInfo.Policies["POLICY_UNITED_FRONT"].ID or policyID == GameInfo.Policies["POLICY_MOBILIZATION"].ID or policyID == GameInfo.Policies["POLICY_ELITE_FORCES"].ID or policyID == GameInfo.Policies["POLICY_UNIVERSAL_HEALTHCARE_A"].ID) then
		if player:HasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID) then
		player:SetHasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID, false)
		end
	end
end
GameEvents.PlayerAdoptPolicy.Add(OnPlayerAdoptIdeology)


Spoiler :

Code:
<Policy_Disables>
	<Row>
		<PolicyType>POLICY_DUMMY_ABOLISH_SLAVERY</PolicyType>
		<PolicyDisable>POLICY_MIGHT_SLAVERY</PolicyDisable>
	</Row>
</Policy_Disables>
 
Sorry, for the moment I just rewrote that code to make it easier to work with. Give me a few to think about this.

this is the rewritten code...don't you think it's easier to read? And less hardcodey. :)
Code:
function OnPlayerAdoptIdeology(playerID, policyID)
	local player = Players[playerID]

	local policyBranch = GameInfo.Policies[policyID].PolicyBranchType;
	local policyLevel = GameInfo.Policies[policyID].Level;

	-- Freedom/Order
	if ((policyBranch == "POLICY_BRANCH_FREEDOM" or policyBranch == "POLICY_BRANCH_ORDER") and policyLevel == 1) then
		print("Player has adopted Freedom or Order")
		
		-- Human player
		if player:IsHuman() then
			if (not player:HasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID)) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "Your slaves have been emancipated!");
				player:SetHasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID, true);
			end
		-- AI player
		else
			if(player:HasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID)) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "A foreign civilization has emancipated their slaves!");
				player:SetHasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID, true);
			end
		end
	
	-- Player has adopted Autocracy
	else
		if policyLevel == 1 then
			-- Dummy policy
			if player:HasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID) then
				player:SetHasPolicy(GameInfo.Policies["POLICY_DUMMY_ABOLISH_SLAVERY"].ID, false)
			end
		end
	end
end
GameEvents.PlayerAdoptPolicy.Add(OnPlayerAdoptIdeology)
I will hopefully come up with an answer within the hour...

Edit what I was proposing was to add a new column to Policies table called PolicyBranchDisable or something. Basically, if you adopt that branch, that policy gets disabled. But in actuality you probably have to create a new table entirely called Policy_PolicyBranchDisabled or something, which is super complicated and not worth it. And I also forgot that blocking policies is actually policy branch level stuff, not what you're looking for.

Why not just go with the dummy approach...it seems to work just fine. If it's not broke, don't fix it. :P

The truth is Lua is limiting and trying to find loopholes around everything just sucks. :( You can't block individual policies of course through Lua, I should know that, so my original thought would not work.
 
Yes, that is easier to read. Thank you. Please take your time; I really appreciate your help. And if it's any help to you, this is the (working) FreeSlaves function.

Spoiler :

Code:
function FreeSlaves(player)
	for pUnit in player:Units() do
		local unitID = pUnit:GetUnitType()
			if (pUnit ~= nil) then
			local unitID = pUnit:GetUnitType()
			local unitPlot = pUnit:GetPlot()
			local unitX = unitPlot:GetX()
			local unitY = unitPlot:GetY()

			if (unitID == nil) then
				print("Error: Invalid UnitID")
				break
			end

			if (unitID == GameInfoTypes.UNIT_SLAVE) then
				pUnit:Kill()
				player:InitUnit(GameInfoTypes.UNIT_WORKER, unitX, unitY)
				print("Slaves freed")
			end
			end
		end
end
 
I'm kind of confused, actually. Why wouldn't HasPolicy("POLICY_MIGHT_SLAVERY") work? Where is the code that disables it? Can't you, before you disable the policy, just assign it to a variable bHadSlavery = player:HasPolicy("POLICY_MIGHT_SLAVERY"); Then disable it, then just run a simple if bHadSlavery check?

Like this?

Code:
function OnPlayerAdoptIdeology(playerID, policyID)
	local player = Players[playerID]

	local policyBranch = GameInfo.Policies[policyID].PolicyBranchType;
	local policyLevel = GameInfo.Policies[policyID].Level;

	local bHadPolicy = player:HasPolicy("POLICY_MIGHT_SLAVERY");

	-- Freedom/Order
	if ((policyBranch == "POLICY_BRANCH_FREEDOM" or policyBranch == "POLICY_BRANCH_ORDER") and policyLevel == 1) then
		print("Player has adopted Freedom or Order")
		
		-- Human player
		if player:IsHuman() then
			if (bHadPolicy) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "Your slaves have been emancipated!");
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, false);
			end
		-- AI player
		else
			if(not bHadPolicy) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "A foreign civilization has emancipated their slaves!");
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, false);
			end
		end
	
	-- Player has adopted Autocracy
	else
		if policyLevel == 1 then
			-- Dummy policy
			if not bHadPolicy then
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, true)
			end
		end
	end
end
GameEvents.PlayerAdoptPolicy.Add(OnPlayerAdoptIdeology)
 
HasPolicy doesn't work because when you adopt Freedom/Order the policy is disabled (or, rather, the policy is removed from the player), thus you don't have it and returning HasPolicy fails - this is what happened in testing, at least. The disabling part is done through xml, so I don't know about the code - the table is unused, but functions.

I probably could assign it a variable, but I'm not sure how.
 
HasPolicy doesn't work because when you adopt Freedom/Order the policy is disabled, thus you don't have it and returning HasPolicy fails - this is what happened in testing, at least. The disabling part is done through xml, so I don't know about the code - the table is unused, but functions.

I probably could assign it a variable, but I'm not sure how.

Post that specific XML please.
 
This is the table definition:

Spoiler :

Code:
<Table name="Policy_Disables">
	<Column name="PolicyType" type="text" reference="Policies(Type)"/>
	<Column name="PolicyDisable" type="text" reference="Policies(Type)"/>
</Table>


And this is how I've used it:

Spoiler :

Code:
<Policy_Disables>
	<Row>
	         <PolicyType>POLICY_DUMMY_ABOLISH_MANDATORY_VOLUNTEERISM</PolicyType>
		<PolicyDisable>POLICY_MIGHT_MANDATORY_VOLUNTEERISM</PolicyDisable>
	</Row>
	<Row>
		<PolicyType>POLICY_DUMMY_ABOLISH_SLAVERY</PolicyType>
		<PolicyDisable>POLICY_MIGHT_SLAVERY</PolicyDisable>
	</Row>
</Policy_Disables>


The only other thread that talks about this table is a thread about the release of Civ V from 2010 and discusses what it does hypothetically, rather than in any practice.
 
Why wouldn't the code I just posted do, then? Remove those lines of XML, or keep them, they'll be deprecated using this method.

Just disable the policy when you send the notification. This should work. The code, again.

Code:
function OnPlayerAdoptIdeology(playerID, policyID)
	local player = Players[playerID]

	local policyBranch = GameInfo.Policies[policyID].PolicyBranchType;
	local policyLevel = GameInfo.Policies[policyID].Level;

	local bHadPolicy = player:HasPolicy("POLICY_MIGHT_SLAVERY");

	-- Freedom/Order
	if ((policyBranch == "POLICY_BRANCH_FREEDOM" or policyBranch == "POLICY_BRANCH_ORDER") and policyLevel == 1) then
		print("Player has adopted Freedom or Order")
		
		-- Human player
		if player:IsHuman() then
			if (bHadPolicy) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "Your slaves have been emancipated!");
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, false);
			end
		-- AI player
		else
			if(not bHadPolicy) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "A foreign civilization has emancipated their slaves!");
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, false);
			end
		end
	
	-- Player has adopted Autocracy
	else
		if policyLevel == 1 then
			-- Dummy policy
			if not bHadPolicy then
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, true)
			end
		end
	end
end
GameEvents.PlayerAdoptPolicy.Add(OnPlayerAdoptIdeology)

Does this not work for some reason?

edit: fyi I think this is slightly wrong. You should disable the policies separately from checking if they're human. Will update again.

How does this look? I have a problem with the autocracy check...

Code:
function OnPlayerAdoptIdeology(playerID, policyID)
	local player = Players[playerID]

	local policyBranch = GameInfo.Policies[policyID].PolicyBranchType;
	local policyLevel = GameInfo.Policies[policyID].Level;

	local bHadPolicy = player:HasPolicy("POLICY_MIGHT_SLAVERY");

	-- Freedom/Order
	if ((policyBranch == "POLICY_BRANCH_FREEDOM" or policyBranch == "POLICY_BRANCH_ORDER") and policyLevel == 1) then
		print("Player has adopted Freedom or Order")
		
		if bHadPolicy then
			-- Disable Slavery if we had it
			player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, false);
		
			if(player:IsHuman()) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "Your slaves have been emancipated!");
			else
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "A foreign civilization has emancipated their slaves!");
			end
		end
		
	-- Player has adopted Autocracy
	else
		-- Only run for Tier 1 tenets
		if policyLevel == 1 then
			-- If we didn't have the policy then enable it again
			-- this is troublesome...
			if not bHadPolicy then
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, true)
			end
		end
	end
end
GameEvents.PlayerAdoptPolicy.Add(OnPlayerAdoptIdeology)

The Autocracy check...you are trying to re-enable the policy if the player ever had it? The only way I can see this working if by saving the variable externally to the player, and then allowing it to be saved/loaded with the file. I know a method to do this.

last edit: I think I can get what you want saved/loaded...but I'm not 100% on that...lol.
 
The reason that doesn't work is because the SetHasPolicy... false doesn't disable it, which means the Player can just readopt Slavery, even if they still have Freedom/Order. That's why I used a dummy policy, which disabled Slavery, rather than removed it from the player; because then Slavery cannot be readopted unless the player switches to Autocracy.

Sorry for the slow reply - I had to test your code and try and remember my reasoning for the dummy policy.

The Autocracy part only makes sense with the dummy policy, as that part was used to allow Autocrats to adopt Slavery. Basically, if the player had had the Freedom/Order dummy policy (which they would get if they had adopted Freedom/Order in the first place), it removed it, which reenabled the ability to adopt Slavery again. It doesn't give Slavery if the Player previously had it.
 
This question must be answered: Is there a game event PlayerCanAdoptPolicy()? If so then I believe I have the answer that will use save/loads variables without a dummy policy. But if they deprecated PlayerCanAdoptPolicy then I can't answer.

(in the source code there is)

Code:
ICvEngineScriptSystem1* pkScriptSystem = gDLL->GetScriptSystem();
	if(pkScriptSystem)
	{
		CvLuaArgsHandle args;
		args->Push(m_pPlayer->GetID());
		args->Push(eIndex);

		// Attempt to execute the game events.
		// Will return false if there are no registered listeners.
		bool bResult = false;
		if(LuaSupport::CallTestAll(pkScriptSystem, "PlayerCanAdoptPolicy", args.get(), bResult))
		{
			// Check the result.
			if(bResult == false)
			{
				return false;
			}
		}
	}

So I assume one exists.

This is what I have so far, I'm not 100% sure it works....

Code:
function SavePlayerSlaveryVariable(playerID, bValue)
	local savedData = Modding.OpenSaveData();
	
	-- Assign the persistent property bValue to HadSlaveryData_ID where ID could be 1, 2, 3, etc.
	savedData.SetValue("HadSlaveryData_" .. tostring(playerID), bValue);

end

function GetPlayerSlaveryVariable(playerID)
	local savedData = Modding.OpenSaveData();
	
	return savedData.GetValue("HadSlaveryData_" .. tostring(playerID));
end

function OnPlayerAdoptIdeology(playerID, policyID)
	local player = Players[playerID]

	local policyBranch = GameInfo.Policies[policyID].PolicyBranchType;
	local policyLevel = GameInfo.Policies[policyID].Level;

	local bHadPolicy = player:HasPolicy("POLICY_MIGHT_SLAVERY");

	-- Freedom/Order
	if ((policyBranch == "POLICY_BRANCH_FREEDOM" or policyBranch == "POLICY_BRANCH_ORDER") and policyLevel == 1) then
		print("Player has adopted Freedom or Order")
		
		if bHadPolicy then
			-- If we got here we're disabling slavery. Therefore, set that this player has slavery
			SavePlayerSlaveryVariable(playerID, bHadPolicy);
			
			-- Disable Slavery if we had it
			player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, false);
		
			if(player:IsHuman()) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "Your slaves have been emancipated!");
			else
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "A foreign civilization has emancipated their slaves!");
			end
		end
		
	-- Player has adopted Autocracy
	else
		-- Only run for Tier 1 tenets
		if policyLevel == 1 then
			-- If we had slavery and adopted autocracy
			if GetPlayerSlaveryVariable(playerID) then
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, true)
			end
		end
	end
end
GameEvents.PlayerAdoptPolicy.Add(OnPlayerAdoptIdeology)
 
That game event does exist, but SetHasPolicy doesn't actually disable the policy, it just removes it.

If that event does exist then just add

Code:
GameEvents.PlayerCanAdoptPolicy(function (iPlayerID, iPolicyID)
     if GameInfo.Policies[iPolicyID].Type == "POLICY_MIGHT_SLAVERY" then
		-- if we ever had slavery, but it got disabled
		 if GetPlayerSlaveryVariable(iPlayerID) then
			return false;
		 end
	 end
end);

If my two Set and Get functions work, then this code should work.

Untested.

Everything together...

Code:
function SavePlayerSlaveryVariable(playerID, bValue)
	local savedData = Modding.OpenSaveData();
	
	-- Assign the persistent property bValue to HadSlaveryData_ID where ID could be 1, 2, 3, etc.
	savedData.SetValue("HadSlaveryData_" .. tostring(playerID), bValue);

end

function GetPlayerSlaveryVariable(playerID)
	local savedData = Modding.OpenSaveData();
	
	return savedData.GetValue("HadSlaveryData_" .. tostring(playerID));
end

function OnPlayerAdoptIdeology(playerID, policyID)
	local player = Players[playerID]

	local policyBranch = GameInfo.Policies[policyID].PolicyBranchType;
	local policyLevel = GameInfo.Policies[policyID].Level;

	local bHadPolicy = player:HasPolicy("POLICY_MIGHT_SLAVERY");

	-- Freedom/Order
	if ((policyBranch == "POLICY_BRANCH_FREEDOM" or policyBranch == "POLICY_BRANCH_ORDER") and policyLevel == 1) then
		print("Player has adopted Freedom or Order")
		
		if bHadPolicy then
			-- If we got here we're disabling slavery. Therefore, set that this player has slavery
			SetPlayerSlaveryVariable(playerID, bHadPolicy);
			
			-- Disable Slavery if we had it
			player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, false);
		
			if(player:IsHuman()) then
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "Your slaves have been emancipated!");
			else
				FreeSlaves(player)
				Players[Game.GetActivePlayer()]:AddNotification(NotificationTypes.NOTIFICATION_GENERIC, "A foreign civilization has emancipated their slaves!");
			end
		end
		
	-- Player has adopted Autocracy
	else
		-- Only run for Tier 1 tenets
		if policyLevel == 1 then
			-- If we had slavery and adopted autocracy
			if GetPlayerSlaveryVariable(playerID) then
				player:SetHasPolicy(GameInfo.Policies["POLICY_MIGHT_SLAVERY"].ID, true);
				
				SavePlayerSlaveryVariable(playerID, false);
			end
		end
	end
end
GameEvents.PlayerAdoptPolicy.Add(OnPlayerAdoptIdeology)

GameEvents.PlayerCanAdoptPolicy(function (iPlayerID, iPolicyID)
     if GameInfo.Policies[iPolicyID].Type == "POLICY_MIGHT_SLAVERY" then
		-- if we ever had slavery, but it got disabled
		 if GetPlayerSlaveryVariable(iPlayerID) then
			return false;
		 end
	 end
end);

I hope it works....
 
I'm afraid it didn't work (i.e. no notification, not disabling of Slavery and no removal of the adoption of slavery either). Wouldn't it be easier if I just returned to how I was doing it, but tidied up a bit, as per your suggestions earlier?
 
I'm afraid it didn't work (i.e. no notification, not disabling of Slavery and no removal of the adoption of slavery either). Wouldn't it be easier if I just returned to how I was doing it, but tidied up a bit, as per your suggestions earlier?

Sure...

I'm sure there's a runtime in there somewhere a few print statements couldn't sniff out but it's 1:30 in the morning. Not gonna bother tonight.
 
Back
Top Bottom