Ticket #4431: petra_ally_requests_v0.5.patch

File petra_ally_requests_v0.5.patch, 10.5 KB (added by Sandarac, 7 years ago)

Update.

  • binaries/data/mods/public/simulation/ai/common-api/gamestate.js

     
    384384    return ret;
    385385};
    386386
     387m.GameState.prototype.getMutualAllies = function()
     388{
     389    let ret = [];
     390    for (let i in this.playerData.isMutualAlly)
     391        if (this.playerData.isMutualAlly[i] &&
     392            this.sharedScript.playersData[i].isMutualAlly[this.player])
     393            ret.push(+i);
     394    return ret;
     395};
     396
    387397m.GameState.prototype.isEntityAlly = function(ent)
    388398{
    389399    if (!ent)
  • binaries/data/mods/public/simulation/ai/petra/chatHelper.js

     
    6363    ]
    6464};
    6565
     66m.chatAnswerAllyRequestMessages = {
     67    "decline": [
     68        markForTranslation("I cannot accept your offer to be allies %(_player_)s.")
     69    ],
     70    "declineSuggestNeutral": [
     71        markForTranslation("I will not ally with you %(_player_)s, but I will consider a neutrality pact.")
     72    ],
     73    "declineRepeatedOffer": [
     74        markForTranslation("%(_player_)s, our previous alliance did not work out, so I must decline your offer.")
     75    ],
     76    "accept": [
     77        markForTranslation("I will accept your offer to become allies %(_player_)s. We will both benefit from this alliance.")
     78    ],
     79    "acceptWithTribute": [
     80        markForTranslation("I will ally with you %(_player_)s, but only if you send me a tribute of %(_amount_)s %(_resource_)s."),
     81        markForTranslation("%(_player_)s, you must send me a tribute of %(_amount_)s %(_resource_)s for me to accept an alliance.")
     82    ],
     83    "waitingForTribute": [
     84        markForTranslation("%(_player_)s, my offer still stands. I will ally with you if you send me a tribute of %(_amount_)s %(_resource_)s.")
     85    ]
     86};
     87
    6688m.chatLaunchAttack = function(gameState, player, type)
    6789{
    6890    Engine.PostCommand(PlayerID, {
     
    140162    });
    141163};
    142164
     165m.chatAnswerRequestAlly = function(gameState, player, response, requiredTribute)
     166{
     167    Engine.PostCommand(PlayerID, {
     168        "type": "aichat",
     169        "message": "/msg " + gameState.sharedScript.playersData[player].name + " " +
     170            pickRandom(this.chatAnswerAllyRequestMessages[response]),
     171        "translateMessage": true,
     172        "translateParameters": requiredTribute ? ["_amount_", "_resource_", "_player_"] : ["_player_"],
     173        "parameters": requiredTribute ?
     174            { "_amount_": requiredTribute.wanted, "_resource_": requiredTribute.type, "_player_": player } :
     175            { "_player_": player }
     176    });
     177};
     178
    143179return m;
    144180}(PETRA);
  • binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js

     
    55 * Manage the diplomacy:
    66 *     update our cooperative trait
    77 *     sent tribute to allies
     8 *     decide which player to turn against in "Last Man Standing" mode
     9 *     respond to diplomacy requests
    810 */
    911
     12/**
     13 * If a player sends us an ally request, an Object in this.allyRequests will be created
     14 * that includes the request status, and the amount and type of the resource tribute (if any)
     15 * that they must send in order for us to accept their request.
     16 * In addition, a message will be sent if the player has not sent us a tribute within a minute.
     17 * If two minutes pass without a tribute, we will decline their request.
     18 */
    1019m.DiplomacyManager = function(Config)
    1120{
    1221    this.Config = Config;
     
    1524    this.nextTributeRequest.set("all", 240);
    1625    this.betrayLapseTime = -1;
    1726    this.waitingToBetray = false;
     27    this.allyRequests = new Map();
    1828};
    1929
    2030/**
     31 * If there are any players that are allied with us but we are not allied with them,
     32 * treat this situation like an ally request.
     33 */
     34m.DiplomacyManager.prototype.init = function(gameState)
     35{
     36    for (let i = 1; i < gameState.sharedScript.playersData.length; ++i)
     37    {
     38        if (i === PlayerID)
     39            continue;
     40
     41        if (gameState.isPlayerMutualAlly(i))
     42            this.allyRequests.set(i, { "status": "accepted" });
     43        else if (gameState.sharedScript.playersData[i].isAlly[PlayerID] && !gameState.isPlayerAlly(i))
     44            this.handleAllyRequest(gameState, i);
     45    }
     46};
     47
     48/**
    2149 * Check if any allied needs help (tribute) and sent it if we have enough resource
    2250 * or ask for a tribute if we are in need and one ally can help
    2351 */
     
    86114    // or if our allies attack enemies inside our territory
    87115    for (let evt of events.TributeExchanged)
    88116    {
     117        if (evt.to === PlayerID && !gameState.isPlayerAlly(evt.from) && this.allyRequests.has(evt.from))
     118        {
     119            let request = this.allyRequests.get(evt.from);
     120            if (request.status === "waitingForTribute")
     121            {
     122                request.wanted -= evt.amounts[request.type];
     123
     124                if (request.wanted <= 0)
     125                {
     126                    if (this.Config.debug > 1)
     127                        API3.warn("Player " + uneval(evt.from) + " has sent the required tribute amount");
     128
     129                    this.changePlayerDiplomacy(gameState, evt.from, "ally");
     130                    request.status = "accepted";
     131                }
     132                else
     133                {
     134                    // Reset the warning sent to the player that reminds them to speed up the tributes
     135                    request.warnTime = gameState.ai.elapsedTime + 60;
     136                    request.sentWarning = false;
     137                }
     138            }
     139        }
     140
    89141        if (evt.to !== PlayerID || !gameState.isPlayerAlly(evt.from))
    90142            continue;
    91143        let tributes = 0;
     
    114166
    115167    if (events.DiplomacyChanged.length || events.PlayerDefeated.length || events.CeasefireEnded.length)
    116168        this.lastManStandingCheck(gameState);
     169
     170    for (let evt of events.DiplomacyChanged)
     171    {
     172        if (evt.otherPlayer !== PlayerID)
     173            continue;
     174
     175        if (this.allyRequests.has(evt.player) && !gameState.sharedScript.playersData[evt.player].isAlly[PlayerID])
     176        {
     177            // a player that had requested to be allies changed their stance with us
     178            let request = this.allyRequests.get(evt.player);
     179            if (request.status === "accepted")
     180                request.status = "allianceBroken";
     181            else if (request.status !== "allianceBroken")
     182                request.status = "declinedRequest";
     183        }
     184        else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerEnemy(evt.player))
     185            m.chatAnswerRequestAlly(gameState, evt.player, "declineSuggestNeutral");
     186        else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerNeutral(evt.player))
     187            this.handleAllyRequest(gameState, evt.player);
     188    }
    117189};
    118190
    119191/**
     
    186258    this.waitingToBetray = false;
    187259};
    188260
     261/**
     262 * Do not become allies with a player if the game would be over.
     263 * Overall, be reluctant to become allies with any one player.
     264 */
     265m.DiplomacyManager.prototype.handleAllyRequest = function(gameState, player)
     266{
     267    let response;
     268    let requiredTribute;
     269    let request = this.allyRequests.get(player);
     270
     271    // For any given ally request be likely to permanently decline
     272    if (!request && gameState.getPlayerCiv() !== gameState.getPlayerCiv(player) && Math.random() > 0.4 ||
     273        gameState.getEnemies().length < gameState.getMutualAllies().length ||
     274        gameState.ai.HQ.attackManager.currentEnemyPlayer === player)
     275    {
     276        this.allyRequests.set(player, { "status": "declinedRequest" });
     277        response = "decline";
     278    }
     279    else if (request)
     280    {
     281        if (request.status === "declinedRequest")
     282            response = "decline";
     283        else if (request.status === "allianceBroken") // Previous alliance was broken, so decline
     284            response = "declineRepeatedOffer";
     285        else if (request.status === "waitingForTribute")
     286        {
     287            response = "waitingForTribute";
     288            requiredTribute = request;
     289        }
     290    }
     291    else if (gameState.getEntities(player).length < gameState.getOwnEntities().length && Math.random() > 0.6)
     292    {
     293        response = "accept";
     294        this.changePlayerDiplomacy(gameState, player, "ally");
     295        this.allyRequests.set(player, { "status": "accepted" });
     296    }
     297    else
     298    {
     299        response = "acceptWithTribute";
     300        requiredTribute = gameState.ai.HQ.pickMostNeededResources(gameState)[0];
     301        requiredTribute.wanted = Math.max(1000, gameState.getOwnUnits().length * 10);
     302        this.allyRequests.set(player, {
     303            "status": "waitingForTribute",
     304            "wanted": requiredTribute.wanted,
     305            "type": requiredTribute.type,
     306            "warnTime": gameState.ai.elapsedTime + 60,
     307            "sentWarning": false
     308        });
     309    }
     310    m.chatAnswerRequestAlly(gameState, player, response, requiredTribute);
     311};
     312
    189313m.DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, player, newDiplomaticStance)
    190314{
    191315    Engine.PostCommand(PlayerID, { "type": "diplomacy", "player": player, "to": newDiplomaticStance });
     
    195319        m.chatNewDiplomacy(gameState, player, newDiplomaticStance);
    196320};
    197321
     322m.DiplomacyManager.prototype.checkRequestedTributes = function(gameState)
     323{
     324    for (let [player, data] of this.allyRequests.entries())
     325        if (data.status === "waitingForTribute" && gameState.ai.elapsedTime > data.warnTime)
     326        {
     327            if (data.sentWarning)
     328            {
     329                this.allyRequests.delete(player);
     330                m.chatAnswerRequestAlly(gameState, player, "decline");
     331            }
     332            else
     333            {
     334                data.sentWarning = true;
     335                data.warnTime = gameState.ai.elapsedTime + 60;
     336                m.chatAnswerRequestAlly(gameState, player, "waitingForTribute", {
     337                    "wanted": data.wanted,
     338                    "type": data.type
     339                });
     340            }
     341        }
     342};
     343
    198344m.DiplomacyManager.prototype.update = function(gameState, events)
    199345{
    200346    this.checkEvents(gameState, events);
     
    204350
    205351    if (this.waitingToBetray && gameState.ai.elapsedTime > this.betrayLapseTime)
    206352        this.lastManStandingCheck(gameState);
     353
     354    this.checkRequestedTributes(gameState);
    207355};
    208356
    209357m.DiplomacyManager.prototype.Serialize = function()
     
    212360        "nextTributeUpdate": this.nextTributeUpdate,
    213361        "nextTributeRequest": this.nextTributeRequest,
    214362        "betrayLapseTime": this.betrayLapseTime,
    215         "waitingToBetray": this.waitingToBetray
     363        "waitingToBetray": this.waitingToBetray,
     364        "allyRequests": this.allyRequests
    216365    };
    217366};
    218367
  • binaries/data/mods/public/simulation/ai/petra/startingStrategy.js

     
    1313    this.attackManager.init(gameState);
    1414    this.navalManager.init(gameState);
    1515    this.tradeManager.init(gameState);
     16    this.diplomacyManager.init(gameState);
    1617
    1718    // Make a list of buildable structures from the config file
    1819    this.structureAnalysis(gameState);