DLL - Various Mod Components

Two new GameEvents in v88 for handling the World Congress / UN resolutions.

The first to override what resolution(s) an AI will propose
GameEvents.ResolutionProposing.Add(function(iPlayer, iLeague) return false; end)

The second to override what proposal(s) (including Host and World Leader) an AI will vote for
GameEvents.ResolutionVoting.Add(function(iPlayer, iLeague) return false; end)

You can either reprogam the default (DLL hard-coded) AI's logic for proposing/voting in the World Congress, or TC modders can completely repurpose the World Congress when used with event (added in v51)
GameEvents.ResolutionResult.Add(function(iResolution, iChoice, bEnact, bPassed) end)

All are enabled via the EVENTS_RESOLUTIONS option, both new ones bring interesting and exciting new ways to crash the DLL - so take care with them!


As a (cut-down) example, the following changes the way the AI votes in the WC from being concerned with what it gains from each proposal to a more politically orientated one (the AI will tend to vote with its allies and against its enemies). See "Resolutions - Political Voting" for the complete code.

Spoiler :
Code:
local iResolutionWorldLeader = GameInfoTypes.RESOLUTION_DIPLOMATIC_VICTORY
local iResolutionHost = GameInfoTypes.RESOLUTION_CHANGE_LEAGUE_HOST
local iResolutionEmbargo = GameInfoTypes.RESOLUTION_PLAYER_EMBARGO

local sDecisionYesNo = "RESOLUTION_DECISION_YES_OR_NO"

local kChoiceNo = 0;
local kChoiceYes = 1;

--
-- [B]Main voting functions[/B] - World Leader, Host and Proposals
-- Return true if ALL remaining votes used
--
function [B]CastVotesForWorldLeader[/B](iPlayer, iLeague, proposal)
  local pLeague = Game.GetLeague(iLeague)
  local iNumVotes = pLeague:GetRemainingVotesForMember(iPlayer)
  local iProposedPlayer = -1
  print(string.format("CastVotesForWorldLeader: %s with %i votes", Players[iPlayer]:GetName(), iNumVotes))

  ------
  ------ FOR TESTING ONLY, USE THE SAME DECISION MAKING PROCESS AS FOR THE HOST
  ------
  return CastVotesForHost(iPlayer, iLeague, proposal)
end

function [B]CastVotesForHost[/B](iPlayer, iLeague, proposal)
  local pLeague = Game.GetLeague(iLeague)
  local iCurrentHost = pLeague:GetHostMember()
  local iNumVotes = pLeague:GetRemainingVotesForMember(iPlayer)
  local iProposedPlayer = -1
  print(string.format("CastVotesForHost: %s with %i votes", Players[iPlayer]:GetName(), iNumVotes))

  -- Set iProposedPlayer to our preferred host, or leave at -1 for the standard (DLL hard-coded) AI decision
  if (iCurrentHost == iPlayer) then
    -- I'm the host, always vote for myself
    iProposedPlayer = iCurrentHost
  else
    local pPlayer = Players[iPlayer]

    if (pPlayer:IsDoF(iCurrentHost)) then
      -- I have a DoF with the current host, so vote for them
      iProposedPlayer = iCurrentHost
    else
      -- Including myself, find the player I have a DoF with who has the largest number of votes (in the case of a tie, the strongest) and vote for them
      iProposedPlayer = iPlayer
      local iMostVotes = iNumVotes

      local iDecision = GameInfo.ResolutionDecisions[GameInfo.Resolutions[proposal.ID].VoterDecision].ID
      for _, iChoice in ipairs(pLeague:GetChoicesForDecision(iDecision, iPlayer)) do
        if (iChoice ~= iPlayer) then
          if (pPlayer:IsDoF(iChoice)) then
            local iVotes = pLeague:GetRemainingVotesForMember(iChoice)

            -- I have a DoF with this player, are they in a better position to win?
            if (iVotes > iMostVotes or (iVotes == iMostVotes and Players[iChoice]:GetPower() > Players[iProposedPlayer]:GetPower())) then
              iProposedPlayer = iChoice
              iMostVotes = iVotes
            end
          end
        end
      end
    end
  end

  if (iProposedPlayer and iProposedPlayer ~= -1) then
    print(string.format("  voting for %i (%s)", iProposedPlayer, Players[iProposedPlayer]:GetName()))
    pLeague:DoVoteEnact(proposal.ID, iPlayer, iNumVotes, iProposedPlayer)
    return true
  end

  return false
end

function [B]CastVotesForProposals[/B](iPlayer, iLeague, enactProposals, repealProposals)
  local pLeague = Game.GetLeague(iLeague)
  local iNumVotes = pLeague:GetRemainingVotesForMember(iPlayer)
  print(string.format("CastVotesForProposals: %s with %i votes", Players[iPlayer]:GetName(), iNumVotes))

  -- If not all votes are cast, the default logic in the DLL will allocate the rest
  local bVoted = (
    CastVotesAgainstEnactEmbargoMe(iPlayer, pLeague, enactProposals) or
    CastVotesForEnactMyProposal(iPlayer, pLeague, enactProposals) or
    CastVotesForRepealMyProposal(iPlayer, pLeague, repealProposals)
  )

  return bVoted
end


--
-- [B]Sub-functions for voting [/B]for/against specific proposals.
-- Return true if ALL remaining votes used
--
function [I]CastVotesAgainstEnactEmbargoMe[/I](iPlayer, pLeague, proposals)
  local iNumVotes = pLeague:GetRemainingVotesForMember(iPlayer)

  local temp = GetProposalsByTypeOnPlayer(proposals, iResolutionEmbargo, iPlayer)

  if (#temp > 0) then
    -- Vote against an embargo on me!
    print(string.format("%s voting AGAINST an ENACT of an embargo on them", Players[iPlayer]:GetName()))
    pLeague:DoVoteEnact(temp[1].ID, iPlayer, iNumVotes, kChoiceNo)
    return true
  end

  return false
end

function [I]CastVotesForEnactMyProposal[/I](iPlayer, pLeague, proposals)
  local iNumVotes = pLeague:GetRemainingVotesForMember(iPlayer)

  local proposal = GetProposalByPlayer(proposals, iPlayer)

  if (proposal and IsYesNoDecision(proposal)) then
    -- Vote for my proposal
    print(string.format("%s voting FOR their own proposal (ENACT)", Players[iPlayer]:GetName()))
    pLeague:DoVoteEnact(proposal.ID, iPlayer, iNumVotes, kChoiceYes)
    return true
  end

  return false
end

function [I]CastVotesForRepealMyProposal[/I](iPlayer, pLeague, proposals)
  local iNumVotes = pLeague:GetRemainingVotesForMember(iPlayer)

  local proposal = GetProposalByPlayer(proposals, iPlayer)

  if (proposal and IsYesNoDecision(proposal)) then
    -- Vote for my proposal
    print(string.format("%s voting FOR their own proposal (REPEAL)", Players[iPlayer]:GetName()))
    pLeague:DoVoteRepeal(proposal.ID, iPlayer, iNumVotes, kChoiceYes)
    return true
  end

  return false
end


--
-- [B]Voting helper functions[/B]
--
function [I]GetProposalByPlayer[/I](proposals, iPlayer)
  for _, proposal in ipairs(proposals) do
    if (proposal.ProposalPlayer == iPlayer) then
      return proposal
    end
  end

  return nil
end

function [I]GetProposalsByTypeOnPlayer[/I](proposals, iType, iPlayer)
  local temp = {}

  for _, proposal in ipairs(proposals) do
    if (proposal.Type == iType and proposal.ProposerDecision == iPlayer) then
      table.insert(temp, proposal)
    end
  end

  return temp
end

function [I]IsYesNoDecision[/I](proposal)
  return (GameInfo.Resolutions[proposal.Type].VoterDecision == sDecisionYesNo)
end


--
-- [B]Main proposing function[/B]
-- Return true if ALL remaining proposals used
--
function [B]Propose[/B](iPlayer, pLeague)
  local bProposed = (
    ProposeRepealEmbargoMe(iPlayer, pLeague) or
    ProposeRepealEmbargoHost(iPlayer, pLeague) or
    ProposeEnactEmbargoHost(iPlayer, pLeague)
  )
  
  return bProposed
end


--
-- [B]Sub-functions for specific proposals[/B]
-- Return true if all proposals made
--
function [I]ProposeRepealEmbargoMe[/I](iPlayer, pLeague)
  if (pLeague:IsPlayerEmbargoed(iPlayer)) then
    RepealEmbargo(iPlayer, pLeague, iPlayer)
  end

  return (pLeague:GetRemainingProposalsForMember(iPlayer) == 0)
end

function [I]ProposeRepealEmbargoHost[/I](iPlayer, pLeague)
  local iCurrentHost = pLeague:GetHostMember()

  if (iCurrentHost ~= iPlayer and pLeague:IsPlayerEmbargoed(iCurrentHost)) then
    local pPlayer = Players[iPlayer]

    if (pPlayer:IsDoF(iCurrentHost)) then
        RepealEmbargo(iPlayer, pLeague, iCurrentHost)
    end
  end

  return (pLeague:GetRemainingProposalsForMember(iPlayer) == 0)
end

function [I]ProposeEnactEmbargoHost[/I](iPlayer, pLeague)
  local iCurrentHost = pLeague:GetHostMember()

  if (iCurrentHost ~= iPlayer and not pLeague:IsPlayerEmbargoed(iCurrentHost)) then
    local pPlayer = Players[iPlayer]

    -- If iPlayer is at war with, or really hates, the host ...
    if (pPlayer:IsAtWarWith(iCurrentHost) or pPlayer:GetMajorCivApproach(iCurrentHost) == MajorCivApproachTypes.MAJOR_CIV_APPROACH_HOSTILE) then
        EnactEmbargo(iPlayer, pLeague, iCurrentHost)
    end
  end

  return (pLeague:GetRemainingProposalsForMember(iPlayer) == 0)
end


--
-- [B]Proposal helper functions[/B]
--
function [I]EnactEmbargo[/I](iPlayer, pLeague, iEmbargoedPlayer)
  if (pLeague:CanProposeEnact(iResolutionEmbargo, iPlayer, iEmbargoedPlayer)) then
    print(string.format("%s proposing to ENACT an embargo on %s", Players[iPlayer]:GetName(), Players[iEmbargoedPlayer]:GetName()))
    pLeague:DoProposeEnact(iResolutionEmbargo, iPlayer, iEmbargoedPlayer)
  end
end

function [I]RepealEmbargo[/I](iPlayer, pLeague, iEmbargoedPlayer)
  local iProposal

  for _, proposal in ipairs(pLeague:GetActiveResolutions()) do
    if (proposal.Type == iResolutionEmbargo and proposal.ProposerDecision == iEmbargoedPlayer) then
      iProposal = proposal.ID
      break
    end
  end

  if (iProposal and pLeague:CanProposeRepeal(iProposal, iPlayer)) then
    print(string.format("%s proposing to REPEAL an embargo on %s", Players[iPlayer]:GetName(), Players[iEmbargoedPlayer]:GetName()))
    pLeague:DoProposeRepeal(iProposal, iPlayer)
  end
end


--
-- [B]Main event handlers[/B]
--
function [B]OnResolutionProposing[/B](iPlayer, iLeague)
  print(string.format("OnResolutionProposing(%s)", Players[iPlayer]:GetName()))
  local pLeague = Game.GetLeague(iLeague)

  return Propose(iPlayer, pLeague)
end
GameEvents.ResolutionProposing.Add(OnResolutionProposing)

function [B]OnResolutionVoting[/B](iPlayer, iLeague)
  print(string.format("OnResolutionVoting(%s)", Players[iPlayer]:GetName()))
  local pLeague = Game.GetLeague(iLeague)

  local enactProposals = pLeague:GetEnactProposals()
  local repealProposals = pLeague:GetRepealProposals()
  local iProposal = (#enactProposals == 1) and enactProposals[1].Type

  local bVoted
  if (#enactProposals == 1 and #repealProposals == 0 and (iProposal == iResolutionWorldLeader or iProposal == iResolutionHost)) then
    if (iProposal == iResolutionWorldLeader) then
      bVoted = CastVotesForWorldLeader(iPlayer, iLeague, enactProposals[1])
    else
      bVoted = CastVotesForHost(iPlayer, iLeague, enactProposals[1])
    end
  else
    bVoted = CastVotesForProposals(iPlayer, iLeague, enactProposals, repealProposals)
  end

  return bReturn
end
GameEvents.ResolutionVoting.Add(OnResolutionVoting)
 
v88 uploaded to my web-site and GitHub

For changes, see post above
 
Is there a mod that adjusts the NaturalWonderFirstFinderGold Bonus to the game speed?
At the moment for spain it is always 500, regardless of the gamespeed.

I thought about modifying the NaturalWonderPopUp.lua with help of GameEvents.NaturalWonderDiscovered (getting the set amount and multiply it with gamespeed), but here again the problem is, that a popup only works in singleplayer. In mulitplayer I'm sure the "OnPopUp" function is never called, so I don't know a way to get the initial gold amount a player should get...

Do you know a solution?
 
Where exactly can I see all the new functions you added? I mean functions like GetXY you added to DLL.
Is there any function that checks/returns all adjacent plots? I would like to check if a city is adjacent to a specific plot, without the need to add all the plotmath...

edit:
and how does your UI-diary mod work? I only see the diary options on the top right, but where is the diary itself?
 
No one place, they are in various posts across these forums

Easiest way to find them all is to search CvLua*.h for LUAAPIEXTN, but the ones you're interested in are

Code:
	LUAAPIEXTN(IsOnFeature, bool, iFeatureType);
	LUAAPIEXTN(IsAdjacentToFeature, bool, iFeatureType);
	LUAAPIEXTN(IsWithinDistanceOfFeature, bool, iFeatureType, iDistance);
	LUAAPIEXTN(IsOnImprovement, bool, iImprovementType);
	LUAAPIEXTN(IsAdjacentToImprovement, bool, iImprovementType);
	LUAAPIEXTN(IsWithinDistanceOfImprovement, bool, iImprovementType, iDistance);
	LUAAPIEXTN(IsOnPlotType, bool, iPlotType);
	LUAAPIEXTN(IsAdjacentToPlotType, bool, iPlotType);
	LUAAPIEXTN(IsWithinDistanceOfPlotType, bool, iPlotType, iDistance);
	LUAAPIEXTN(IsOnResource, bool, iResourceType);
	LUAAPIEXTN(IsAdjacentToResource, bool, iResourceType);
	LUAAPIEXTN(IsWithinDistanceOfResource, bool, iResourceType, iDistance);
	LUAAPIEXTN(IsOnTerrain, bool, iTerrainType);
	LUAAPIEXTN(IsAdjacentToTerrain, bool, iTerrainType);
	LUAAPIEXTN(IsWithinDistanceOfTerrain, bool, iTerrainType, iDistance);
	LUAAPIEXTN(CountFeature, int, iFeature);
	LUAAPIEXTN(CountWorkedFeature, int, iFeature);
	LUAAPIEXTN(CountImprovement, int, iImprovement);
	LUAAPIEXTN(CountWorkedImprovement, int, iImprovement);
	LUAAPIEXTN(CountPlotType, int, iPlotType);
	LUAAPIEXTN(CountWorkedPlotType, int, iPlotType);
	LUAAPIEXTN(CountResource, int, iResource);
	LUAAPIEXTN(CountWorkedResource, int, iResource);
	LUAAPIEXTN(CountTerrain, int, iTerrain);
	LUAAPIEXTN(CountWorkedTerrain, int, iTerrain);
 
I sometimes wonder why I write this stuff ;)

Displays a summary of key events as a tooltip on the date and turn indicators in the top panel

And you can avoid the "plot maths" by using the plot iterator helper functions I wrote some years back
 
I sometimes wonder why I write this stuff ;)
To be honest, often your sentences are quite short and we are not 100% clear what you mean ;)
E.g this sentence, I first thought I could see tooltips when I hover over the date IN the diary. Not that the tooltips showing up when hovering over the date ARE the diary...
I got it now?

And you can avoid the "plot maths" by using the plot iterator helper functions I wrote some years back
Thank you very much ! :)
Yes you wrote alot great stuff!

Maybe make another thread, post there a list of links to great stuff like this and pin the thread in your signature? :)
 
To be honest, often your sentences are quite short and we are not 100% clear what you mean ;)
Yeah, it was as much a dig at myself as anyone - don't forget I'm British and we only do sarcasm as humour :D

Maybe make another thread, post there a list of links to great stuff like this and pin the thread in your signature? :)

Like this?
 
Bookmarked, thanks :)

About the NaturalWonderFirstFinderGold from Spain:
I set this value in database to 0 and distribute this gold bonus in lua only, depending on gamespeed and send a generic notification now.
Of course this won't be compatible to any mods that change these database values, but I don't know a better solution.
 
Hey whoward :)

Could you please take look at this list
http://forums.civfanatics.com/showpost.php?p=14357088&postcount=125
And tell me if you know any of your mods, that is not compatible for multiplayer? E.g uses ActivePlayer to do some game changes or simular ?
Of course it could be also an mistake I made, but if you know that any of these won't work, it would be good to know :)
 
pPlayer:CountAllResource(iResourceType) does not return correct value on BuildFinished event when you get new resource by building citadel.
When you build a citadel, BuildFinished event fires before you get new plots. Can this be fixed?
 
Were should I post possible bugs from the "AI - MP Diplomacy" mod from JaiDerHerr?
Here or should I try to contact JdH directly, but were.. is he at this forum?

Someone reported at my modpack thread:
Trades only hold up for what seem like a couple of turns. I'll exchange luxuries with an AI, and then a couple of turns (maybe one turn? maybe five? certainly not 90) later I'll see a trade request from the same AI for the same luxury.
 
For the AI - Warmonger Adjustments, I set WARMONGER_THREAT_AGGRIEVED_PERCENT to 0. Yet I still get pretty substantial warmonger penalties after capturing cities from a civ that DOWed me. Is it supposed to be that way? How to I reduce it even further, preferably close to zero warmonger penalties? Can I put in a negative number?

I still need an answer to this question. WARMONGER_THREAT_AGGRIEVED_PERCENT doesn't seem to do anything for me.
 
Hey whoward :)
I found out that the mod that causes desync is ironically the
AI - MP Diplomacy mod.

Could you please take a look at your implementation, if something there causes the problem?
Everytime when a player accepts an offer from AI, we will get the loading screen.
Also without accepting it, there is every few rounds (3-5) a loading screen (maybe because AI trades with AI?).
After I deactivated this mod, we had no more loading screens.

If there is nothing about your implementation, could you please tell me how to contact JdH ? I wrote him at http://civforum.de/ , but he wasn't active there over a year =/
 
Back
Top Bottom