From 5725542fc4d9eea366d4a643e924ad496b5b0d63 Mon Sep 17 00:00:00 2001 From: mimo Date: Sat, 21 Jan 2017 10:59:18 +0000 Subject: [PATCH] petra: respond to neutral requests from enemies, patch by Sandarac, refs #4431 This was SVN commit r19161. --- .../simulation/ai/petra/attackManager.js | 20 ++++ .../public/simulation/ai/petra/chatHelper.js | 68 ++++++++----- .../simulation/ai/petra/diplomacyManager.js | 97 +++++++++++-------- .../simulation/ai/petra/startingStrategy.js | 2 - 4 files changed, 120 insertions(+), 67 deletions(-) diff --git a/binaries/data/mods/public/simulation/ai/petra/attackManager.js b/binaries/data/mods/public/simulation/ai/petra/attackManager.js index 803c22529a..9e11943678 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -455,6 +455,26 @@ m.AttackManager.prototype.getEnemyPlayer = function(gameState, attack) return enemyPlayer; }; +/** f.e. if we have changed diplomacy with another player. */ +m.AttackManager.prototype.cancelAttacksAgainstPlayer = function(player) +{ + for (let attackType in this.upcomingAttacks) + for (let attack of this.upcomingAttacks[attackType]) + if (attack.targetPlayer === player) + attack.targetPlayer = undefined; + + for (let attackType in this.startedAttacks) + for (let i = 0; i < this.startedAttacks[attackType].length; ++i) + { + let attack = this.startedAttacks[attackType][i]; + if (attack.targetPlayer === player) + { + attack.Abort(gameState); + this.startedAttacks[attackType].splice(i--, 1); + } + } +}; + m.AttackManager.prototype.Serialize = function() { let properties = { diff --git a/binaries/data/mods/public/simulation/ai/petra/chatHelper.js b/binaries/data/mods/public/simulation/ai/petra/chatHelper.js index 32b5b8bea0..2d346a55bc 100644 --- a/binaries/data/mods/public/simulation/ai/petra/chatHelper.js +++ b/binaries/data/mods/public/simulation/ai/petra/chatHelper.js @@ -63,26 +63,50 @@ m.newDiplomacyMessages = { ] }; -m.answerAllyRequestMessages = { - "decline": [ - markForTranslation("I cannot accept your offer to be allies %(_player_)s.") - ], - "declineSuggestNeutral": [ - markForTranslation("I will not ally with you %(_player_)s, but I will consider a neutrality pact.") - ], - "declineRepeatedOffer": [ - markForTranslation("%(_player_)s, our previous alliance did not work out, so I must decline your offer.") - ], - "accept": [ - markForTranslation("I will accept your offer to become allies %(_player_)s. We will both benefit from this alliance.") - ], - "acceptWithTribute": [ - markForTranslation("I will ally with you %(_player_)s, but only if you send me a tribute of %(_amount_)s %(_resource_)s."), - markForTranslation("%(_player_)s, you must send me a tribute of %(_amount_)s %(_resource_)s for me to accept an alliance.") - ], - "waitingForTribute": [ - markForTranslation("%(_player_)s, my offer still stands. I will ally with you if you send me a tribute of %(_amount_)s %(_resource_)s.") - ] +m.answerDiplomacyRequestMessages = { + "ally": { + "decline": [ + markForTranslation("I cannot accept your offer to be allies %(_player_)s.") + ], + "declineSuggestNeutral": [ + markForTranslation("I will not ally with you %(_player_)s, but I will consider a neutrality pact.") + ], + "declineRepeatedOffer": [ + markForTranslation("%(_player_)s, our previous alliance did not work out, so I must decline your offer.") + ], + "accept": [ + markForTranslation("I will accept your offer to become allies %(_player_)s. We will both benefit from this alliance.") + ], + "acceptWithTribute": [ + markForTranslation("I will ally with you %(_player_)s, but only if you send me a tribute of %(_amount_)s %(_resource_)s."), + markForTranslation("%(_player_)s, you must send me a tribute of %(_amount_)s %(_resource_)s for me to accept an alliance.") + ], + "waitingForTribute": [ + markForTranslation("%(_player_)s, my offer still stands. I will ally with you if you send me a tribute of %(_amount_)s %(_resource_)s."), + markForTranslation("%(_player_)s, if you do not send me part of the %(_amount_)s %(_resource_)s tribute soon, I will break off our negotiations.") + ] + }, + "neutral": { + "decline": [ + markForTranslation("I will not become neutral with you %(_player_)s."), + markForTranslation("%(_player_)s, I must decline your request for a neutrality pact.") + ], + "declineRepeatedOffer": [ + markForTranslation("Our previous neutrality agreement ended in failure %(_player_)s; I will not consider another one.") + ], + "accept": [ + markForTranslation("I welcome your request for peace between our civilizations %(_player_)s. I will accept."), + markForTranslation("%(_player_)s, I will accept your neutrality request. May both our civilizations benefit.") + ], + "acceptWithTribute": [ + markForTranslation("If you tribute me %(_amount_)s %(_resource_)s, I will accept your neutrality request %(_player_)s."), + markForTranslation("%(_player_)s, if you send me %(_amount_)s %(_resource_)s, I will accept a neutrality pact.") + ], + "waitingForTribute": [ + markForTranslation("%(_player_)s, I will not accept your neutrality request unless you tribute me %(_amount_)s %(_resource_)s soon."), + markForTranslation("%(_player_)s, if you do not send me part of the %(_amount_)s %(_resource_)s tribute soon, I will break off our negotiations.") + ] + } }; m.chatLaunchAttack = function(gameState, player, type) @@ -162,12 +186,12 @@ m.chatNewDiplomacy = function(gameState, player, newDiplomaticStance) }); }; -m.chatAnswerRequestAlly = function(gameState, player, response, requiredTribute) +m.chatAnswerRequestDiplomacy = function(gameState, player, requestType, response, requiredTribute) { Engine.PostCommand(PlayerID, { "type": "aichat", "message": "/msg " + gameState.sharedScript.playersData[player].name + " " + - pickRandom(this.answerAllyRequestMessages[response]), + pickRandom(this.answerDiplomacyRequestMessages[requestType][response]), "translateMessage": true, "translateParameters": requiredTribute ? ["_amount_", "_resource_", "_player_"] : ["_player_"], "parameters": requiredTribute ? diff --git a/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js b/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js index c63bd4d77e..f57c5f4ebd 100644 --- a/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js @@ -10,7 +10,7 @@ var PETRA = function(m) */ /** - * If a player sends us an ally request, an Object in this.allyRequests will be created + * If a player sends us an ally or neutral request, an Object in this.diplomacyRequests will be created * that includes the request status, and the amount and type of the resource tribute (if any) * that they must send in order for us to accept their request. * In addition, a message will be sent if the player has not sent us a tribute within a minute. @@ -24,24 +24,29 @@ m.DiplomacyManager = function(Config) this.nextTributeRequest.set("all", 240); this.betrayLapseTime = -1; this.waitingToBetray = false; - this.allyRequests = new Map(); + this.diplomacyRequests = new Map(); }; /** - * If there are any players that are allied with us but we are not allied with them, - * treat this situation like an ally request. + * If there are any players that are allied/neutral with us but we are not allied/neutral with them, + * treat this situation like an ally/neutral request. */ m.DiplomacyManager.prototype.init = function(gameState) { + if (!gameState.getAlliedVictory() && !gameState.isCeasefireActive()) + this.lastManStandingCheck(gameState); + for (let i = 1; i < gameState.sharedScript.playersData.length; ++i) { if (i === PlayerID) continue; if (gameState.isPlayerMutualAlly(i)) - this.allyRequests.set(i, { "status": "accepted" }); + this.diplomacyRequests.set(i, { "requestType": "ally", "status": "accepted" }); else if (gameState.sharedScript.playersData[i].isAlly[PlayerID]) - this.handleAllyRequest(gameState, i); + this.handleDiplomacyRequest(gameState, i, "ally"); + else if (gameState.sharedScript.playersData[i].isNeutral[PlayerID] && gameState.isPlayerEnemy(i)) + this.handleDiplomacyRequest(gameState, i, "neutral"); } }; @@ -114,9 +119,9 @@ m.DiplomacyManager.prototype.checkEvents = function (gameState, events) // or if our allies attack enemies inside our territory for (let evt of events.TributeExchanged) { - if (evt.to === PlayerID && !gameState.isPlayerAlly(evt.from) && this.allyRequests.has(evt.from)) + if (evt.to === PlayerID && !gameState.isPlayerAlly(evt.from) && this.diplomacyRequests.has(evt.from)) { - let request = this.allyRequests.get(evt.from); + let request = this.diplomacyRequests.get(evt.from); if (request.status === "waitingForTribute") { request.wanted -= evt.amounts[request.type]; @@ -126,10 +131,10 @@ m.DiplomacyManager.prototype.checkEvents = function (gameState, events) if (this.Config.debug > 1) API3.warn("Player " + uneval(evt.from) + " has sent the required tribute amount"); - this.changePlayerDiplomacy(gameState, evt.from, "ally"); + this.changePlayerDiplomacy(gameState, evt.from, request.requestType); request.status = "accepted"; } - else + else if (evt.amounts[request.type] > 0) { // Reset the warning sent to the player that reminds them to speed up the tributes request.warnTime = gameState.ai.elapsedTime + 60; @@ -172,31 +177,24 @@ m.DiplomacyManager.prototype.checkEvents = function (gameState, events) if (evt.otherPlayer !== PlayerID) continue; - if (this.allyRequests.has(evt.player) && !gameState.sharedScript.playersData[evt.player].isAlly[PlayerID]) + if (this.diplomacyRequests.has(evt.player) && !gameState.sharedScript.playersData[evt.player].isAlly[PlayerID]) { // a player that had requested to be allies changed their stance with us - let request = this.allyRequests.get(evt.player); + let request = this.diplomacyRequests.get(evt.player); if (request.status === "accepted") request.status = "allianceBroken"; else if (request.status !== "allianceBroken") request.status = "declinedRequest"; } else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerEnemy(evt.player)) - m.chatAnswerRequestAlly(gameState, evt.player, "declineSuggestNeutral"); + m.chatAnswerRequestDiplomacy(gameState, evt.player, "ally", "declineSuggestNeutral"); else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerNeutral(evt.player)) - this.handleAllyRequest(gameState, evt.player); + this.handleDiplomacyRequest(gameState, evt.player, "ally"); + else if (gameState.sharedScript.playersData[evt.player].isNeutral[PlayerID] && gameState.isPlayerEnemy(evt.player)) + this.handleDiplomacyRequest(gameState, evt.player, "neutral"); } }; -/** - * Check the diplomacy at the start of the game - */ -m.DiplomacyManager.prototype.diplomacyCheck = function(gameState) -{ - if (!gameState.getAlliedVictory() && !gameState.isCeasefireActive()) - this.lastManStandingCheck(gameState); -}; - /** * If the "Last Man Standing" option is enabled, check if the only remaining players are allies or neutral. * If so, turn against the strongest first, but be more likely to first turn against neutral players, if there are any. @@ -253,30 +251,39 @@ m.DiplomacyManager.prototype.lastManStandingCheck = function(gameState) } if (playerToTurnAgainst) + { this.changePlayerDiplomacy(gameState, playerToTurnAgainst, "enemy"); + let request = this.diplomacyRequests.get(playerToTurnAgainst); + if (request && request.status !== "allianceBroken") + { + if (request.status === "waitingForTribute") + m.chatAnswerRequestDiplomacy(gameState, player, request.requestType, "decline"); + request.status = request.status === "accepted" ? "allianceBroken" : "declinedRequest"; + } + } this.betrayLapseTime = -1; this.waitingToBetray = false; }; /** * Do not become allies with a player if the game would be over. - * Overall, be reluctant to become allies with any one player. + * Overall, be reluctant to become allies with any one player, but be more likely to accept neutral requests. */ -m.DiplomacyManager.prototype.handleAllyRequest = function(gameState, player) +m.DiplomacyManager.prototype.handleDiplomacyRequest = function(gameState, player, requestType) { let response; let requiredTribute; - let request = this.allyRequests.get(player); + let request = this.diplomacyRequests.get(player); + let moreEnemiesThanAllies = gameState.getEnemies().length > gameState.getMutualAllies().length; - // For any given ally request be likely to permanently decline + // For any given diplomacy request be likely to permanently decline if (!request && gameState.getPlayerCiv() !== gameState.getPlayerCiv(player) && Math.random() > 0.4 || - gameState.getEnemies().length < gameState.getMutualAllies().length || - gameState.ai.HQ.attackManager.currentEnemyPlayer === player) + !moreEnemiesThanAllies || gameState.ai.HQ.attackManager.currentEnemyPlayer === player) { - this.allyRequests.set(player, { "status": "declinedRequest" }); + this.diplomacyRequests.set(player, { "requestType": requestType, "status": "declinedRequest" }); response = "decline"; } - else if (request) + else if (request && request.status !== "accepted" && request.requestType !== "ally") { if (request.status === "declinedRequest") response = "decline"; @@ -288,30 +295,34 @@ m.DiplomacyManager.prototype.handleAllyRequest = function(gameState, player) requiredTribute = request; } } - else if (gameState.getEntities(player).length < gameState.getOwnEntities().length && Math.random() > 0.6) + else if (requestType === "ally" && gameState.getEntities(player).length < gameState.getOwnEntities().length && + Math.random() > 0.6 || requestType === "neutral" && moreEnemiesThanAllies && Math.random() > 0.2) { response = "accept"; - this.changePlayerDiplomacy(gameState, player, "ally"); - this.allyRequests.set(player, { "status": "accepted" }); + this.changePlayerDiplomacy(gameState, player, requestType); + this.diplomacyRequests.set(player, { "requestType": requestType, "status": "accepted" }); } else { response = "acceptWithTribute"; requiredTribute = gameState.ai.HQ.pickMostNeededResources(gameState)[0]; - requiredTribute.wanted = Math.max(1000, gameState.getOwnUnits().length * 10); - this.allyRequests.set(player, { + requiredTribute.wanted = Math.max(1000, gameState.getOwnUnits().length * requestType === "ally" ? 10 : 5) + this.diplomacyRequests.set(player, { "status": "waitingForTribute", "wanted": requiredTribute.wanted, "type": requiredTribute.type, "warnTime": gameState.ai.elapsedTime + 60, - "sentWarning": false + "sentWarning": false, + "requestType": requestType }); } - m.chatAnswerRequestAlly(gameState, player, response, requiredTribute); + m.chatAnswerRequestDiplomacy(gameState, player, requestType, response, requiredTribute); }; m.DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, player, newDiplomaticStance) { + if (gameState.isPlayerEnemy(player) && (newDiplomaticStance === "ally" || newDiplomaticStance === "neutral")) + gameState.ai.HQ.attackManager.cancelAttacksAgainstPlayer(player); Engine.PostCommand(PlayerID, { "type": "diplomacy", "player": player, "to": newDiplomaticStance }); if (this.Config.debug > 1) API3.warn("diplomacy stance with player " + player + " is now " + newDiplomaticStance); @@ -321,19 +332,19 @@ m.DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, player, m.DiplomacyManager.prototype.checkRequestedTributes = function(gameState) { - for (let [player, data] of this.allyRequests.entries()) + for (let [player, data] of this.diplomacyRequests) if (data.status === "waitingForTribute" && gameState.ai.elapsedTime > data.warnTime) { if (data.sentWarning) { - this.allyRequests.delete(player); - m.chatAnswerRequestAlly(gameState, player, "decline"); + this.diplomacyRequests.delete(player); + m.chatAnswerRequestDiplomacy(gameState, player, data.requestType, "decline"); } else { data.sentWarning = true; data.warnTime = gameState.ai.elapsedTime + 60; - m.chatAnswerRequestAlly(gameState, player, "waitingForTribute", { + m.chatAnswerRequestDiplomacy(gameState, player, data.requestType, "waitingForTribute", { "wanted": data.wanted, "type": data.type }); @@ -361,7 +372,7 @@ m.DiplomacyManager.prototype.Serialize = function() "nextTributeRequest": this.nextTributeRequest, "betrayLapseTime": this.betrayLapseTime, "waitingToBetray": this.waitingToBetray, - "allyRequests": this.allyRequests + "diplomacyRequests": this.diplomacyRequests }; }; diff --git a/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js b/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js index 21c02a5133..2c6fc94de3 100644 --- a/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js +++ b/binaries/data/mods/public/simulation/ai/petra/startingStrategy.js @@ -55,8 +55,6 @@ m.HQ.prototype.gameAnalysis = function(gameState) // configure our first base strategy if (this.baseManagers.length > 1) this.configFirstBase(gameState); - - this.diplomacyManager.diplomacyCheck(gameState); }; /**