Please help with lua trait for Anno Domini

Rob (R8XFT)

Ancient Briton
Retired Moderator
Joined
Aug 11, 2002
Messages
10,866
Location
Leeds (UK)
At the moment, I've given the Mitanni the Bismark_Realpolitik that FramedArchitecture did, namely giving culture equivalent to the surplus happiness when at war and production in the capital equivalent to the surplus happiness during peacetime. It doesn't quite do what I want from a Mitanni UA. Could anyone arrange this for me please, which is similar:

When at war, gain +2 culture for every city in your empire; during peacetime, gain +1 production in the capital for every city in your empire.
 
pPlayer:GetNumCities() will return the number of cities in the empire, so you'll just need to replace the bits of code that determine surplus happiness with (something like) that
 
However, how do I give 2 culture for every city in the empire or 1 production in the capital for every city in the empire?

I take it numfounded is the number of cities I have founded, which might differ from the number of cities I currently have?
 
NumFounded is (IIRC) the ones you have ever settled, NumCities is the number in the empire at the moment (includes puppets, excludes ones you've lost)

As to where to mke the changes, posting your current code would help a lot ;)
 
This is what I have so far. The Realpolitik value has been left in here as it points to a new entry in the traits table (which has been entered correctly via sql) - in my code if it's anything other than 0, it knows this civ has the trait.

The code below doesn't effect anything at all; I'm going for +1 production per city in the capital if at peace and +2 culture per city if at war.


-- Mitanni
-- Author: FramedArchitecture (adapted from Bismarck_Realpolitik.lua) for use with the Mitanni in Anno Domini
-- DateCreated: 6/2014
--------------------------------------------------------------------
include("UA_Functions.lua")
--------------------------------------------------------------------
local round = math.ceil
local yieldProd = YieldTypes.YIELD_PRODUCTION
local palaceclassID = GameInfo.BuildingClasses["BUILDINGCLASS_PALACE"].ID
local bFirstTurn = true
local mitanniID = -1
local mitanniTeamID = -1
--------------------------------------------------------------------
function OnPlayerDoTurn(playerID)
local player = Players[playerID]
local capital = player:GetCapitalCity()
local p_value = player:GetNumCities()
local c_value = p_value + p_value
if bWar then
player:ChangeJONSCulturePerTurnForFree(c_value)
else
capital:SetBuildingYieldChange(palaceclassID, yieldProd, round(p_value))
end
end
GameEvents.PlayerDoTurn.Add(OnPlayerDoTurn)
--------------------------------------------------------------------
function OnWarStateChanged(teamID, otherteamID, bWar)
if teamID == mitanniTeamID then
if bWar and not IsOnlylWar(teamID) then return end
if not bWar and not IsGlobalPeace(teamID) then return end
if bWar then
player:ChangeJONSCulturePerTurnForFree(c_value)
else
capital:SetBuildingYieldChange(palaceclassID, yieldProd, round(p_value))
end
end
Events.WarStateChanged.Add(OnWarStateChanged)
--------------------------------------------------------------------
function Init()
for i = 0, GameDefines.MAX_MAJOR_CIVS - 1, 1 do
local player = Players
if player and (GetTraitInfo(player).Realpolitik > 0) then
mitanniID = i
mitanniTeamID = player:GetTeam()
break;
end
end
print("mitanniUA loaded")
end
--------------------------------------------------------------------
Init();
 
There are several issues with the code
  1. It's not in [ CODE ] [ /CODE ] blocks!
  2. If more than one player has Realpolitik, the code will only apply to the last player with it. If the trait is for the Mitanni, why not just check for them directly?
  3. p_value and c_value are local to the OnPlayerDoTurn() function, so when used within the OnWarStateChanged() function they will be nil which should be causing an error in the lua.log file
  4. Similarly, bWar is a parameter of the OnWarStateChanged() function (and hence local) and so will be nil (ie false) when tested within the OnPlayerDoTurn() function, so player:ChangeJONSCulturePerTurnForFree(c_value) in that function will never be executed
  5. In the Init() function, the player variable will never be nil, but they may not be alive so the test should start "player:IsEverAlive() ..."
  6. ChangeJONSCulturePerTurnForFree() will add culture every turn, assuming the usage of bWar within OnPlayerDoTurn() is fixed, the culture will just increase ad infinitum. Assuming 3 cities, on the turn after war is declared, 3 culture will be added, the second turn 6 culture, the third turn 9 culture, until peace is declared at turn 10 (say) when 30 cultur will be being added per turn. Howvere, even when at peace 30 culture will still be added per turn, if another war is started the culture per turn will start increasing again!
  7. Attempting to counteract the above with ChangeJONSCulturePerTurnForFree() with a negative parameter runs into difficulties as it needs to be ChangeJONSCulturePerTurnForFree(-culture_added_last_turn), which requires the amount of culture added to be persisted both across turns (easy) and save games (not so easy)
  8. The usage of SetBuildingYieldChange() against the palace does assume that no other mod is also using this trick!
  9. In the same way the culture accumulates during peace once war has been declared, so production will still be granted from the palace on the outbreak of war (as there is no SetBuildingYieldChange(0) to clear it)

Issues 6, 7, 8 and 9 can be avoided by directly adding culture to the player or production to the capital

Untested in game, but should work
Code:
--------------------------------------------------------------------
local g_iMitanniPlayer = -1
local g_iMitanniTeam = -1
local g_bMitanniAtWar = false

--------------------------------------------------------------------
function OnPlayerDoTurn(iPlayer)
  -- If this is the Mitanni's turn, grant their bonus directly
  if (iPlayer == g_iMitanniPlayer) then
    local pPlayer = Players[iPlayer]
    local pCapital = pPlayer:GetCapitalCity()
    local iBoost = pPlayer:GetNumCities()

    if (g_bMitanniAtWar) then
      -- Immediate boost to culture
      pPlayer:ChangeJONSCulture(2 * iBoost)
    else
      if (pCapital:GetProduction() ~= 0) then
        -- Something is cooking, so add to it
        pCapital:ChangeProduction(iBoost)
      else
        -- Nothing being produced (it was probably completed this turn), so add to the overflow bucket
        pCapital:SetOverflowProduction(pCapital:GetOverflowProduction() + iBoost)
      end
    end
  end
end

--------------------------------------------------------------------
function OnWarStateChanged(iMyTeam, iTheirTeam, bWar)
  -- When our war state changes with a team, recompute our global war state
  if (iMyTeam == g_iMitanniTeam and bWar ~= g_bMitanniAtWar) then
    g_bMitanniAtWar = AtWarWithAnyMajor(g_iMitanniTeam)
  end
end

--------------------------------------------------------------------
function AtWarWithAnyMajor(iTeam)
  local pTeam = Teams[iTeam]

  for iOtherPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1, 1 do
    local pOtherPlayer = Players[iOtherPlayer]
    if (pOtherPlayer:IsAlive() and pTeam:IsAtWar(pOtherPlayer:GetTeam())) then
      return true
    end
  end

  return false
end

--------------------------------------------------------------------
function Init()
  -- Find the player and team IDs for the Mitanni player (if any)
  for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1, 1 do
    local pPlayer = Players[iPlayer]
    if (pPlayer:IsEverAlive()) then
      -- if (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_MITANNI) then
      if (GetTraitInfo(pPlayer).Realpolitik > 0) then
        g_iMitanniPlayer = iPlayer
        g_iMitanniTeam = pPlayer:GetTeam()
        break
      end
    end
  end

  -- If the Mitanni are in play, compute their global war state and hook the events
  if (g_iMitanniPlayer ~= -1) then
    g_bMitanniAtWar = AtWarWithAnyMajor(g_iMitanniTeam)
    GameEvents.PlayerDoTurn.Add(OnPlayerDoTurn)
    Events.WarStateChanged.Add(OnWarStateChanged)
    print("mitanniUA loaded")
  end
end

--------------------------------------------------------------------
Init()
 
Thank-you!! I'm a real novice with lua and am trying to learn from other peoples' code, so I really appreciate this. I'll get there (eventually) and will learn from this experience. The fact you've been kind enough to put in what each part of the code does is extremely useful, thank-you.


EDIT: I've now playtested and it's working for the most part. The culture increases as it should (and counts the capital city as one of the cities); however, the toolbar does notify the increase (i.e. in the playtest, I declared war when I had one city and the culture was +1 per turn; I received +3 culture and the overall figure increased by 3 in the toolbar, but it still showed +1 as the incremental step per turn). The production in the capital only seemed to increase for each city other than the capital (i.e. no effect when I had one city) and again, whilst I did get a production bonus, when looking at the city screen it implied I wasn't getting a bonus, even though production was faster (i.e. with two cities, it showed I was getting +5: 2 from terrain, 3 from buildings, but in practice, I was getting +6). Can you help with this part please?
 
The lack of UI feedback is a consequence of directly adding the culture/production at the start of the Mitanni's turn. If you really need this to show in the UI the only safe way to do it will be to create a unique hidden building in the capital to attach the yields to with SetBuildingYieldChange() with the extra complications that introduces.

With regard to the odd increase with only the capital, print out iBoost. It may be that pPlayer:GetNumCities() excludes the capital (but I'm pretty sure it doesn't), otherwise you can confirm how much production is being added by placing "print(pCapital:GetProduction())" before and after the ChangeProduction() line
 
Thanks again. The thing is, the original code I was looking at managed to update the various counters correctly without a hidden building. It seems like FramedArchitect attached the extra yields to the palace.
 
The code below doesn't effect anything at all

the original code I was looking at managed to update the various counters correctly
Contradictory statements :crazyeye:

... attached the extra yields to the palace.

Which works fine if you can guarantee that only your mod will use that trick - which you could in a TC mod, but can't in a modular one. For example, say a player is using both your Mitanni mod and another mod that applies to all civs that adds bonus hammers to the palace if any other city is connected by a railway to the capital. The two mods will conflict on their usage of the palace to add hammers and only one bonus from one of the mods will be applied.
 
Contradictory statements :crazyeye:
They're not :cooool:. "The code below" was code I'd put together after first looking at code from FramedArchitect, which is what I was referring to as "the code I was looking at".


Which works fine if you can guarantee that only your mod will use that trick - which you could in a TC mod, but can't in a modular one. For example, say a player is using both your Mitanni mod and another mod that applies to all civs that adds bonus hammers to the palace if any other city is connected by a railway to the capital. The two mods will conflict on their usage of the palace to add hammers and only one bonus from one of the mods will be applied.
Anno Domini is a TC mod, so I'm gathering that I could attach the bonuses to the palace after I've studied the code and worked out how to do it ;).
 
So, going back to basics.

If at war, add production, else add culture - so we need to watch for war/peace changing and update bonus yields accordingly

The bonus is a function of the number of cities in the empire, so we also need to watch for cities being founded (created), captured or destroyed (razed) - as these events will change the amount of bonus

We actually don't need to do anything at the start of the Mitanni's turn provided we hook the war state and city state change events

Code:
--------------------------------------------------------------------
local g_iMitanniPlayer = -1
local g_iMitanniTeam = -1
local g_bMitanniAtWar = false

local g_iPalaceClass = GameInfoTypes.BUILDINGCLASS_PALACE

--------------------------------------------------------------------
function OnCityChange(_, iPlayer, _, iNewPlayer)
  if (iPlayer == g_iMitanniPlayer or iNewPlayer == g_iMitanniPlayer) then
    UpdateMitanniBonusYields()
  end
end

--------------------------------------------------------------------
function OnWarStateChanged(iMyTeam, iTheirTeam, bWar)
  -- When our war state changes with a team, recompute our global war state
  if (iMyTeam == g_iMitanniTeam and bWar ~= g_bMitanniAtWar) then
    g_bMitanniAtWar = AtWarWithAnyMajor(g_iMitanniTeam)
    UpdateMitanniBonusYields()
  end
end

--------------------------------------------------------------------
function AtWarWithAnyMajor(iTeam)
  local pTeam = Teams[iTeam]

  for iOtherPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1, 1 do
    local pOtherPlayer = Players[iOtherPlayer]
    if (pOtherPlayer:IsAlive() and pTeam:IsAtWar(pOtherPlayer:GetTeam())) then
      return true
    end
  end

  return false
end

--------------------------------------------------------------------
function UpdateMitanniBonusYields()
  local pPlayer = Players[g_iMitanniPlayer]
  local pCapital = pPlayer:GetCapitalCity()
  local iBoost = pPlayer:GetNumCities()
  
  -- Take away any previous culture bonus
  pPlayer:ChangeJONSCulturePerTurnForFree(pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE))

  -- Update the per turn bonuses
  if (g_bMitanniAtWar) then
    pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_PRODUCTION, 0)
    -- Culture cannot be added this way, but we can use the palace to store how much culture we need to add
    pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE, 2 * iBoost)
  else
    pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_PRODUCTION, iBoost)
    pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE, 0)
  end

  -- As per turn culture can't be added via the palace, add it directly to the player
  pPlayer:ChangeJONSCulturePerTurnForFree(pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE))
end

--------------------------------------------------------------------
function Init()
  -- Find the player and team IDs for the Mitanni player (if any)
  for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1, 1 do
    local pPlayer = Players[iPlayer]
    if (pPlayer:IsEverAlive()) then
      -- if (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_MITANNI) then
      if (GetTraitInfo(pPlayer).Realpolitik > 0) then
        g_iMitanniPlayer = iPlayer
        g_iMitanniTeam = pPlayer:GetTeam()
        break
      end
    end
  end

  -- If the Mitanni are in play, compute their global war state and hook the events
  if (g_iMitanniPlayer ~= -1) then
    g_bMitanniAtWar = AtWarWithAnyMajor(g_iMitanniTeam)
	
    Events.SerialEventCityCreated.Add(OnCityChange)
    Events.SerialEventCityDestroyed.Add(OnCityChange)
    Events.SerialEventCityCaptured.Add(OnCityChange)
	
    Events.WarStateChanged.Add(OnWarStateChanged)
	
    print("mitanniUA loaded")
  end
end

--------------------------------------------------------------------
Init()
 
You're brilliant! Thank-you ever so much :).

EDIT: I've playtested this and it gives the bonus production as it should; it's getting +4 instead of +3 from the palace. Great. However, it starts with an extra 8 culture per turn (9 instead of 1); not quite sure how at the moment; will see if there's something else in the mod affecting it (though I don't think there is).

When I checked the tooltip on the top panel, it stated that 8 culture was from the leader trait. This increased to 16 when I declared war (with just one city in my empire).
 
Then check the leader trait?

Culture from ChangeJONSCulturePerTurnForFree() just shows in the Top Panel tooltip as "+x for free"
 
Then check the leader trait?

Culture from ChangeJONSCulturePerTurnForFree() just shows in the Top Panel tooltip as "+x for free"

I agree that the leader trait seems to be the thing to check - however, it's ok. All it had was 2 free spearmen at Bronze working (plus this lua); I've taken out the free spearmen, which had no effect on the culture as expected. I have done a search on the entire project and there's no other mention of the Mitanni in terms of traits.

The only reference in the lua logs is that the mitanniUA.lua has loaded.
 
There's a fairly major typo in that code :blush:

Code:
  -- Take away any previous culture bonus
  pPlayer:ChangeJONSCulturePerTurnForFree([COLOR="Red"]-1 * [/COLOR]pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE))
 
Thanks again for your help :thumbsup:.

I thought I'd put here the code I have, which is what you wrote, with the amendment, minus the directions. I've indicated the Mitanni as CIVILIZATION_MITANNI rather than the realpolitik counter.

What's happening is this: when at peace, +1 production in the capital for every city in the empire. This is happening as it should, counts the capital as one of the cities, which I wanted and the bonus disappears when war is declared.

However, the culture bonus seems to be +1 culture per city in the empire, including the capital, both in peacetime and at war. It needs to be +2 culture per city during wartime and nothing during peacetime. Could you help any further please?

One other question; if I wanted to split the trait further down the line and have one civ have the production bonus at wartime and the other the production bonus during peacetime, I take it I use the same code, refer the code to the correct civ and delete the lines relating to the part I don't want?

Code:
--------------------------------------------------------------------
local g_iMitanniPlayer = -1
local g_iMitanniTeam = -1
local g_bMitanniAtWar = false
local g_iPalaceClass = GameInfoTypes.BUILDINGCLASS_PALACE

--------------------------------------------------------------------
function OnCityChange(_, iPlayer, _, iNewPlayer)
  if (iPlayer == g_iMitanniPlayer or iNewPlayer == g_iMitanniPlayer) then
    UpdateMitanniBonusYields()
  end
end

--------------------------------------------------------------------
function OnWarStateChanged(iMyTeam, iTheirTeam, bWar)
  if (iMyTeam == g_iMitanniTeam and bWar ~= g_bMitanniAtWar) then
    g_bMitanniAtWar = AtWarWithAnyMajor(g_iMitanniTeam)
    UpdateMitanniBonusYields()
  end
end

--------------------------------------------------------------------
function AtWarWithAnyMajor(iTeam)
  local pTeam = Teams[iTeam]

  for iOtherPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1, 1 do
    local pOtherPlayer = Players[iOtherPlayer]
    if (pOtherPlayer:IsAlive() and pTeam:IsAtWar(pOtherPlayer:GetTeam())) then
      return true
    end
  end

  return false
end

--------------------------------------------------------------------
function UpdateMitanniBonusYields()
  local pPlayer = Players[g_iMitanniPlayer]
  local pCapital = pPlayer:GetCapitalCity()
  local iBoost = pPlayer:GetNumCities()
  pPlayer:ChangeJONSCulturePerTurnForFree(-1 * pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE))
  if (g_bMitanniAtWar) then
    pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_PRODUCTION, 0)
    pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE, 2 * iBoost)
  else
    pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_PRODUCTION, iBoost)
    pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE, 0)
  end
  pPlayer:ChangeJONSCulturePerTurnForFree(pCapital:SetBuildingYieldChange(g_iPalaceClass, YieldTypes.YIELD_CULTURE))
end

--------------------------------------------------------------------
function Init()
  for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1, 1 do
    local pPlayer = Players[iPlayer]
    if (pPlayer:IsEverAlive()) then
      if (pPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_MITANNI) then
        g_iMitanniPlayer = iPlayer
        g_iMitanniTeam = pPlayer:GetTeam()
        break
      end
    end
  end

  if (g_iMitanniPlayer ~= -1) then
    g_bMitanniAtWar = AtWarWithAnyMajor(g_iMitanniTeam)
	
    Events.SerialEventCityCreated.Add(OnCityChange)
    Events.SerialEventCityDestroyed.Add(OnCityChange)
    Events.SerialEventCityCaptured.Add(OnCityChange)
	
    Events.WarStateChanged.Add(OnWarStateChanged)
	
    print("MitanniUA loaded")
  end
end

--------------------------------------------------------------------
Init()
 
Please, please, please use [ CODE] ... [ /CODE] blocks - they preserve the layout!
 
Back
Top Bottom