CTP2 Bureau : Notes on Diplomacy (Preliminary Version)
CTP2 Bureau
The Modding
Knowledgebase
WePlayCiv: CtP2 Downloads
WePlayCiv: Call To Power forum
Apolyton Call To Power forums
Civilization Fanatics: Call To Power

Notes on Diplomacy (Preliminary Version)

Much of the following is cut and pasted from the 'AI Customization via SLIC' document and other documentation that can be found on the Apolyton Modification site or in some cases in the aidata folder.

by Peter Triggs

But, to start with, consider the following (Modification:Data File Definitions [diplomacy.txt]):

This file defines the Diplomatic States. The active Diplomatic State is selected by logic from within the game. Future plans are to expose the state selection logic to SLIC so state changes can be scripted in SLIC scripts. This seems to contradict much of what is said in the AI Customization document, but is too cryptic to make a lot of sense (at least to me). It might mean that although it's possible for us to define new diplomatic states (like Dale did in Diplomod) it's damn near impossible for us to get the game to make them 'active', i.e., to use them. On the other hand it may be possible to work around this problem by generating events (although there can be a fairly heavy CPU cost in doing this).

Contents:

0) Proposals and Diplomatic States: What a diplomatic state does. The 'inherit' flag.  
Proposals:How prioritized. Re-prioritizing and adding new diplomatic states. The link between diplomatic and strategic states. The order of diplomatic events.

1) Sending a Proposal: To the Human. To An AI Civ. More on Motivating the AI to send a proposal.

2) Receiving a Proposal

3) Responding to Counter-proposals and Rejections:

4) Responding to Threats:

Appendix 1: Some SLIC functions. 

Appendix 2: The New Proposal List.

0) Proposals and Diplomatic States:

A diplomatic state defines "how each AI player reacts diplomatically to each other player in the game."  Originally, there were only three diplomatic states: DIPLOMACY_DEFAULT, DIPLOMACY_PROVOKE_WAR, and DIPLOMACY_MAKE_FRIEND. Dale added three more for Diplomod: DIPLOMACY_AI_AI_NORMAL, DIPLOMACY_AI_AI_SUCKUP, and DIPLOMACY_AI_AI_WARMONGER.

DIPLOMACY_DEFAULT is, self-evidently, the default state. The other two original states, PROVOKE_WAR, and MAKE_FRIEND, are loaded in some hard coded circumstances that we don't know about. (It probably depends at least in part on the AI civs leader's  personality attributes: trustworthiness, conquest, and alignment.)

These two states are much smaller than DIPLOMACY_DEFAULT, because they 'inherit' it:

a diplomatic state might define other states that it inherits from in order reduce the redundancy in diplomatic state records. If the Inherit field is set, then the referenced diplomacy record is loaded first and the current record is merged in overwriting any variables that it redefines.

For example, in DIPLOMACY_PROVOKE_WAR the ProposalElement PROPOSAL_REQUEST_WITHDRAW_TROOPS is redefined as

ProposalElement {
  Proposal PROPOSAL_REQUEST_WITHDRAW_TROOPS
  SendPriority 250
  AcceptPriority 0
  RejectPriority 150
  SenderRegardResult -50
  ReceiverRegardResult 200
  ViolationRegardCost -250
  ViolationTrustCost -20
}

This overwrites the corresponding definition in DIPLOMACY_DEFAULT (see below), while proposal elements like   OFFER_GIVE_ADVANCE, and REQUEST_GIVE_ADVANCE, which are not redefined in PROVOKE_WAR retain their DEFAULT values.

Every diplomatic state contains a whole bunch of parameters concerning 'regard events' and regard/trust penalties/bonuses, but what follows just deals with sending and receiving proposals.

Although the analogy isn't perfect, proposals are to Diplomatic states as goals are to Strategic states: just as goals define what an AI civ does to an opponant (how it "tasks it's units"), so proposals define what it says to him. Moreover, just as Goals are prioritized in each of the various Strategies, so each  New Proposal is given a sequence of base priorities in each of the various Diplomatic 'strategies' or states in diplomacy.txt:

ProposalElement   
   Proposal                  -  Reference of a proposal from DiplomacyProposal.txt 
   SendPriority              -  Base priority to send this proposal 
   AcceptPriority            -  Base priority to accept this proposal 
   RejectPriority            -  Base priority to reject this proposal
   SenderRegardResult        -  Regard penalty/bonus for the sender of this proposal  
   ReceiverRegardResult      -  Regard penalty/bonus for the receiver of this proposal  
   ViolationRegardCost       -  Regard penalty for violating this proposal  
   ViolationTrustCost        -  Trust penalty for violating this proposal

For example (DIPLOMACY_DEFAULT),

ProposalElement {
  Proposal PROPOSAL_REQUEST_WITHDRAW_TROOPS
  SendPriority                100
  AcceptPriority             110
  RejectPriority              100
  SenderRegardResult    -50
  ReceiverRegardResult  200
  ViolationRegardCost   -250
  ViolationTrustCost        -20
}

Compare this with the same proposal element when it occurs in PROVOKE_WAR (above). When an AI civ is in a PROVOKE_WAR state it has a much higher priority to request that troops be withdrawn, nil priority to accept such a request, and a slightly higher priority to reject such a request.      

Here's the explanation of how diplomatic states are used:

Unlike strategic states, each player maintains multiple diplomatic states that define their relationship with each foreigner. Also unlike strategic states, diplomatic states are taken all or nothing. After all diplomatic states have been considered in a turn, only the one with the highest priority is used.

The base priorities of the various diplomatic states are again hard-coded but we can re-prioritize them by calling the ConsiderDiplomaticState function within the NextDiplomaticState event:

     HandleEvent (NextDiplomaticState) ‘Example_NextDiplomaticState’ pre {
         //stuff
         // ConsiderDiplomaticState(player[0],player[1], priority, DiplomacyDB(DIPLOMACY_<state name>),-1,-1,-1);
                // player[0] is the player being whose turn it is
                // player[1] is the foreigner
 
     }

For example, 

     HandleEvent (NextDiplomaticState) ‘Example_Load_Provoke_War’ pre {
          if( !IsHumanPlayer(player[0]) && GetEffectiveRegard(player[0],player[1])< 200){
              ConsiderDiplomaticState(player[0],player[1], 2000, DiplomacyDB(DIPLOMACY_PROVOKE_WAR));
          }
     }

will put an AI player[0] into a PROVOKE_WAR diplomatic state with any player[1] whenever his effective regard for player[1] is less than 200 (i.e., he doesn't really like player[1]).

We can also use this event/function combination to load any new diplomatic state that we may care to define. In Diplomod, Dale uses it to load his DIPLOMACY_AI_AI_NORMAL, DIPLOMACY_AI_AI_SUCKUP, and DIPLOMACY_AI_AI_WARMONGER states on a random basis, but my inclination is to try to find some more game dependent situations that warrent new diplomatic states. For example, we could define a new state DIPLOMACY_DEVELOPING_NATION where the priorities for requesting advances are very high and then

   HandleEvent (NextDiplomaticState) ‘Example_Load_Developing_Nation’ pre {
          if(!IsHumanPlayer(player[0]) && KnowledgeRank(player[0])<KnowledgeRank(player[1])){
                   ConsiderDiplomaticState(player[0],player[1], 2000, DiplomacyDB(DIPLOMACY_DEVELOPING_NATION));
                   Event:ReactionMotivation(player[0],player[1]);
                   Event:DesireMotivation(player[0],player[1]);
          }
   }

which would load this state whenever AI player[0] is behind any player[1] in science. The two following motivation events should then force the AI player[0] to use these priorities to send proposals to any other player it has contact with. (Restrict to AI's?) (To be really safe, maybe we should load a new strategy that prioritizes science.)

(comment on initializing)

Finally, diplomatic states are linked to strategic states via the motivations priorities in the various strategies, e.g., (STRATEGY_SCIENTIST_DEFAULT):

    //
    //  DIPLOMACY
    //
    // priority for fear motivations
    FearInvasion                1000
    FearCityDefense          1100
    FearPiracy                    900
    FearScienceRank        1000
    FearMilitaryRank           800
    FearTradeRank             800
    FearPollution                900
    // priority for desire motivations
    DesireAttack                 800
    DesireGold                   900
    DesireScience              900
    DesireMakeFriend        1000
    DesireEnlistFriend        1100

It's these motivations that ultimately trigger the sending of new proposals (from Interactions.txt):

    How does diplomacy work?
    AI Diplomacy sequence:
    Step 1) Create ranked list of motivations (fears and desires). [from Strategies]
    Step 2) Select top motivation. [Event:FearMotivation, DesireMotivation, ReactionMotivation]
    Step 3) Select highest priority New Proposal triggered by top motivation. [GetNewProposalPriority function]
                
    Step 4) Send highest priority New Proposal triggered by motivation, if any. [ConsiderNewProposal function]


Diplomatic events: There are a number of Diplomatic events. They occur roughly in the following order, but the occurence of some depends on the occurence of previous ones (see below):

(Note on methodology: As well as relying on the documentation, I added handlers like

     HandleEvent(DesireMotivation)'TestDesireMotivation' pre {
     message(1,'TestDesireMotivationM');  
     }
     messagebox 'TestDesireMotivationM' {
     Text (ID_M_TestDesireMotivation) ;
     //M_TestDesireMotivation "Desire {player[0].civ_name_plural} to {player[1].civ_name_plural} value[0]={value[0]}"
     }
     HandleEvent(NewProposalReady)'TestNewProposalReady' pre {
     message(1,'TestNewProposalReadyM');  
     }
     messagebox 'TestNewProposalReadyM' {
     Text (ID_M_TestNewProposalReady) ;
     //M_TestNewProposalReady "M_TestNewProposalReady"
     }

and then tried to deduce the order of events from the order of the resulting messages.)

    BeginTurnAgreements(int_t<player>)
    Begin player diplomatic agreements phase
    InitDiplomaticState(int_t<player>, int_t<player>)
    Initialize Diplomatic state
 
    NextDiplomaticState(int_t<player>, int_t<player>)
    Determine next diplomatic state
    ComputeMotivations(int_t<player>)
    Determine fears and desires
    StartNegotiations(int_t<player>)
    After computing motivations, select new proposals
    FearMotivation(int_t<player>, int_t<player>, int_t)
    Find new proposal for fear motivation

The final int can take values like 3,8,9,10,11.

    DesireMotivation(int_t<player>, int_t<player>, int_t)
    Find new proposal for desire motivation

Here the final int can take values like 13,18,20,21.

Both of these last events can occur more than once per turn.  In both cases the number of times the event occurs can be raised by using the ConsiderMotivation(int<player>,int,int) function, e.g.:

               HandleEvent(ComputeMotivations)'TestComputeMotivations' pre {
                   
                   stuff
               
                   ConsiderMotivation(player[0],5000,8)
               }

the '8' being the value that comes up in the later FearMotivation event. These numbers must refer to the motivation priorities in the player's current strategy, but I haven't figured out how. 

    ReactionMotivation(int_t<player>, int_t<player>)
    Find new proposals motivated as reactions to game events
    NewProposalReady(int_t<player>, int_t<player>)
    Notify world that a new proposal is ready
    NewProposal(int_t<player>, int_t<player>)
    A new proposal has been made.
    NewNegotiationEvent(int_t<player>, int_t<player>)
    A new negotiation history event has been added 
    ResponseReady(int_t<player>, int_t<player>)
    Notify world that a diplomatic response is ready
    Counter(int_t<player>, int_t<player>)
    A proposal has been countered.
   
    Reject(int_t<player>, int_t<player>)
    A proposal has been rejected
    Accept(int_t<player>, int_t<player>)
    A proposal has been accepted.
    Threaten(int_t<player>, int_t<player>)
    A player has been threatened   
    ContinueDiplomacy(int_t<player>, int_t<player>)
    Decide if sender should make a new proposal.
----------------
    ToggleInitiative(int_t<player>, int_t<player>)
    Initiative passes to party currently without initiative.

1) Sending a Proposal:

To The Human: Although there's nothing in what follows to indicate that the recipient of the proposal must be the human player, in practice that's the way it is. There was even a supposed example (see below), but I couldn't get it to work. Also see below for workarounds.

At the beginning of every player turn the ReactionMotivation and possibly one or both of the FearMotivation or DesireMotivation events will be triggered. To add a rule which can trigger the sending of a new proposal you should write a SLIC function to handle one of these events. For simplicity it is best to write your handles to be triggered by the ReactionMotivation event. For all of these events, player[0] is the player whose turn it is, and player[1] is the foreigner that would receive the new proposal.

The recommended procedure is:

You should access the current diplomacy state for the player using the GetNewProposalPriority function and also call any other functions needed to determine whether or not the player should send a particular new proposal. If it is determined that the proposal should be sent, then call the function ConsiderNewProposal. If multiple calls to ConsiderNewProposal are made, then the proposal with the highest priority will be sent.

So an easy way to force the AI to send a particular new proposal (to the Human) is just to use the ConsiderNewProposal function. It's syntax is:

VOID  ConsiderNewProposal(int<player0>, int<player1>, int1, int2, param, int3, int4, int5)
e.g.,  ConsiderNewProposal(player[0].owner,    //  sender
player[1].owner,          //  receiver
1000,                     //  priority
4,                        //  ProposalDB(index), 4 is REQUEST_WITHDRAW_TROOPS
??int                     //  an int parameter that seems to depend on the new proposal type
ID_string,                //   e.g., ID_EXPLAIN_WANT_GOLD_ADVANCE
ID_string,                //   e.g., ID_ADVICE_WANT_GOLD_ADVANCE
ID_string,                //   e.g., ID_NEWS_WANT_GOLD_ADVANCE)
);

These string ID's were found in junkstring.txt. I've tried to test the function with string ID's but haven't seen them appear anywhere.  My guess is that these function parameters were never fully implemented. Here's an example of it's use:

HandleEvent(ReactionMotivation) 'NewDipProposal'  pre  {
 
         ... stuff...
 
         ConsiderNewProposal(player[0].owner, player[1].owner, 2000, 21, 0, ID_TEST,ID_TEST,ID_TEST);
}

The priority 2000 will invariably force (AI-)player[0] to send new proposal #21 to player[1] (who will inevitably be the human player).

The problem is with that strange parameter: Some proposals involve cities, advances, or gold. This parameter is what determines the specific city, advance, or amount of gold. But I can't figure out how it works, and without it can't generate those proposals.

According to Azmel2 (Richard Myers):

You can also redefine specific diplomatic responses and new proposal triggers. The commented out handler in diplomacy.slc titled 'WantGoldAdvance_NewProposalEvent' is an example of how to make the AI consider sending a new proposal with high (1000) priority requesting a desired gold advance.

// DESIRES
// ask for the next advance on our list of gold enhancing advances
// if the target player has it, and we are on good terms with them.
int_t dipAdvance;
HandleEvent(DesireMotivation) 'WantGoldAdvance_NewProposalEvent' pre {
 // get the next unknown gold advance on our advance list that
 // has already been discovered by the target player.
 // dipAdvance = GetDesiredAdvance(player[0].owner,
 //                             player[1].owner,
 //                             GOLD_LIST);
 // dipAdvance = 5;
 //ConsiderNewProposal(player[0].owner, // sender
 //        player[1].owner, // receiver
 //        1000,        // priority
 //        PROPOSAL_REQUEST_GIVE_ADVANCE, // param1
 //        dipAdvance,        // param2
 //        ID_EXPLAIN_WANT_GOLD_ADVANCE,
 //        ID_ADVICE_WANT_GOLD_ADVANCE,
 //        ID_NEWS_WANT_GOLD_ADVANCE);
}

But I couldn't get this to work: the "5" (or dipAdvance) produces a "Wrong type of argument" error.

The syntax for the GetNewProposalPriority function is:

     INT GetNewProposalPriority(player[0],player[1],int)

where the last int is a ProposalDB index. It returns the priority for this proposal as determined by the current diplomactic state that player[0] takes toward player[1].

To An AI Civ:   It's the motivation settings in the strategies in combination with the priorities for the proposal in diplomacy.txt that should determine whether or not a New Proposal is sent. But this happens in the three motivation events: FearMotivation, DesireMotivation, and ReactionMotivation. Each of these has a player[0] (the sender) and a player[1] (the receiver) parameter and no matter what I tried I couldn't get the events occur with anything other than the human as player[1].

So, as they say, here's a workaround. Just prior to these motivation events the NextDiplomaticState event occurs, again with player[0] and player[1] parameters, and here the game does take player[1] in a loop through all the other players that player[0] has contact with. There's two tricks involved: first, you work in this event rather than the motivation events we're supposed to be working in, and second, immediately after telling the sender to consider a new proposal you generate a NewProposal event. Here's an example test handler:

HandleEvent(NextDiplomaticState)'Test_AI_AI_Diplomacy' pre {
    if (!IsHumanPlayer(player[0]) && !IsHumanPlayer(player[1])) {
         if (TurnsAtWar(player[0],player[1])>25 ) {// 0 for test
       // offer cease-fire    
       LogRegardEvent(player[0],player[1], 500, 0, ID_NONE, 0);
       ConsiderNewProposal(player[0],player[1], 2000,32,0,ID_NONE,ID_NONE,ID_NONE);    
       Event:NewProposal(player[0],player[1]);      
         }
         if (HasAgreement(player[0], player[1],32) && !HasAgreement(player[0], player[1],33) ) {
  //if they have a cease-fire but not a peace treaty, offer one
       LogRegardEvent(player[0],player[1], 500, 0, ID_NONE, 0);
       ConsiderNewProposal(player[0], player[1], 2000, 33, 0,ID_NONE,ID_NONE,ID_NONE);
       Event:NewProposal(player[0], player[1]);
         }
         if(AtWarWith(player[0],1) && AtWarWith(player[1],1)){// 1 being the Human
  //if both sender and recipient are at war with the Human
              if (!HasAgreement(player[0], player[1],33) ) {//are these mutual?
       //if they don't have a peace treaty, offer one
            ConsiderNewProposal(player[0], player[1], 2000, 33, 0,ID_NONE,ID_NONE,ID_NONE);
       Event:NewProposal(player[0], player[1]);       
              }
              if(HasAgreement(player[0], player[1],33) && !HasAgreement(player[0], player[1],38)){
       //if they have a peace treaty but not an alliance, offer one
                   ConsiderNewProposal(player[0], player[1], 2000, 38, 0,ID_NONE,ID_NONE,ID_NONE);
          Event:NewProposal(player[0], player[1]);         
              }
         }
    }
}

More on Motivating the AI to send a proposal:

    You can generate the motivation events within NextDiplomaticState, but it's very CPU intensive
    new strategies
    new diplomatic states
    use ConsiderMotivation

2) Receiving a Proposal:

From here on down we're talking about 'Response Logic', how does an AI civ respond to a new proposal that's been sent to them (by either a human or another AI player).

The instructions are:

If an AI has received a new proposal, then the NewProposal event will be triggered. You should write functions to handle this event and respond to it. The parameter player[0] will be the player that sent the proposal and player[1] will be the player that received it. You should call the functions GetLastNewProposalType, GetLastNewProposalArg and GetLastNewProposalTone to figure out how to respond; other functions might also be useful when deciding how to respond. You should use the function ConsiderResponse if you wish to accept or counter the new proposal. By default the new proposal will be rejected. If multiple calls to ConsiderResponse are made then the response with the highest priority will be sent.

Here's the syntax for the above functions:

        INT GetLastNewProposalType(sender, receiver, first/second_type);

where   first/second_type is 0 to get the type of the 'initial' proposal or 1 to get the type of the 'in addition' proposal if it has one. This seems only to apply if the Human sent it; I have yet to see how to get the AI to initiate such a two part proposal.

        INT GetLastNewProposalArg(sender, receiver,int,param/int);

Don't know what the ints are for. But I always get either 0 or -1. This should return a value related to the above strange parameter.

       GetLastNewProposalArg(sender, receiver,0,tmpCity);   // 0= first_type ?  

when it's offer city is syntactically correct but always returns tmpCity=0, the invalid one.

        INT GetLastNewProposalTone( sender, receiver);

Returns  0= Magnanimous/Kind, 1=Friendly/Equal, 2=Obsequious/Meek, 3=Haughty/Indignant, 4=Hostile/Angry. 

       VOID  ConsiderResponse(sender, receiver, priority,response_type , [counter/threat type], [param1], [tone]);
where:     
         enum RESPONSE_TYPE {
            RESPONSE_INVALID,          // 0
            RESPONSE_REJECT,          // 1
            RESPONSE_ACCEPT,         // 2
            RESPONSE_COUNTER,      // 3
            RESPONSE_THREATEN,     // 4
            RESPONSE_MAX                // 5 (just marks end of list, I think)
        If RESPONSE_TYPE=3 then  counter/threat type should be the proposalDB index of the proposal you want to counter with.
        If RESPONSE_TYPE=4 then  enum THREAT_TYPE { 
                                      THREAT_NONE,                             //0
                                      THREAT_ATTACK_CITY,                //1
                                      THREAT_PIRATE,                          //2
                                      THREAT_SPECIAL_ATTACK_CITY, //3
                                      THREAT_DESTROY_CITY,             //4
                                      THREAT_END_AGREEMENT,        //5
                                                 THREAT_TRADE_EMBARGO,        //6
                                      THREAT_DECLARE_WAR,             //7
                                      THREAT_MAX                                //8

(I'm guessing at the threat types, haven't actually tried them.)

param1 depends on the counter/threat type and may be, e.g., a city_t variable. In this case the problem is to fill it in.

                             tone is an int:         0= Magnanimous/Kind 
                                                     1=Friendly/Equal
                                                     2=Obsequious/Meek
                                                     3=Haughty/Indignant
                                                     4=Hostile/Angry.

This is just like the problematic cases above, but here there was an example:

// Example proposal response:
// when a foreigner asks for gold and AI can take a city from the foreigner, ask for the city
//HandleEvent(NewProposal) 'Example_ProposalResponseEvent' pre {
.
.
.
// INT FindCityToExtortFrom(player, foreigner, max_value, &cityvar)
// city_t city;
// FindCityToExtortFrom(player[1], player[0], total_sender_result, city);
//
// // PROPOSAL_REQUEST_GIVE_GOLD == 20
// // PROPOSAL_OFFER_GIVE_CITY == 1
// if (CityIsValid(city) && first_type == 20 && total_receiver_result < 0 && total_sender_result > 0)
// {
//  // VOID ConsiderResponse(sender, receiver, priority, response_type, [counter/threat type], [param1], [tone])
//  // PROPOSAL_OFFER_GIVE_CITY = 1 and DIPLOMATIC_TONE_ANGRY = 4
//  // RESPONSE_COUNTER == 3
//  ConsiderResponse(player[0], player[1], 100, 3, 1, city, 4);
// }
// else
// {
//  // VOID ConsiderResponse(sender, receiver, priority, response_type, [counter/threat type], [param1], [tone])
//  // RESPONSE_REJECT == 1
//  // RESPONSE_ACCEPT == 2
//  ConsiderResponse(player[0], player[1], 10, 1);
// }

One of the 'other functions' mentioned is:

       INT GetNewProposalResult(sender,receiver,player,type)
   // type: 0 = science, 1 = gold, 2 = production, 3 = regard

where, for example, we can have stuff like:

 int_t sender_science_result;
 int_t receiver_science_result;
 sender_science_result = GetNewProposalResult(player[0], player[1], player[0], 0);
 receiver_science_result = GetNewProposalResult(player[0], player[1], player[1], 0);
 int_t sender_gold_result;
 int_t receiver_gold_result;
 sender_gold_result = GetNewProposalResult(player[0], player[1], player[0], 1);
 receiver_gold_result = GetNewProposalResult(player[0], player[1], player[1], 1);
 int_t sender_production_result;
 int_t receiver_production_result;
 sender_production_result = GetNewProposalResult(player[0], player[1], player[0], 2);
 receiver_production_result = GetNewProposalResult(player[0], player[1], player[1], 2);

and then use these results to determine the AI's response as in the above example.
 
So, someone (either Human or AI) has sent a new proposal and it's been received by the AI player[1]. Here's an example of a handler where player[1] responds to a proposal asking him to withdraw his troops by countering with a proposal that the sender, player[0], should stop piracy:

HandleEvent(NewProposal) 'ResponseToWithdrawCounterWithStopPiracy' pre {
int_t first_type; //initial proposal
int_t second_type; // 'in exchange' proposal
          first_type = GetLastNewProposalType(player[0], player[1], 0);
          second_type = GetLastNewProposalType(player[0], player[1], 1);
         if ( first_type==4 || second_type == 4 ) {
   
                 ConsiderResponse(player[0], player[1], 1000, 3, 6);
         }
}

Here's one where player[1] responds to a proposal (#22) when it is additionally accompanied with an offer of gold:

HandleEvent(NewProposal)'RespondToNewProposals' pre {
int_t first_type; //initial proposal
int_t second_type; // 'in exchange' proposal
int_t sender_gold_result;
     
          first_type = GetLastNewProposalType(player[0], player[1], 0);
          second_type = GetLastNewProposalType(player[0], player[1], 1);
 
          receiver_gold_result = GetNewProposalResult(player[0], player[1], player[1], 1);
          if (first_type==22 && second_type == 19) {//request pollution agreement && offer gold
                if (receiver_gold_result >100) {
                      ConsiderResponse(player[0], player[1], 2000,2);  //accept
          
     }
     else{
           ConsiderResponse(player[0], player[1], 2000,1);   //reject
     }
          }
    }

3) Responding to Counter-proposals and Rejections.

If an AI has received a response to their new proposal other than accept, then the Counter event will be triggered if the receiver of the proposal countered or a Reject event if they rejected it outright. The parameter player[0] will be the player that sent the original new proposal and player[1] will be the player that was the original receiver. You should call the functions GetLastResponseType, GetLastCounterResponseType, GetLastCounterResponseArg before determining how to respond to a counter proposal or rejection. You can then call the ConsiderResponse function (with the receiver and sender id’s swapped if accepting or rejecting) to respond. You can also call ConsiderResponse to threaten.

The syntax involved here is:

      INT GetLastResponseType(player[0], player[1], first/secondtype);
Returns(?):RESPONSE_TYPE {
            RESPONSE_INVALID,          // 0
            RESPONSE_REJECT,          // 1
            RESPONSE_ACCEPT,         // 2
            RESPONSE_COUNTER,      // 3
            RESPONSE_THREATEN,     // 4

      INT GetLastCounterResponseType(player[0], player[1], first/secondtype);
      INT GetLastCounterResponseArg(player[0], player[1],int,int);  Same problem as GetLastNewProposalArg
      INT GetCounterProposalResult(sender,receiver,player,type);  // like GetNewProposalResult  
                    // type: 0 = science, 1 = gold, 2 = production, 3 = regard

4)  Responding to Threats.

If an AI has received a threat from a proposal sender, then the Threaten event will be triggered. The parameter player[0] will be the player that send the original new proposal and player[1] will be the player that was the original receiver. You can call the GetLastNewProposal* and GetLastResponse* functions to determine how to respond to a threat. You can then call the ConsiderResponse function to accept or reject the original proposal after a threat.

       INT  GetLastNewProposal*   
       INT   GetLastResponse* 

Appendix 1: Some SLIC functions.

A number of these were gleaned from Diplomod; thanks, Dale.

GetPublicRegard( ??? )
INT GetEffectiveRegard(int<player>, int<player>) [regard adjusted for being at war]
INT GetTrust(int<player>, int<player>)

Azmel2 [Richard Myers]:

Both attitude [regard] and trust are on a scale from 0 to 1000.
The break down is as follows:

0 - 100, hot war     (Goals with TargetOwner: HotEnemy)
101-300, cold war  (Goals with TargetOwner: ColdEnemy)
301-700, neutral    (Goals with TargetOwner: Neutral)
701-900, friend
900-1000, allied

The first three also say which goals will be pursued against foreign nations that you have a particular attitude toward. For example, you'll only do HotEnemy goals against foreigners you have a regard of 100 or less with. Going to war will also induce an automatic hotwar regard.
Trust is on the same scale. An AI will not sign certain treaties or agreements unless trust or regard is above a certain point. That can be redefined using your own diplomatic response logic.


VOID RecomputeRegard(int<player>)


VOID SetTrust(int<player>, int<player>, int)

Sets first player's trust towards second player to 0<= int <=1000. (?)


VOID LogRegardEvent(int<player>, int<player>, regardDelta, regardEventType, ID_string, Delta_length)

regardDelta is the amount to increase the first player's regard towards the second by.

The Regard Event Types are ScenarioEvent, MilitaryPowerEvent, MilitarySafetyEvent, DiplomacyEvent, GoldEvent, KnowledgeEvent, & ProductionEvent. So "0" is probably the null event type.

ID_string is a string used in the Diplomacy intelligence screen.

Delta_length is, I think, the number of turns this should last for.

Example:

         // Make the Allies like each other
        LogRegardEvent(2, 3, 1000, 0, ID_WW_BLANK, turnMax);     

where turnMax is the number of turns in the WW2 scenario.


INT GetPersonalityType(int<player>) 

Returns the personalityDB index of the player's leader

Dale discovered an optional second int argument: GetPersonalityType(int<player>,[int]), but it's use is completely mystifying, so far as I know.


INT GetDesireWarWith(int<player>, int<player>)

Returns 1 or 0. [May be used to load ProvokeWar Dip State, also in response logic.]


INT TurnsAtWar(int<player>, int<player>)

Returns -1 if they've never been at war and otherwise what it says (0 means they have been but are not now at war).


INT TurnsSinceLastWar(int<player>, int<player>)

Returns  -1 if they've never been at war and otherwise what it says (0 means they are now at war).


INT AtWarWith(int<player>, int<player>)

Returns 1 or 0.


INT EffectiveWarWith(int<player>, int<player>) 

Returns 1 if first player is effectively at war with second player. (regard<100 but yet to attack/declare war?)


INT AtWarCount(int<player>)

Returns the number of civs that player is at war with.


INT EffectiveAtWarCount(int<player>)

Returns the number of civs that player is effectively at war with. (regard<100 but yet to attack/declare war?)


INT IsHostile(int<player>, int<player>)

 [?,only seen it return 0]


INT GetAtRiskCitiesValue(int<player>, int<player>) 

Returns the number of first player's cities that second player could attack on the next turn ?.


INT GetTotalValue(int<player>)

Returns the total value (whatever that is) of the player's cities. Used in Response Logic ( 2 REQUEST_GIVE_CITY)


INT GetMostAtRiskCity(player, foreigner, &cityvar)

Example: When asked for a ceasefire, an AI civ counters by asking for a city.

         HandleEvent(NewProposal) 'Example_ProposalResponseEvent' pre {
         int_t   first_type;
         city_t tmpcity;
              first_type = GetLastNewProposalType(player[0], player[1], 0);
   GetMostAtRiskCity(player[1], player[0], tmpcity);
              if (CityIsValid(tmpcity) && first_type == 32) {
  ConsiderResponse(player[0], player[1], 1000, 3, 1, tmpcity, 4);
             }
  else{
  ConsiderResponse(player[0], player[1], 10, 1);
  }
         }

INT FindCityToExtortFrom(player, foreigner, max_value, &cityvar)


Should be like GetMostAtRiskCity but no positive results yet


INT GetRelativeStrength(int<player>, int<player>)

Returns 0- very weak, 1- weak, 2- average, ...


INT  GetLastHotwarAttack(int<player>, int<player>)

Returns the number of turns since second player last Hotwar attacked (goals with Hotenemy target?) first player (9999 if never)


INT GetLastColdwarAttack(int<player>, int<player>)

Returns the number of turns since second player last Coldwar attacked (goals with Coldenemy target?) first player (9999 if never)


INT HasAgreement(int<player>, int<player>, int)

Returns 0 or 1. The final int is a proposalDB index


VOID CancelAgreement(int<player>, int<player>, int)

The final int is a proposalDB index


INT GetAgreementDuration(int<player>, int<player>, int)

The final int is a proposalDB index. Returns the number of turns the first player has had this agreement with the second.


INT  CanFormAlliance(int<player>,int<player)

[???]


INT  GetDesiredAdvanceFrom(int<player>, int<player>, int, int)


INT  GetStopResearchingAdvance(player[0], player[1])


INT  GetNextAdvance(player[0])

Returns the advanceDB index of whatever player[0] is researching



Appendix 2: The New Proposal List.

Remarks on Response Logic are gathered together from Interactions.txt. These look to me to be someone's "work in progress" notes and the resulting comments may not all be true. The motivation comments (e.g.,  8    REQUEST_BREAK_AGREEMENT, (MOTIVATION_DESIRE_ENLIST_FRIEND)) must refer to the conditions which initiate these proposals.

              0    NONE, icon ="updi01.tga", the default one
              1    OFFER_GIVE_CITY, icon ="updi01.tga", the default one
              2    REQUEST_GIVE_CITY, icon ="updi01.tga", the default one

                     'LandForPeace' triggered when:
                 1) Declared war exists between sender and receiver,
                 2) receiver trusts sender,
                 3) more than 50% of receiver's city value is at risk from sender
                            4) the total value of receivers at risk cities exceeds the value
                    of the requested city,
                            5) neither sender nor receiver has overwhelming military superiority.
     
                      then, COUNTER_PROPOSAL with request TREATY_CEASEFIRE.
                    REQUEST_GIVE_CITY + threat THREAT_ATTACK_CITY:
                      Reject if receivers cities at risk of conquest by the sender
                      exceeds 75% of the total city value in his nation. (Slic function)
                'Reject' triggered when:
                      1) No declared war between sender and receiver.
                      2) Sender does not have overwhelming (>30%) military superiority
                                    over receiver.
                 then, sender responds with RESPONSE_REJECT.
                'CaveInTo' triggered when:
                      1) TBD. (always)
                 then, sender responds with RESPONSE_ACCEPT.

              3    OFFER_WITHDRAW_TROOPS, icon ="updi01.tga", the default one
              4    REQUEST_WITHDRAW_TROOPS,  (REACTION) Image "updi47.tga"
                       'MutualWithdraw' triggered when:
                 1) Do not want war and
                 2) Sender has troops in our territory;
                 then, COUNTER_PROPOSAL with request for OFFER_WITHDRAW_TROOPS.
                 'PayTribute' triggered when:
                 1) Do not want war and
                 2) Sender has no troops in our territory
                 3) We are stronger;
                  then, COUNTER_PROPOSAL with request for OFFER_GIVE_GOLD.
              5    OFFER_STOP_PIRACY, 
              6    REQUEST_STOP_PIRACY,  (FEAR_PIRACY)Image "updi46.tga"
                       'StopPiracy' triggered when:
                 1) receiver does not lose any gold from piracy from sender.
                 2) sender loses gold from piracy by receiver.
                  then, COUNTER_PROPOSAL with request of OFFER_GIVE_GOLD:
   
             (3 * gold pirated by receiver each turn from sender).
                  'StopPiracy' triggered when:
                1) receiver loses more gold from senders piracy than receiver
                               loses from senders piracy.
     
                       then, COUNTER_PROPOSAL with request of OFFER_STOP_PIRACY
              7    OFFER_BREAK_AGREEMENT, 
              8    REQUEST_BREAK_AGREEMENT, (MOTIVATION_DESIRE_ENLIST_FRIEND)
                       
              9    OFFER_STOP_RESEARCH,
             10    REQUEST_STOP_RESEARCH, Image "updi45.tga"
                      Only valid if we are researching an advance from our stop
                 researching advance list.
                 Receivers with the scientist personality will always REJECT this.
                 If sender is not equal to or stronger than receiver militarily,
                 REJECT.  Otherwise, if they are currently researching the same
                 advance, COUNTER for them to top researching the same advance.  If
                 they are much stronger, and already have the advance they are
                 asking us to stop researching, then ask for a PEACE_TREATY.
             11    OFFER_REDUCE_NUCLEAR_WEAPONS,
             12    REQUEST_REDUCE_NUCLEAR_WEAPONS,
             13    OFFER_REDUCE_BIO_WEAPONS,
             14    REQUEST_REDUCE_BIO_WEAPONS,
             15    OFFER_REDUCE_NANO_WEAPONS,
             16    REQUEST_REDUCE_NANO_WEAPONS, all these= Image "updi01.tga"
                       REQUEST_REDUCE_{NUCLEAR, BIO, NANO}_WEAPONS:
                       Triggered for coldwar enemies with nuclear, bio or nano weapons.
                  Looks at ratio of his weapons to our cities and our weapons to his
                  cities.  Tries to get enemy to reduce to the point that our losses
                  would be half of his and so that our losses will never be more
                  than 50% of our cities.
 
             17    OFFER_GIVE_ADVANCE,
             18    REQUEST_GIVE_ADVANCE,   (DESIRE_SCIENCE)
                       'PayForAdvance' triggered when:
                   1) sender has not offered to pay (80% of advance cost) gold
                   2) sender can pay (80% of advance cost) gold
                   then, COUNTER_PROPOSAL with request of OFFER_GIVE_GOLD of (80% of
                   advance cost) gold.
                   'AdvanceExchange' triggered when:
                    1) sender has an advance that receiver wants.
                    2) advance that receiver wants costs less than 120% of advance sender
                                   requested.
                    then, COUNTER_PROPOSAL with request of OFFER_GIVE_ADVANCE of
                    advance receiver wants.
             19    OFFER_GIVE_GOLD, 
             20    REQUEST_GIVE_GOLD,
                       'AdvanceForGold' triggered when:
                   1) sender has an advance that receiver wants.
                   2) advance that receiver wants costs less than 120% of gold
                                  requested.
                   then, COUNTER_PROPOSAL with request of OFFER_GIVE_ADVANCE of
                   advance receiver wants.
             21    OFFER_REDUCE_POLLUTION, 
             22    REQUEST_REDUCE_POLLUTION,  Image "updi44.tga"
                       If receiver produces less than 50% of the pollution of sender,
                  then REJECT.  If receiver fears pollution disaster, then COUNTER
                  propose as follows: Ecotopians request sender to reduce pollution
                  to to 5% below their pollution level, diplomats reject if there is
                  no peace treaty or counters with a 10% reduction, and all others
                  counter with a 5% reduction.
 
             23    OFFER_MAP,
             24    REQUEST_MAP, (DESIRE_MAKE_FRIEND)
                       Always ACCEPT for allies and REJECT for anyone we are at war with
                       or have coldwar or less regard for.  If we have gotten their map
                       within the last 5 turns, REJECT.  In all other cases, counter with
                       a request for their map.
 
             25    OFFER_HONOR_MILITARY_AGREEMENT,
             26    REQUEST_HONOR_MILITARY_AGREEMENT,
                      If receiver at war with foreigner, has alliance with sender or has
                 lawful personality then ACCEPT.  If sender much stronger than
                 foreigner or receiver, then ACCEPT.  If foreigner much stronger than
                      sender, then REJECT.  Otherwise, REJECT if regard for sender not friendly
                 or higher or ACCEPT if regard for foreigner not friendly or
                 higher.
             27    OFFER_HONOR_POLLUTION_AGREEMENT,    Image "updi08.tga"
             28    REQUEST_HONOR_POLLUTION_AGREEMENT,  Image "updi08.tga"
                      If request to honor pollution agreement requests significantly
                 larger reduction than original pact, then REJECT.
                 If sender has not met pollution goal or half way to goal within 20
                 turns (10 turns for Ecotopian receivers), then counter with
                 request that sender honor their pollution agreement.
                 If receiver met pollution goal or half way to goal within 20
                 turns, then ACCEPT
 
             29    OFFER_END_EMBARGO,
             30    REQUEST_END_EMBARGO,   (DESIRE_GOLD, REACTION)  Image "updi48.tga"
                      If embargo has not continued for more than 5 turns, then REJECT.
                 If receiver does not have friendly regard for sender and sender
                 gets 50% or more gold from total trade than receiver, then COUNTER
                 asking for 2x the difference in gold from trade.
                      If sender has embargo against receiver, then COUNTER propose that
                 they end their embargo, otherwise, REJECT.
                 Otherwise, ACCEPT request.
 
             31    TREATY_DECLARE_WAR,  Image "updi02.tga"
                       1) War is declared if you attack someone or they attack you.
  
                  2) If you form an alliance with someone, you go to war with
                  anyone they are at war with. 
                  3) If AI not currently at war with anyone, has hot war level
                  regard towards you, has not been at war with you in the last 5
                  turns (or never), is not allied with you and shares a continent
                  with you, then it declares war.
 
             32    TREATY_CEASEFIRE,  (REQUEST_TREATY_CEASEFIRE:??)  Image "updi03.tga"
                        (FEAR_INVASION, FEAR_CITY_DEFENSE, FEAR_MILITARY_RANK)
                        Accept when 1) war has been declared between sender and receiver,
                        2) sender trusts receiver 3) sender does not want to be at war with
                   receiver. [GetDesireWarWith(i,j)?]
             33    TREATY_PEACE,   (REQUEST_TREATY_PEACE ??)
                        (DESIRE_MAKE_FRIEND)  Image "updi04.tga"
                       Accept when 1) no war has occurred between sender and receiver for
                  10 turns, 2) sender trusts receiver, 3) receiver has no troops on
                  our territory 4) no coldwar attacks have been made by receiver for
                  5 rounds 5) sender does not want to be at war with receiver and 6)
                  we have average regard towards them.
 
             34    TREATY_TRADE_PACT,  (DESIRE_MAKE_FRIEND, DESIRE_GOLD)Image "updi05.tga"
                       ACCEPT when 1) no war has occurred between sender and receiver for
                  10 turns, 2) receiver trusts sender, 3) sender has no troops on
                  receivers territory, 4) sender is not pirating receivers trade
                  routes, 5) more than 10% of the value of receivers trade is with
                  the sender. 6) value of senders trade with receiver does not
                  exceed twice receivers trade with sender.
             35    TREATY_RESEARCH_PACT, 
                        (DESIRE_MAKE_FRIEND, FEAR_SCIENCE_RANK) Image "updi06.tga"
                       ACCEPT when 1) no war has occurred between sender and receiver
                  for 10 turns, 2) receiver trusts sender, 3) sender has no troops
                  on our territory, 4) receiver generates less science than
                  sender per turn, or 5) receiver does not generate more than 20%
                  more science than sender.
             36    TREATY_MILITARY_PACT, Image "updi07.tga"
                     (DESIRE_MAKE_FRIEND, DESIRE_ENLIST_FRIEND, FEAR_MILITARY_RANK)
                        ACCEPT when 1) no war has occurred between sender and receiver
                   for 10 turns, 2) receiver trusts sender, 3) sender has no troops
                   on receivers territory, 3) we are at war and peace with the same
                        nations.
 
             37    TREATY_POLLUTION_PACT, Image "updi08.tga"
                       ACCEPT when 1) no war has occurred between sender and receiver for
                  10 turns, 2) receiver trusts sender, 3) sender has no troops on
                  our territory, 3) for Ecotopians, any proposal as long as sender
                  is producing no more than 5% of the pollution as receiver. For
                  diplomats, as long as sender is producing no more than 20% of the
                  pollution as receiver, and the proposal is not for more than 20%
                  reduction, and for everyone else, only if no more than 10%
                  reduction.
 
             38    TREATY_ALLIANCE. (DESIRE_MAKE_FRIEND, FEAR_MILITARY_RANK) Image "updi09.tga"
                       Accept when 1) no war has occurred between sender and receiver for
                  25 turns, 2) sender trusts receiver a lot, 3) receiver has no
                  troops on our territory 4) no coldwar attacks have been made by
                  receiver for 15 rounds 5) we are at war and peace with the same
                  nations and 6) have very high regard for receiver (ie. lots of
                  gold and advances gifts.
  Administration
This site is currently maintained by: BureauBert (Webmaster)  Maquiladora (Manager)  Legal Notice: Legal Notice Statistics: 71181 unique users since 02.05.2004 (» Site Statistics) Hosted by: WebhostOne
Share this page:  VK Facebook Twitter LiveJournal OK MR Google+ LinkedIn tumblr Pinterest Blogger Digg Evernote