From 1b286f5518b9f698be3ef41a0e4c0f66e0c81c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Kosman?= Date: Thu, 15 May 2025 19:09:27 +0200 Subject: [PATCH] Feat : Auto remove embargoes that were automatically created (#707) ## Description: Adds two fields to an Embargo : - `createdAt` : the tick at which it was created - `willExpire` : whether the embargo will expire on its own An embargo will remove itself only if the player didn't intentionally set it. It expires either when an alliance is made, or if enough time has passed (according to the `embargoDuration` config entry). I put 5 minutes for `embargoDuration` by default, which seems reasonable to me. closes #702 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: leo21_ --- src/core/configuration/Config.ts | 1 + src/core/configuration/DefaultConfig.ts | 4 ++++ src/core/execution/AttackExecution.ts | 4 ++-- src/core/execution/EmbargoExecution.ts | 2 +- src/core/execution/FakeHumanExecution.ts | 2 +- src/core/execution/PlayerExecution.ts | 11 ++++++++++ src/core/game/Game.ts | 10 ++++++++- src/core/game/GameImpl.ts | 15 +++++++++++-- src/core/game/PlayerImpl.ts | 27 ++++++++++++++++++++---- 9 files changed, 65 insertions(+), 11 deletions(-) diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index d789d52b2..cc8627bb2 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -111,6 +111,7 @@ export interface Config { boatMaxNumber(): number; allianceDuration(): Tick; allianceRequestCooldown(): Tick; + temporaryEmbargoDuration(): Tick; targetDuration(): Tick; targetCooldown(): Tick; emojiMessageCooldown(): Tick; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 841976d0c..538a36156 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -448,6 +448,10 @@ export class DefaultConfig implements Config { allianceDuration(): Tick { return 600 * 10; // 10 minutes. } + temporaryEmbargoDuration(): Tick { + return 300 * 10; // 5 minutes. + } + percentageTilesOwnedToWin(): number { if (this._gameConfig.gameMode == GameMode.Team) { return 95; diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 8d62debb5..f514cd02b 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -79,8 +79,8 @@ export class AttackExecution implements Execution { targetPlayer.type() != PlayerType.Bot && this._owner.type() != PlayerType.Bot ) { - // Don't let bots embargo since they can't trade anyways. - targetPlayer.addEmbargo(this._owner.id()); + // Don't let bots embargo since they can't trade anyway. + targetPlayer.addEmbargo(this._owner.id(), true); } } diff --git a/src/core/execution/EmbargoExecution.ts b/src/core/execution/EmbargoExecution.ts index 7e159a4ea..a3dfaf7f5 100644 --- a/src/core/execution/EmbargoExecution.ts +++ b/src/core/execution/EmbargoExecution.ts @@ -23,7 +23,7 @@ export class EmbargoExecution implements Execution { } tick(_: number): void { - if (this.action == "start") this.player.addEmbargo(this.targetID); + if (this.action == "start") this.player.addEmbargo(this.targetID, false); else this.player.stopEmbargo(this.targetID); this.active = false; diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index 18b84bc3f..a64ba258a 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -96,7 +96,7 @@ export class FakeHumanExecution implements Execution { this.player.relation(other) <= Relation.Hostile && !this.player.hasEmbargoAgainst(other) ) { - this.player.addEmbargo(other.id()); + this.player.addEmbargo(other.id(), false); } else if ( this.player.relation(other) >= Relation.Neutral && this.player.hasEmbargoAgainst(other) diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index df86b96e6..b93b96223 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -97,6 +97,17 @@ export class PlayerExecution implements Execution { } } + const embargoes = this.player.getEmbargoes(); + for (const embargo of embargoes) { + if ( + embargo.isTemporary && + this.mg.ticks() - embargo.createdAt > + this.mg.config().temporaryEmbargoDuration() + ) { + this.player.stopEmbargo(embargo.target); + } + } + if (ticks - this.lastCalc > this.ticksPerClusterCalc) { if (this.player.lastTileChange() > this.lastCalc) { this.lastCalc = ticks; diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index bb57ca831..b4ccf7ba7 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -378,6 +378,12 @@ export interface TerraNullius { smallID(): number; } +export interface Embargo { + createdAt: Tick; + isTemporary: boolean; + target: PlayerID; +} + export interface Player { // Basic Info smallID(): number; @@ -476,8 +482,10 @@ export interface Player { // Embargo hasEmbargoAgainst(other: Player): boolean; tradingPartners(): Player[]; - addEmbargo(other: PlayerID): void; + addEmbargo(other: PlayerID, isTemporary: boolean): void; + getEmbargoes(): Embargo[]; stopEmbargo(other: PlayerID): void; + endTemporaryEmbargo(other: PlayerID): void; canTrade(other: Player): boolean; // Attacking. diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 1b88abad0..797a3396f 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -221,16 +221,27 @@ export class GameImpl implements Game { acceptAllianceRequest(request: AllianceRequestImpl) { this.allianceRequests = this.allianceRequests.filter((ar) => ar != request); + + const requestor = request.requestor(); + const recipient = request.recipient(); + const alliance = new AllianceImpl( this, - request.requestor() as PlayerImpl, - request.recipient() as PlayerImpl, + requestor as PlayerImpl, + recipient as PlayerImpl, this._ticks, ); this.alliances_.push(alliance); (request.requestor() as PlayerImpl).pastOutgoingAllianceRequests.push( request, ); + + // Automatically remove embargoes only if they were automatically created + if (requestor.hasEmbargoAgainst(recipient)) + requestor.endTemporaryEmbargo(recipient.id()); + if (recipient.hasEmbargoAgainst(requestor)) + recipient.endTemporaryEmbargo(requestor.id()); + this.addUpdate({ type: GameUpdateType.AllianceRequestReply, request: request.toUpdate(), diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 0a98ff41a..ed202b9cf 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -21,6 +21,7 @@ import { BuildableUnit, Cell, ColoredTeams, + Embargo, EmojiMessage, Gold, MessageType, @@ -73,7 +74,7 @@ export class PlayerImpl implements Player { markedTraitorTick = -1; - private embargoes: Set = new Set(); + private embargoes = new Map(); public _borderTiles: Set = new Set(); @@ -142,7 +143,7 @@ export class PlayerImpl implements Player { troops: this.troops(), targetTroopRatio: this.targetTroopRatio(), allies: this.alliances().map((a) => a.other(this).smallID()), - embargoes: this.embargoes, + embargoes: new Set([...this.embargoes.keys()].map((p) => p.toString())), isTraitor: this.isTraitor(), targets: this.targets().map((p) => p.smallID()), outgoingEmojis: this.outgoingEmojis(), @@ -582,14 +583,32 @@ export class PlayerImpl implements Player { return !embargo && other.id() != this.id(); } - addEmbargo(other: PlayerID): void { - this.embargoes.add(other); + addEmbargo(other: PlayerID, isTemporary: boolean): void { + if (this.embargoes.has(other) && !this.embargoes.get(other).isTemporary) + return; + + this.embargoes.set(other, { + createdAt: this.mg.ticks(), + isTemporary: isTemporary, + target: other, + }); + } + + getEmbargoes(): Embargo[] { + return [...this.embargoes.values()]; } stopEmbargo(other: PlayerID): void { this.embargoes.delete(other); } + endTemporaryEmbargo(other: PlayerID): void { + if (this.embargoes.has(other) && !this.embargoes.get(other).isTemporary) + return; + + this.stopEmbargo(other); + } + tradingPartners(): Player[] { return this.mg .players()