Changing UA based on state of war/peace?

A_Wandering_Man

Chieftain
Joined
Oct 31, 2019
Messages
47
Location
Canada
Hi all,
So for my next mod I'm looking to make, I want to make the UA have a static part (this will be trivial, as it is part of the existing tables, so will just have to turn it on) and a dynamic part that will rely on switching between two dummy policies. In my previous mod, I used a "listener" to see when a particular building was constructed, after which the lua triggered a dummy policy to be implemented. The policy side seems easy enough.

I did some digging and found that these two lua hooks exist which seem to be close to what I'm looking for: "GameEvents.MakePeace" and "GameEvents.DeclareWar". However, I immediately thought of an issue: If I make peace with one civ while still being at war with another, the policy would still switch to the peace version (presumably, anyway).

Is there something I can use that is a global check for being at war? Is it "Team.IsAtWar"? Presumably then I could code something in Lua to use "GameEvents.MakePeace", check if they're at war with anyone else, and then change the policy to the peace one, else end?

Would something like this rough outline work? I'm sure there's plenty of stuff wrong here (missing defines and suchlike), but am I on the right track?

Code:
--called to set peace policy
function SetPeacePolicy()
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local pPlayer = Players[iPlayerLoop]
        if (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
            pPlayer:SetNumFreePolicies(1)
            pPlayer:SetNumFreePolicies(0)
            pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, true)
        end
    end
end
-- for loading saves
if (Modding.OpenSaveData().GetValue("HyrulePolicyCheck") == nil) then
    if pActiveTeam.IsAtWar() == 1 then
       SetPeacePolicy()
       SetWarPolicy()
       Modding.OpenSaveData().SetValue("HyrulePolicyCheck", 1)
   end
   else
   SetPeacePolicy()
    Modding.OpenSaveData().SetValue("HyrulePolicyCheck", 1)
end
-- called to set war policy
function SetWarPolicy()
   for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
       local pPlayer = Players[iPlayerLoop]
       if (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
       pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, false)
       pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_WAR_POLICY, true)
       end
   end
end
GameEvents.DeclareWar(teamId,otherTeamId).Add(SetWarPolicy)
--checks if player is at war with anyone else when peace is declared, if not, then sets peace policy
function CheckForOngoingWars()
local bWar = pActiveTeam:IsAtWar(iTeam)
if bWar == 1 then
end
else SetPeacePolicy()
end
GameEvents.MakePeace(teamId,otherTeamId).Add(CheckForOngoingWars)
 
Last edited:
Code:
function IsPlayerAtWar(iPlayer)
	local pOurPlayer = Players[iPlayer]
	local iOurTeam = pOurPlayer:GetTeam()
	local pOurTeam = Teams[iOurTeam]
	for iOtherPlayer = 0, GameDefines.MAX_PLAYERS - 2 do
		if iOtherPlayer ~= iPlayer then
			local pOtherPlayer = Players[iOtherPlayer]
			if pOtherPlayer:IsAlive() then
				local iOtherTeam = pOtherPlayer:GetTeam()
				if pOurTeam:IsAtWar(iOtherTeam) then
					return true
				end
			end
		end
	end
	return false
end
Also, you shouldn't need to store anything in the Modding.OpenSaveData() system. The status of the game itself should provide all the info you need when a saved game is reloaded. And even though a policy may be a dummy it is still a valid policy and can be checked as to whether or not a given player has that policy.
 
Code:
function IsPlayerAtWar(iPlayer)
    local pOurPlayer = Players[iPlayer]
    local iOurTeam = pOurPlayer:GetTeam()
    local pOurTeam = Teams[iOurTeam]
    for iOtherPlayer = 0, GameDefines.MAX_PLAYERS - 2 do
        if iOtherPlayer ~= iPlayer then
            local pOtherPlayer = Players[iOtherPlayer]
            if pOtherPlayer:IsAlive() then
                local iOtherTeam = pOtherPlayer:GetTeam()
                if pOurTeam:IsAtWar(iOtherTeam) then
                    return true
                end
            end
        end
    end
    return false
end

Is this what I need for my "CheckForOngoingWars" to work, using the "return true" to end and the return false telling it to switch (i.e., can I do "if IsPlayerAtWar ==1 then end, else set peace policy, as above?) Does the rest look OK for setting the policies?

Also, you shouldn't need to store anything in the Modding.OpenSaveData() system. The status of the game itself should provide all the info you need when a saved game is reloaded. And even though a policy may be a dummy it is still a valid policy and can be checked as to whether or not a given player has that policy.

Noted! Thanks.

Lastly, is there something like "on game start" where I can call the function to set the initial peace policy?

Edit to add: Is there a simple way to make a notification appear to let the player know the UA has switched over? I know the Ayyubids mod has a notification pop up when its UA triggers...
 
Last edited:
Is SequenceGameInitComplete the Lua hook I'd need to use to set the initial peace policy? (Thereafter I can just call the function itself in the Lua and not worry about the hook, yes?)

Does this look OK?
Code:
--called to set peace policy
function SetPeacePolicy()
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local pPlayer = Players[iPlayerLoop]
        if (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
            pPlayer:SetNumFreePolicies(1)
            pPlayer:SetNumFreePolicies(0)
            pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, true)
        end
    end
end
SequenceGameInitComplete.Add(SetPeacePolicy)
-- called to set war policy
function SetWarPolicy()
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local pPlayer = Players[iPlayerLoop]
        if (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
        pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, false)
        pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_WAR_POLICY, true)
        end
    end
end
GameEvents.DeclareWar(teamId,otherTeamId).Add(SetWarPolicy)
--checks if player is at war with anyone else when peace is declared, if not, then sets peace policy
function IsPlayerAtWar(iPlayer)
    local pOurPlayer = Players[iPlayer]
    local iOurTeam = pOurPlayer:GetTeam()
    local pOurTeam = Teams[iOurTeam]
    for iOtherPlayer = 0, GameDefines.MAX_PLAYERS - 2 do
        if iOtherPlayer ~= iPlayer then
            local pOtherPlayer = Players[iOtherPlayer]
            if pOtherPlayer:IsAlive() then
                local iOtherTeam = pOtherPlayer:GetTeam()
                if pOurTeam:IsAtWar(iOtherTeam) then
                    return true
                end
            end
        end
    end
    return false
end
if IsPlayerAtWar() == 1 then
end
else SetPeacePolicy()
end
GameEvents.MakePeace(teamId,otherTeamId).Add(IsPlayerAtWar)
 
Code:
function SetWarPolicy(teamId,otherTeamId)
    -- stuff
end
GameEvents.DeclareWar.Add(SetWarPolicy)
Instead of
Code:
function SetWarPolicy()
    -- stuff
end
GameEvents.DeclareWar(teamId,otherTeamId).Add(SetWarPolicy)
Documentation that shows like the following is indicating the argument data that is passed from a game hook to any function assigned to that game hook:
Code:
GameEvents.DeclareWar(teamId,otherTeamId)
In this case any function that is assigned to that game hook will be sent two argument parameters. The first of these will be a Team ID# for one of the teams involved in the war declaration, and the second will be the other Team ID# involved in the war declaration.

The two arguments are better thought of as
Code:
GameEvents.DeclareWar(AttackingTeamId,DefendingTeamId)
The function that is assigned as a listener to the game hook event needs to list the two arguments.

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

Game Hook Event "GameEvents.DeclareWar" sends two arguments that are Team ID #'s because teams declare and make peace -- individual players do not declare war or make peace. In 90%+ of all games there is only one player on a team, so it might seem like players declare war and peace, but they do not. In order for an lua function to operate correctly, the player ID# that belong to a team must first be extracted from the Team ID # in ordeer to properly execute any lua code for players who may or may not be members of a team that is involved in declaring a war, or making peace, or researching a technology, etc. In order to extract whether or not a given player is a member of a given team we can write a loop that examines which players are part of which team, and if that player is part of the team involved in the war declaration, take the action needed:
Code:
function SetWarPolicy(teamId,otherTeamId)
	for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
		local pPlayer = Players[iPlayerLoop]
		if (pPlayer:GetTeam() == teamId) or (pPlayer:GetTeam() == otherTeamId) then
			if (pPlayer:IsAlive() == true) and (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
				if (pPlayer:HasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY) == true) then
					pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, false)
					pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_WAR_POLICY, true)
				end
			end
		end
	end
end
GameEvents.DeclareWar.Add(SetWarPolicy)

This part of your code is also not going to work properly:
Code:
if IsPlayerAtWar() == 1 then
end
else SetPeacePolicy()
end
  1. "else" must be contained within an "if" chunk. You have it dangling by itself. An "if .. then .. else … end" code chunk must be written like this:
    Code:
    if Something then
         --what we do when "Something" condition is met
    else
         --what we do whenever the "Something" condition is not met
    end
  2. The toolkit function I passed along to you requires a valid player ID# passed to it on the "call" line
  3. The toolkit function I passed along to you returns boolean true/false. Integer 1 is not the same as boolean true in lua
  4. The code I quoted will be executed when the game loads and will result in a runtime error but nothing else ever. It is not part of the code that will execute from
    Code:
    GameEvents.MakePeace(teamId,otherTeamId).Add(IsPlayerAtWar)
  5. Your SetPeacePolicy() function as written is going to give a free policy to the CIVILIZATION_NCLJ_HYRULE player every time anyone makes peace
  6. This is incorrect because "SequenceGameInitComplete" is a hook event of a different sort than GameEvents but it still needs its proper event-type:
    Code:
    SequenceGameInitComplete.Add(SetPeacePolicy)
    You need:
    Code:
    Events.SequenceGameInitComplete.Add(SetPeacePolicy)
  7. Any function that is assigned to "SequenceGameInitComplete" really ought not unless great care is taken in crafting the code to be used at any other time.
So in order to deal with the "Start New Game" issue as opposed to peace being declared mid-game, you need two different functions, really. So, for new game setup redraft as:
Code:
function GameSetUp()
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local pPlayer = Players[iPlayerLoop]
        if (pPlayer:IsAlive() == true) and (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
            pPlayer:SetNumFreePolicies(1)
            pPlayer:SetNumFreePolicies(0)
            pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, true)
        end
    end
end
Events.SequenceGameInitComplete.Add(GameSetUp)
An additional function is needed for when peace is declared mid-game, and your pre-existing SetPeacePolicy() can be used as the templpate for that, with alterations:
Code:
-- ==================================================================
-- function IsPlayerAtWar(iPlayer) returns true/false for whether a given player
--	is at war with anyone except the Barbarian player because everyone is always
--	"at war" with the Barbarians.
-- this is a "toolkit function" that can streamline the code we need elsewhere
-- ==================================================================

function IsPlayerAtWar(iPlayer)
    local pOurPlayer = Players[iPlayer]
    local iOurTeam = pOurPlayer:GetTeam()
    local pOurTeam = Teams[iOurTeam]
    for iOtherPlayer = 0, GameDefines.MAX_PLAYERS - 2 do
        if iOtherPlayer ~= iPlayer then
            local pOtherPlayer = Players[iOtherPlayer]
            if pOtherPlayer:IsAlive() then
                local iOtherTeam = pOtherPlayer:GetTeam()
                if pOurTeam:IsAtWar(iOtherTeam) then
                    return true
                end
            end
        end
    end
    return false
end

-- ==================================================================

--called to set peace policy mid-game
function SetPeacePolicy(teamId,otherTeamId)
	for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
		local pPlayer = Players[iPlayerLoop]
		if (pPlayer:GetTeam() == teamId) or (pPlayer:GetTeam() == otherTeamId) then
			if (pPlayer:IsAlive() == true) and (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
				if (pPlayer:HasPolicy(GameInfoTypes.NCLJ_HYRULE_WAR_POLICY) == true) and (IsPlayerAtWar(iPlayerLoop) == false) then
					pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_WAR_POLICY, false)
					pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, true)
				end
			end
		end
	end
end
GameEvents.MakePeace.Add(SetPeacePolicy)
So to frankenstein it all together:
Code:
-- ==================================================================
-- function IsPlayerAtWar(iPlayer) returns true/false for whether a given player
--	is at war with anyone except the Barbarian player because everyone is always
--	"at war" with the Barbarians.
-- this is a "toolkit function" that can streamline the code we need elsewhere
-- ==================================================================

function IsPlayerAtWar(iPlayer)
    local pOurPlayer = Players[iPlayer]
    local iOurTeam = pOurPlayer:GetTeam()
    local pOurTeam = Teams[iOurTeam]
    for iOtherPlayer = 0, GameDefines.MAX_PLAYERS - 2 do
        if iOtherPlayer ~= iPlayer then
            local pOtherPlayer = Players[iOtherPlayer]
            if pOtherPlayer:IsAlive() then
                local iOtherTeam = pOtherPlayer:GetTeam()
                if pOurTeam:IsAtWar(iOtherTeam) then
                    return true
                end
            end
        end
    end
    return false
end

-- ==================================================================

function GameSetUp()
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local pPlayer = Players[iPlayerLoop]
        if  (pPlayer:IsAlive() == true) and (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
            pPlayer:SetNumFreePolicies(1)
            pPlayer:SetNumFreePolicies(0)
            pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, true)
        end
    end
end
Events.SequenceGameInitComplete.Add(GameSetUp)

-- ==================================================================

function SetWarPolicy(teamId,otherTeamId)
	for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
		local pPlayer = Players[iPlayerLoop]
		if (pPlayer:GetTeam() == teamId) or (pPlayer:GetTeam() == otherTeamId) then
			if (pPlayer:IsAlive() == true) and (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
				if (pPlayer:HasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY) == true) then
					pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, false)
					pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_WAR_POLICY, true)
				end
			end
		end
	end
end
GameEvents.DeclareWar.Add(SetWarPolicy)

-- ==================================================================

--called to set peace policy mid-game
function SetPeacePolicy(teamId,otherTeamId)
	for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
		local pPlayer = Players[iPlayerLoop]
		if (pPlayer:GetTeam() == teamId) or (pPlayer:GetTeam() == otherTeamId) then
			if (pPlayer:IsAlive() == true) and (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_NCLJ_HYRULE) then
				if (pPlayer:HasPolicy(GameInfoTypes.NCLJ_HYRULE_WAR_POLICY) == true) and (IsPlayerAtWar(iPlayerLoop) == false) then
					pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_WAR_POLICY, false)
					pPlayer:SetHasPolicy(GameInfoTypes.NCLJ_HYRULE_PEACE_POLICY, true)
				end
			end
		end
	end
end
GameEvents.MakePeace.Add(SetPeacePolicy)
 
Last edited:
Thank you so much, Lee! I am very much not familiar with coding, in general, and your help has been invaluable.
 
Top Bottom