Ticket #4431: petra_ally_requests_v0.4.patch

File petra_ally_requests_v0.4.patch, 11.0 KB (added by Sandarac, 7 years ago)

Fix issues in diplomacyManager pointed out by mimo.

  • binaries/data/mods/public/gui/session/messages.js

     
    185185        g_IsObserver,
    186186
    187187    "/msg": (senderID, addresseeGUID) =>
    188         addresseeGUID == Engine.GetPlayerGUID()
     188        addresseeGUID == (Engine.GetPlayerGUID() || "local")
    189189};
    190190
    191191/**
  • binaries/data/mods/public/simulation/ai/common-api/gamestate.js

     
    384384    return ret;
    385385};
    386386
     387m.GameState.prototype.getExclusiveMutualAllies = function()
     388{   // Player is not included
     389    let ret = [];
     390    for (let i in this.playerData.isAlly)
     391        if (this.playerData.isAlly[i] &&
     392            this.sharedScript.playersData[i].isAlly[this.player] && +i !== 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, {
    6991        "type": "aichat",
    70         "message": "/allies " + pickRandom(this.launchAttackMessages[type === "HugeAttack" ? type : "other"]),
     92        "message": "/allies " + pickRandom(this.launchAttackMessages[type === "HugeAttack" ? "hugeAttack" : "other"]),
    7193        "translateMessage": true,
    7294        "translateParameters": ["_player_"],
    7395        "parameters": { "_player_": player }
     
    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
     30m.DiplomacyManager.prototype.init = function(gameState)
     31{
     32    for (let ally of gameState.getExclusiveMutualAllies())
     33        this.allyRequests.set(ally, { "status": "accepted" });
     34};
     35
    2036/**
    2137 * Check if any allied needs help (tribute) and sent it if we have enough resource
    2238 * or ask for a tribute if we are in need and one ally can help
     
    86102    // or if our allies attack enemies inside our territory
    87103    for (let evt of events.TributeExchanged)
    88104    {
     105        if (evt.to === PlayerID && !gameState.isPlayerAlly(evt.from) && this.allyRequests.has(evt.from))
     106        {
     107            let request = this.allyRequests.get(evt.from);
     108            if (request.status === "waitingForTribute")
     109            {
     110                request.wanted -= evt.amounts[request.type];
     111
     112                if (request.wanted <= 0)
     113                {
     114                    if (this.Config.debug > 1)
     115                        API3.warn("Player " + uneval(evt.from) + " has sent the required tribute amount");
     116
     117                    this.changePlayerDiplomacy(gameState, evt.from, "ally");
     118                    request.status = "accepted";
     119                }
     120                else
     121                {
     122                    // Reset the warning sent to the player that reminds them to speed up the tributes
     123                    request.warnTime = gameState.ai.elapsedTime + 60;
     124                    request.sentWarning = false;
     125                }
     126            }
     127        }
     128
    89129        if (evt.to !== PlayerID || !gameState.isPlayerAlly(evt.from))
    90130            continue;
    91131        let tributes = 0;
     
    114154
    115155    if (events.DiplomacyChanged.length || events.PlayerDefeated.length || events.CeasefireEnded.length)
    116156        this.lastManStandingCheck(gameState);
     157
     158    for (let evt of events.DiplomacyChanged)
     159    {
     160        if (evt.otherPlayer !== PlayerID)
     161            continue;
     162
     163        if (this.allyRequests.has(evt.player) && !gameState.sharedScript.playersData[evt.player].isAlly[PlayerID])
     164        {
     165            // a player that had requested to be allies changed their stance with us
     166            let request = this.allyRequests.get(evt.player);
     167            if (request.status === "accepted")
     168                request.status = "allianceBroken";
     169            else if (request.status !== "allianceBroken")
     170                request.status = "declinedRequest";
     171        }
     172        else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerEnemy(evt.player))
     173            m.chatAnswerRequestAlly(gameState, evt.player, "declineSuggestNeutral");
     174        else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerNeutral(evt.player))
     175            this.handleAllyRequest(gameState, evt.player);
     176    }
    117177};
    118178
    119179/**
     
    186246    this.waitingToBetray = false;
    187247};
    188248
     249/**
     250 * Do not become allies with a player if the game would be over.
     251 * Overall, be reluctant to become allies with any one player.
     252 */
     253m.DiplomacyManager.prototype.handleAllyRequest = function(gameState, player)
     254{
     255    let response;
     256    let requiredTribute;
     257    let request = this.allyRequests.get(player);
     258
     259    // For any given ally request be likely to permanently decline
     260    if (!request && gameState.getPlayerCiv() !== gameState.getPlayerCiv(player) && Math.random() > 0.4 ||
     261        gameState.getEnemies().length < gameState.getExclusiveMutualAllies().length ||
     262        gameState.ai.HQ.attackManager.currentEnemyPlayer === player)
     263    {
     264        this.allyRequests.set(player, { "status": "declinedRequest" });
     265        response = "decline";
     266    }
     267    else if (request)
     268    {
     269        if (request.status === "declinedRequest")
     270            response = "decline";
     271        else if (request.status === "allianceBroken") // Previous alliance was broken, so decline
     272            response = "declineRepeatedOffer";
     273        else if (request.status === "waitingForTribute")
     274        {
     275            response = "waitingForTribute";
     276            requiredTribute = request;
     277        }
     278    }
     279    else if (gameState.getEntities(player).length < gameState.getOwnEntities().length && Math.random() > 0.6)
     280    {
     281        response = "accept";
     282        this.changePlayerDiplomacy(gameState, player, "ally");
     283        this.allyRequests.set(player, { "status": "accepted" });
     284    }
     285    else
     286    {
     287        response = "acceptWithTribute";
     288        requiredTribute = gameState.ai.HQ.pickMostNeededResources(gameState)[0];
     289        requiredTribute.wanted = Math.max(1000, gameState.getOwnUnits().length * 10);
     290        this.allyRequests.set(player, {
     291            "status": "waitingForTribute",
     292            "wanted": requiredTribute.wanted,
     293            "type": requiredTribute.type,
     294            "warnTime": gameState.ai.elapsedTime + 60,
     295            "sentWarning": false
     296        });
     297    }
     298    m.chatAnswerRequestAlly(gameState, player, response, requiredTribute);
     299};
     300
    189301m.DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, player, newDiplomaticStance)
    190302{
    191303    Engine.PostCommand(PlayerID, { "type": "diplomacy", "player": player, "to": newDiplomaticStance });
     
    195307        m.chatNewDiplomacy(gameState, player, newDiplomaticStance);
    196308};
    197309
     310m.DiplomacyManager.prototype.allyRequestsTributesCheck = function(gameState)
     311{
     312    for (let [player, data] of this.allyRequests.entries())
     313        if (data.status === "waitingForTribute" && gameState.ai.elapsedTime > data.warnTime)
     314        {
     315            if (data.sentWarning)
     316            {
     317                this.allyRequests.delete(player);
     318                m.chatAnswerRequestAlly(gameState, player, "decline");
     319            }
     320            else
     321            {
     322                data.sentWarning = true;
     323                data.warnTime = gameState.ai.elapsedTime + 60;
     324                m.chatAnswerRequestAlly(gameState, player, "waitingForTribute", {
     325                    "wanted": data.wanted,
     326                    "type": data.type
     327                });
     328            }
     329        }
     330};
     331
    198332m.DiplomacyManager.prototype.update = function(gameState, events)
    199333{
    200334    this.checkEvents(gameState, events);
     
    204338
    205339    if (this.waitingToBetray && gameState.ai.elapsedTime > this.betrayLapseTime)
    206340        this.lastManStandingCheck(gameState);
     341
     342    this.allyRequestsTributesCheck(gameState);
    207343};
    208344
    209345m.DiplomacyManager.prototype.Serialize = function()
     
    212348        "nextTributeUpdate": this.nextTributeUpdate,
    213349        "nextTributeRequest": this.nextTributeRequest,
    214350        "betrayLapseTime": this.betrayLapseTime,
    215         "waitingToBetray": this.waitingToBetray
     351        "waitingToBetray": this.waitingToBetray,
     352        "allyRequests": this.allyRequests
    216353    };
    217354};
    218355
  • 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);