Line data Source code
1 : /**
2 : * Manage the diplomacy:
3 : * update our cooperative trait
4 : * sent tribute to allies
5 : * decide which player to turn against in "Last Man Standing" mode
6 : * respond to diplomacy requests
7 : * send diplomacy requests to other players (rarely)
8 : */
9 :
10 : /**
11 : * If a player sends us an ally or neutral request, an Object in this.receivedDiplomacyRequests will be created
12 : * that includes the request status, and the amount and type of the resource tribute (if any)
13 : * that they must send in order for us to accept their request.
14 : * In addition, a message will be sent if the player has not sent us a tribute within a minute.
15 : * If two minutes pass without a tribute, we will decline their request.
16 : *
17 : * If we send a diplomacy request to another player, an Object in this.sentDiplomacyRequests will be created,
18 : * which consists of the requestType (i.e. "ally" or "neutral") and the timeSent. A chat message will be sent
19 : * to the other player, and AI players will actually be informed of the request by a DiplomacyRequest event
20 : * sent through AIInterface. It is expected that the other player will change their diplomacy stance to the stance
21 : * that we suggested within a period of time, or else the request will be deleted from this.sentDiplomacyRequests.
22 : */
23 0 : PETRA.DiplomacyManager = function(Config)
24 : {
25 0 : this.Config = Config;
26 0 : this.nextTributeUpdate = 90;
27 0 : this.nextTributeRequest = new Map();
28 0 : this.nextTributeRequest.set("all", 240);
29 0 : this.betrayLapseTime = -1;
30 0 : this.waitingToBetray = false;
31 0 : this.betrayWeighting = 150;
32 0 : this.receivedDiplomacyRequests = new Map();
33 0 : this.sentDiplomacyRequests = new Map();
34 0 : this.sentDiplomacyRequestLapseTime = 120 + randFloat(10, 100);
35 : };
36 :
37 : /**
38 : * If there are any players that are allied/neutral with us but we are not allied/neutral with them,
39 : * treat this situation like an ally/neutral request.
40 : */
41 0 : PETRA.DiplomacyManager.prototype.init = function(gameState)
42 : {
43 0 : this.lastManStandingCheck(gameState);
44 :
45 0 : for (let i = 1; i < gameState.sharedScript.playersData.length; ++i)
46 : {
47 0 : if (i === PlayerID)
48 0 : continue;
49 :
50 0 : if (gameState.isPlayerMutualAlly(i))
51 0 : this.receivedDiplomacyRequests.set(i, { "requestType": "ally", "status": "accepted" });
52 0 : else if (gameState.sharedScript.playersData[i].isAlly[PlayerID])
53 0 : this.handleDiplomacyRequest(gameState, i, "ally");
54 0 : else if (gameState.sharedScript.playersData[i].isNeutral[PlayerID] && gameState.isPlayerEnemy(i))
55 0 : this.handleDiplomacyRequest(gameState, i, "neutral");
56 : }
57 : };
58 :
59 : /**
60 : * Check if any allied needs help (tribute) and sent it if we have enough resource
61 : * or ask for a tribute if we are in need and one ally can help
62 : */
63 0 : PETRA.DiplomacyManager.prototype.tributes = function(gameState)
64 : {
65 0 : this.nextTributeUpdate = gameState.ai.elapsedTime + 30;
66 0 : let resTribCodes = Resources.GetTributableCodes();
67 0 : if (!resTribCodes.length)
68 0 : return;
69 0 : let totalResources = gameState.getResources();
70 0 : let availableResources = gameState.ai.queueManager.getAvailableResources(gameState);
71 : let mostNeeded;
72 0 : for (let i = 1; i < gameState.sharedScript.playersData.length; ++i)
73 : {
74 0 : if (i === PlayerID || !gameState.isPlayerAlly(i) || gameState.ai.HQ.attackManager.defeated[i])
75 0 : continue;
76 0 : let donor = gameState.getAlliedVictory() || gameState.getEntities(i).length < gameState.getOwnEntities().length;
77 0 : let allyResources = gameState.sharedScript.playersData[i].resourceCounts;
78 0 : let allyPop = gameState.sharedScript.playersData[i].popCount;
79 0 : let tribute = {};
80 0 : let toSend = false;
81 0 : for (let res of resTribCodes)
82 : {
83 0 : if (donor && availableResources[res] > 200 && allyResources[res] < 0.2 * availableResources[res])
84 : {
85 0 : tribute[res] = Math.floor(0.3*availableResources[res] - allyResources[res]);
86 0 : toSend = true;
87 : }
88 0 : else if (donor && allyPop < Math.min(30, 0.5*gameState.getPopulation()) && totalResources[res] > 500 && allyResources[res] < 100)
89 : {
90 0 : tribute[res] = 100;
91 0 : toSend = true;
92 : }
93 0 : else if (this.Config.chat && availableResources[res] === 0 && allyResources[res] > totalResources[res] + 600)
94 : {
95 0 : if (gameState.ai.elapsedTime < this.nextTributeRequest.get("all"))
96 0 : continue;
97 0 : if (this.nextTributeRequest.has(res) && gameState.ai.elapsedTime < this.nextTributeRequest.get(res))
98 0 : continue;
99 0 : if (!mostNeeded)
100 0 : mostNeeded = gameState.ai.HQ.pickMostNeededResources(gameState, resTribCodes);
101 0 : for (let k = 0; k < mostNeeded.length; ++k)
102 : {
103 0 : if (mostNeeded[k].type == res && mostNeeded[k].wanted > 0)
104 : {
105 0 : this.nextTributeRequest.set("all", gameState.ai.elapsedTime + 90);
106 0 : this.nextTributeRequest.set(res, gameState.ai.elapsedTime + 240);
107 0 : PETRA.chatRequestTribute(gameState, res);
108 0 : if (this.Config.debug > 1)
109 0 : API3.warn("Tribute on " + res + " requested to player " + i);
110 0 : break;
111 : }
112 : }
113 : }
114 : }
115 0 : if (!toSend)
116 0 : continue;
117 0 : if (this.Config.debug > 1)
118 0 : API3.warn("Tribute " + uneval(tribute) + " sent to player " + i);
119 0 : if (this.Config.chat)
120 0 : PETRA.chatSentTribute(gameState, i);
121 0 : Engine.PostCommand(PlayerID, { "type": "tribute", "player": i, "amounts": tribute });
122 : }
123 : };
124 :
125 0 : PETRA.DiplomacyManager.prototype.checkEvents = function(gameState, events)
126 : {
127 : // Increase slowly the cooperative personality trait either when we receive tribute from our allies
128 : // or if our allies attack enemies inside our territory
129 0 : for (let evt of events.TributeExchanged)
130 : {
131 0 : if (evt.to === PlayerID && !gameState.isPlayerAlly(evt.from) && this.receivedDiplomacyRequests.has(evt.from))
132 : {
133 0 : let request = this.receivedDiplomacyRequests.get(evt.from);
134 0 : if (request.status === "waitingForTribute" && request.type in evt.amounts)
135 : {
136 0 : request.wanted -= evt.amounts[request.type];
137 :
138 0 : if (request.wanted <= 0)
139 : {
140 0 : if (this.Config.debug > 1)
141 0 : API3.warn("Player " + uneval(evt.from) + " has sent the required tribute amount");
142 :
143 0 : this.changePlayerDiplomacy(gameState, evt.from, request.requestType);
144 0 : request.status = "accepted";
145 : }
146 0 : else if (evt.amounts[request.type] > 0)
147 : {
148 : // Reset the warning sent to the player that reminds them to speed up the tributes
149 0 : request.warnTime = gameState.ai.elapsedTime + 60;
150 0 : request.sentWarning = false;
151 : }
152 : }
153 : }
154 :
155 0 : if (evt.to !== PlayerID || !gameState.isPlayerAlly(evt.from))
156 0 : continue;
157 0 : let tributes = 0;
158 0 : for (let key in evt.amounts)
159 : {
160 0 : if (key === "food")
161 0 : tributes += evt.amounts[key];
162 : else
163 0 : tributes += 2*evt.amounts[key];
164 : }
165 0 : this.Config.personality.cooperative = Math.min(1, this.Config.personality.cooperative + 0.0001 * tributes);
166 : }
167 :
168 0 : for (let evt of events.Attacked)
169 : {
170 0 : let target = gameState.getEntityById(evt.target);
171 0 : if (!target || !target.position() ||
172 : gameState.ai.HQ.territoryMap.getOwner(target.position()) !== PlayerID ||
173 : !gameState.isPlayerEnemy(target.owner()))
174 0 : continue;
175 0 : let attacker = gameState.getEntityById(evt.attacker);
176 0 : if (!attacker || attacker.owner() === PlayerID || !gameState.isPlayerAlly(attacker.owner()))
177 0 : continue;
178 0 : this.Config.personality.cooperative = Math.min(1, this.Config.personality.cooperative + 0.003);
179 : }
180 :
181 0 : if (events.DiplomacyChanged.length || events.PlayerDefeated.length || events.CeasefireEnded.length)
182 0 : this.lastManStandingCheck(gameState);
183 :
184 0 : for (let evt of events.PlayerDefeated)
185 : {
186 0 : this.receivedDiplomacyRequests.delete(evt.playerId);
187 0 : this.sentDiplomacyRequests.delete(evt.playerId);
188 : }
189 :
190 0 : for (let evt of events.DiplomacyChanged)
191 : {
192 0 : if (evt.otherPlayer !== PlayerID)
193 0 : continue;
194 :
195 0 : if (this.sentDiplomacyRequests.has(evt.player)) // If another player has accepted a diplomacy request we sent
196 : {
197 0 : let sentRequest = this.sentDiplomacyRequests.get(evt.player);
198 0 : if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && sentRequest.requestType === "ally" ||
199 : gameState.sharedScript.playersData[evt.player].isNeutral[PlayerID] && sentRequest.requestType === "neutral")
200 0 : this.changePlayerDiplomacy(gameState, evt.player, sentRequest.requestType);
201 :
202 : // Just remove the request if the other player switched their stance to a different and/or more negative state
203 : // TODO: Keep this send request and take it into account for later diplomacy changes (maybe be less inclined to offer to this player)
204 0 : this.sentDiplomacyRequests.delete(evt.player);
205 0 : continue;
206 : }
207 :
208 0 : let request = this.receivedDiplomacyRequests.get(evt.player);
209 0 : if (request !== undefined &&
210 : (!gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && request.requestType === "ally" ||
211 : gameState.sharedScript.playersData[evt.player].isEnemy[PlayerID] && request.requestType === "neutral"))
212 : {
213 : // a player that had requested to be allies changed their stance with us
214 0 : if (request.status === "accepted")
215 0 : request.status = "allianceBroken";
216 0 : else if (request.status !== "allianceBroken")
217 0 : request.status = "declinedRequest";
218 : }
219 0 : else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerEnemy(evt.player))
220 : {
221 0 : let response = request !== undefined && (request.status === "declinedRequest" || request.status === "allianceBroken") ?
222 : "decline" : "declineSuggestNeutral";
223 0 : PETRA.chatAnswerRequestDiplomacy(gameState, evt.player, "ally", response);
224 : }
225 0 : else if (gameState.sharedScript.playersData[evt.player].isAlly[PlayerID] && gameState.isPlayerNeutral(evt.player))
226 0 : this.handleDiplomacyRequest(gameState, evt.player, "ally");
227 0 : else if (gameState.sharedScript.playersData[evt.player].isNeutral[PlayerID] && gameState.isPlayerEnemy(evt.player))
228 0 : this.handleDiplomacyRequest(gameState, evt.player, "neutral");
229 : }
230 :
231 : // These events will only be sent by other AI players
232 0 : for (let evt of events.DiplomacyRequest)
233 : {
234 0 : if (evt.player !== PlayerID)
235 0 : continue;
236 :
237 0 : this.handleDiplomacyRequest(gameState, evt.source, evt.to);
238 0 : let request = this.receivedDiplomacyRequests.get(evt.source);
239 0 : if (this.Config.debug > 0)
240 0 : API3.warn("Responding to diplomacy request from AI player " + evt.source + " with " + uneval(request));
241 :
242 : // Our diplomacy will have changed already if the response was "accept"
243 0 : if (request.status === "waitingForTribute")
244 : {
245 0 : Engine.PostCommand(PlayerID, {
246 : "type": "tribute-request",
247 : "source": PlayerID,
248 : "player": evt.source,
249 : "resourceWanted": request.wanted,
250 : "resourceType": request.type
251 : });
252 : }
253 : }
254 :
255 : // An AI player we sent a diplomacy request to demanded we send them a tribute
256 0 : for (let evt of events.TributeRequest)
257 : {
258 0 : if (evt.player !== PlayerID)
259 0 : continue;
260 :
261 0 : let availableResources = gameState.ai.queueManager.getAvailableResources(gameState);
262 : // TODO: Save this event and wait until we get more resources if we don't have enough
263 0 : if (evt.resourceWanted < availableResources[evt.resourceType])
264 : {
265 0 : let responseTribute = {};
266 0 : responseTribute[evt.resourceType] = evt.resourceWanted;
267 0 : if (this.Config.debug > 0)
268 0 : API3.warn("Responding to tribute request from AI player " + evt.source + " with " + uneval(responseTribute));
269 0 : Engine.PostCommand(PlayerID, { "type": "tribute", "player": evt.source, "amounts": responseTribute });
270 0 : this.nextTributeUpdate = gameState.ai.elapsedTime + 15;
271 : }
272 : }
273 : };
274 :
275 : /**
276 : * If the "Last Man Standing" option is enabled, check if the only remaining players are allies or neutral.
277 : * If so, turn against the strongest first, but be more likely to first turn against neutral players, if there are any.
278 : */
279 0 : PETRA.DiplomacyManager.prototype.lastManStandingCheck = function(gameState)
280 : {
281 0 : if (gameState.sharedScript.playersData[PlayerID].teamsLocked || gameState.isCeasefireActive() ||
282 : gameState.getAlliedVictory() && gameState.hasAllies())
283 0 : return;
284 :
285 0 : if (gameState.hasEnemies())
286 : {
287 0 : this.waitingToBetray = false;
288 0 : return;
289 : }
290 :
291 0 : if (!gameState.hasAllies() && !gameState.hasNeutrals())
292 0 : return;
293 :
294 : // wait a bit before turning
295 0 : if (!this.waitingToBetray)
296 : {
297 0 : this.betrayLapseTime = gameState.ai.elapsedTime + randFloat(10, 110);
298 0 : this.waitingToBetray = true;
299 0 : return;
300 : }
301 :
302 : // do not turn against a player yet if we are not strong enough
303 0 : if (gameState.getOwnUnits().length < 50)
304 : {
305 0 : this.betrayLapseTime += 60;
306 0 : return;
307 : }
308 :
309 : let playerToTurnAgainst;
310 0 : let turnFactor = 0;
311 0 : let max = 0;
312 :
313 : // count the amount of entities remaining players have
314 0 : for (let i = 1; i < gameState.sharedScript.playersData.length; ++i)
315 : {
316 0 : if (i === PlayerID || gameState.ai.HQ.attackManager.defeated[i])
317 0 : continue;
318 :
319 0 : turnFactor = gameState.getEntities(i).length;
320 :
321 0 : if (gameState.isPlayerNeutral(i)) // be more inclined to turn against neutral players
322 0 : turnFactor += this.betrayWeighting;
323 :
324 0 : if (gameState.getVictoryConditions().has("wonder"))
325 : {
326 0 : let wonder = gameState.getEnemyStructures(i).filter(API3.Filters.byClass("Wonder"))[0];
327 0 : if (wonder)
328 : {
329 0 : let wonderProgess = wonder.foundationProgress();
330 0 : if (wonderProgess === undefined)
331 : {
332 0 : playerToTurnAgainst = i;
333 0 : break;
334 : }
335 0 : turnFactor += wonderProgess * 2.5 + this.betrayWeighting;
336 : }
337 : }
338 :
339 0 : if (gameState.getVictoryConditions().has("capture_the_relic"))
340 : {
341 0 : let relicsCount = gameState.updatingGlobalCollection("allRelics", API3.Filters.byClass("Relic"))
342 0 : .filter(relic => relic.owner() === i).length;
343 0 : turnFactor += relicsCount * this.betrayWeighting;
344 : }
345 :
346 0 : if (turnFactor < max)
347 0 : continue;
348 :
349 0 : max = turnFactor;
350 0 : playerToTurnAgainst = i;
351 : }
352 :
353 0 : if (playerToTurnAgainst)
354 : {
355 0 : this.changePlayerDiplomacy(gameState, playerToTurnAgainst, "enemy");
356 0 : let request = this.receivedDiplomacyRequests.get(playerToTurnAgainst);
357 0 : if (request && request.status !== "allianceBroken")
358 : {
359 0 : if (request.status === "waitingForTribute")
360 0 : PETRA.chatAnswerRequestDiplomacy(gameState, player, request.requestType, "decline");
361 0 : request.status = request.status === "accepted" ? "allianceBroken" : "declinedRequest";
362 : }
363 : // If we had sent this player a diplomacy request, just rescind it
364 0 : this.sentDiplomacyRequests.delete(playerToTurnAgainst);
365 : }
366 0 : this.betrayLapseTime = -1;
367 0 : this.waitingToBetray = false;
368 : };
369 :
370 : /**
371 : * Do not become allies with a player if the game would be over.
372 : * Overall, be reluctant to become allies with any one player, but be more likely to accept neutral requests.
373 : */
374 0 : PETRA.DiplomacyManager.prototype.handleDiplomacyRequest = function(gameState, player, requestType)
375 : {
376 0 : if (gameState.sharedScript.playersData[PlayerID].teamsLocked)
377 0 : return;
378 : let response;
379 : let requiredTribute;
380 0 : let request = this.receivedDiplomacyRequests.get(player);
381 0 : let moreEnemiesThanAllies = gameState.getEnemies().length > gameState.getMutualAllies().length;
382 :
383 : // For any given diplomacy request be likely to permanently decline
384 0 : if (!request && gameState.getPlayerCiv() !== gameState.getPlayerCiv(player) && randBool(0.6) ||
385 : !moreEnemiesThanAllies || gameState.ai.HQ.attackManager.currentEnemyPlayer === player)
386 : {
387 0 : this.receivedDiplomacyRequests.set(player, { "requestType": requestType, "status": "declinedRequest" });
388 0 : response = "decline";
389 : }
390 0 : else if (request && request.status !== "accepted" && request.requestType !== "ally")
391 : {
392 0 : if (request.status === "declinedRequest")
393 0 : response = "decline";
394 0 : else if (request.status === "allianceBroken") // Previous alliance was broken, so decline
395 0 : response = "declineRepeatedOffer";
396 0 : else if (request.status === "waitingForTribute")
397 : {
398 0 : response = "waitingForTribute";
399 0 : requiredTribute = request;
400 : }
401 : }
402 0 : else if (requestType === "ally" && gameState.getEntities(player).length < gameState.getOwnEntities().length && randBool(0.4) ||
403 : requestType === "neutral" && moreEnemiesThanAllies && randBool(0.8))
404 : {
405 0 : response = "accept";
406 0 : this.changePlayerDiplomacy(gameState, player, requestType);
407 0 : this.receivedDiplomacyRequests.set(player, { "requestType": requestType, "status": "accepted" });
408 : }
409 : else
410 : {
411 : // Try to request a tribute.
412 : // If a resource is not tributable, do not request it.
413 : // If no resources are tributable, decline.
414 0 : let resTribCodes = Resources.GetTributableCodes();
415 0 : if (resTribCodes.length)
416 : {
417 0 : requiredTribute = gameState.ai.HQ.pickMostNeededResources(gameState, resTribCodes)[0];
418 0 : response = "acceptWithTribute";
419 0 : requiredTribute.wanted = Math.max(1000, gameState.getOwnUnits().length * (requestType === "ally" ? 10 : 5));
420 0 : this.receivedDiplomacyRequests.set(player, {
421 : "status": "waitingForTribute",
422 : "wanted": requiredTribute.wanted,
423 : "type": requiredTribute.type,
424 : "warnTime": gameState.ai.elapsedTime + 60,
425 : "sentWarning": false,
426 : "requestType": requestType
427 : });
428 : }
429 : else
430 : {
431 0 : this.receivedDiplomacyRequests.set(player, { "requestType": requestType, "status": "declinedRequest" });
432 0 : response = "decline";
433 : }
434 : }
435 0 : PETRA.chatAnswerRequestDiplomacy(gameState, player, requestType, response, requiredTribute);
436 : };
437 :
438 0 : PETRA.DiplomacyManager.prototype.changePlayerDiplomacy = function(gameState, player, newDiplomaticStance)
439 : {
440 0 : if (gameState.isPlayerEnemy(player) && (newDiplomaticStance === "ally" || newDiplomaticStance === "neutral"))
441 0 : gameState.ai.HQ.attackManager.cancelAttacksAgainstPlayer(gameState, player);
442 0 : Engine.PostCommand(PlayerID, { "type": "diplomacy", "player": player, "to": newDiplomaticStance });
443 0 : if (this.Config.debug > 1)
444 0 : API3.warn("diplomacy stance with player " + player + " is now " + newDiplomaticStance);
445 0 : if (this.Config.chat)
446 0 : PETRA.chatNewDiplomacy(gameState, player, newDiplomaticStance);
447 : };
448 :
449 0 : PETRA.DiplomacyManager.prototype.checkRequestedTributes = function(gameState)
450 : {
451 0 : for (let [player, data] of this.receivedDiplomacyRequests)
452 0 : if (data.status === "waitingForTribute" && gameState.ai.elapsedTime > data.warnTime)
453 : {
454 0 : if (data.sentWarning)
455 : {
456 0 : this.receivedDiplomacyRequests.delete(player);
457 0 : PETRA.chatAnswerRequestDiplomacy(gameState, player, data.requestType, "decline");
458 : }
459 : else
460 : {
461 0 : data.sentWarning = true;
462 0 : data.warnTime = gameState.ai.elapsedTime + 60;
463 0 : PETRA.chatAnswerRequestDiplomacy(gameState, player, data.requestType, "waitingForTribute", {
464 : "wanted": data.wanted,
465 : "type": data.type
466 : });
467 : }
468 : }
469 : };
470 :
471 : /**
472 : * Try to become allies with a player who has a lot of mutual enemies in common with us.
473 : * TODO: Possibly let human players demand tributes from AIs who send diplomacy requests.
474 : */
475 0 : PETRA.DiplomacyManager.prototype.sendDiplomacyRequest = function(gameState)
476 : {
477 : let player;
478 0 : let max = 0;
479 0 : for (let i = 1; i < gameState.sharedScript.playersData.length; ++i)
480 : {
481 0 : let mutualEnemies = 0;
482 0 : let request = this.receivedDiplomacyRequests.get(i); // Do not send to players we have already rejected before
483 0 : if (i === PlayerID || gameState.isPlayerMutualAlly(i) || gameState.ai.HQ.attackManager.defeated[i] ||
484 : gameState.ai.HQ.attackManager.currentEnemyPlayer === i ||
485 : this.sentDiplomacyRequests.get(i) !== undefined || request && request.status === "declinedRequest")
486 0 : continue;
487 :
488 0 : for (let j = 1; j < gameState.sharedScript.playersData.length; ++j)
489 : {
490 0 : if (gameState.sharedScript.playersData[i].isEnemy[j] && gameState.isPlayerEnemy(j) &&
491 : !gameState.ai.HQ.attackManager.defeated[j])
492 0 : ++mutualEnemies;
493 :
494 0 : if (mutualEnemies < max)
495 0 : continue;
496 :
497 0 : max = mutualEnemies;
498 0 : player = i;
499 : }
500 : }
501 0 : if (!player)
502 0 : return;
503 :
504 0 : let requestType = gameState.isPlayerNeutral(player) ? "ally" : "neutral";
505 :
506 0 : this.sentDiplomacyRequests.set(player, {
507 : "requestType": requestType,
508 : "timeSent": gameState.ai.elapsedTime
509 : });
510 :
511 0 : if (this.Config.debug > 0)
512 0 : API3.warn("Sending diplomacy request to player " + player + " with " + requestType);
513 0 : Engine.PostCommand(PlayerID, { "type": "diplomacy-request", "source": PlayerID, "player": player, "to": requestType });
514 0 : PETRA.chatNewRequestDiplomacy(gameState, player, requestType, "sendRequest");
515 : };
516 :
517 0 : PETRA.DiplomacyManager.prototype.checkSentDiplomacyRequests = function(gameState)
518 : {
519 0 : for (let [player, data] of this.sentDiplomacyRequests)
520 0 : if (gameState.ai.elapsedTime > data.timeSent + 60 && !gameState.ai.HQ.saveResources &&
521 : gameState.getPopulation() > 70)
522 : {
523 0 : PETRA.chatNewRequestDiplomacy(gameState, player, data.requestType, "requestExpired");
524 0 : this.sentDiplomacyRequests.delete(player);
525 : }
526 : };
527 :
528 0 : PETRA.DiplomacyManager.prototype.update = function(gameState, events)
529 : {
530 0 : this.checkEvents(gameState, events);
531 :
532 0 : if (Resources.GetTributableCodes().length && !gameState.ai.HQ.saveResources && gameState.ai.elapsedTime > this.nextTributeUpdate)
533 0 : this.tributes(gameState);
534 :
535 0 : if (this.waitingToBetray && gameState.ai.elapsedTime > this.betrayLapseTime)
536 0 : this.lastManStandingCheck(gameState);
537 :
538 0 : this.checkRequestedTributes(gameState);
539 :
540 0 : if (gameState.sharedScript.playersData[PlayerID].teamsLocked || gameState.isCeasefireActive())
541 0 : return;
542 :
543 : // Be unlikely to send diplomacy requests to other players
544 0 : if (gameState.ai.elapsedTime > this.sentDiplomacyRequestLapseTime)
545 : {
546 0 : this.sentDiplomacyRequestLapseTime = gameState.ai.elapsedTime + 300 + randFloat(10, 100);
547 0 : let numEnemies = gameState.getEnemies().length;
548 : // Don't consider gaia
549 0 : if (numEnemies > 2 && gameState.getMutualAllies().length < numEnemies - 1 && randBool(0.1))
550 0 : this.sendDiplomacyRequest(gameState);
551 : }
552 :
553 0 : this.checkSentDiplomacyRequests(gameState);
554 : };
555 :
556 0 : PETRA.DiplomacyManager.prototype.Serialize = function()
557 : {
558 0 : return {
559 : "nextTributeUpdate": this.nextTributeUpdate,
560 : "nextTributeRequest": this.nextTributeRequest,
561 : "betrayLapseTime": this.betrayLapseTime,
562 : "waitingToBetray": this.waitingToBetray,
563 : "betrayWeighting": this.betrayWeighting,
564 : "receivedDiplomacyRequests": this.receivedDiplomacyRequests,
565 : "sentDiplomacyRequests": this.sentDiplomacyRequests,
566 : "sentDiplomacyRequestLapseTime": this.sentDiplomacyRequestLapseTime
567 : };
568 : };
569 :
570 0 : PETRA.DiplomacyManager.prototype.Deserialize = function(data)
571 : {
572 0 : for (let key in data)
573 0 : this[key] = data[key];
574 : };
|