1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Changing UA based on state of war/peace?

Discussion in 'Civ5 - Creation & Customization' started by A_Wandering_Man, Feb 23, 2020.

  1. A_Wandering_Man

    A_Wandering_Man Chieftain

    Joined:
    Oct 31, 2019
    Messages:
    25
    Gender:
    Male
    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: Feb 23, 2020
  2. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,753
    Location:
    Illinois, USA
    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.
     
  3. A_Wandering_Man

    A_Wandering_Man Chieftain

    Joined:
    Oct 31, 2019
    Messages:
    25
    Gender:
    Male
    Location:
    Canada
    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?

    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: Feb 24, 2020
  4. A_Wandering_Man

    A_Wandering_Man Chieftain

    Joined:
    Oct 31, 2019
    Messages:
    25
    Gender:
    Male
    Location:
    Canada
    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)
    
     
  5. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,753
    Location:
    Illinois, USA
    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: Mar 8, 2020
  6. A_Wandering_Man

    A_Wandering_Man Chieftain

    Joined:
    Oct 31, 2019
    Messages:
    25
    Gender:
    Male
    Location:
    Canada
    Thank you so much, Lee! I am very much not familiar with coding, in general, and your help has been invaluable.
     

Share This Page