Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions src/client/Transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,8 @@ export class SendUpgradeStructureIntentEvent implements GameEvent {
) {}
}

export class SendAllianceReplyIntentEvent implements GameEvent {
constructor(
// The original alliance requestor
public readonly requestor: PlayerView,
public readonly recipient: PlayerView,
public readonly accepted: boolean,
) {}
export class SendAllianceRejectIntentEvent implements GameEvent {
constructor(public readonly requestor: PlayerView) {}
}

export class SendAllianceExtensionIntentEvent implements GameEvent {
Expand Down Expand Up @@ -204,8 +199,8 @@ export class Transport {
this.eventBus.on(SendAllianceRequestIntentEvent, (e) =>
this.onSendAllianceRequest(e),
);
this.eventBus.on(SendAllianceReplyIntentEvent, (e) =>
this.onAllianceRequestReplyUIEvent(e),
this.eventBus.on(SendAllianceRejectIntentEvent, (e) =>
this.onAllianceRejectUIEvent(e),
);
this.eventBus.on(SendAllianceExtensionIntentEvent, (e) =>
this.onSendAllianceExtensionIntent(e),
Expand Down Expand Up @@ -447,11 +442,10 @@ export class Transport {
});
}

private onAllianceRequestReplyUIEvent(event: SendAllianceReplyIntentEvent) {
private onAllianceRejectUIEvent(event: SendAllianceRejectIntentEvent) {
this.sendIntent({
type: "allianceRequestReply",
type: "allianceReject",
requestor: event.requestor.id(),
accept: event.accepted,
});
}

Expand Down
9 changes: 4 additions & 5 deletions src/client/graphics/layers/EventsDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import {
} from "../../../core/game/GameUpdates";
import {
SendAllianceExtensionIntentEvent,
SendAllianceReplyIntentEvent,
SendAllianceRejectIntentEvent,
SendAllianceRequestIntentEvent,
} from "../../Transport";
import { Layer } from "./Layer";

Expand Down Expand Up @@ -458,16 +459,14 @@ export class EventsDisplay extends LitElement implements Layer {
className: "btn",
action: () =>
this.eventBus.emit(
new SendAllianceReplyIntentEvent(requestor, recipient, true),
new SendAllianceRequestIntentEvent(recipient, requestor),
),
},
{
text: translateText("events_display.reject_alliance"),
className: "btn-info",
action: () =>
this.eventBus.emit(
new SendAllianceReplyIntentEvent(requestor, recipient, false),
),
this.eventBus.emit(new SendAllianceRejectIntentEvent(requestor)),
},
],
highlight: true,
Expand Down
15 changes: 6 additions & 9 deletions src/core/Schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export type Intent =
| BoatAttackIntent
| CancelBoatIntent
| AllianceRequestIntent
| AllianceRequestReplyIntent
| AllianceRejectIntent
| AllianceExtensionIntent
| BreakAllianceIntent
| TargetPlayerIntent
Expand All @@ -60,9 +60,7 @@ export type BoatAttackIntent = z.infer<typeof BoatAttackIntentSchema>;
export type EmbargoAllIntent = z.infer<typeof EmbargoAllIntentSchema>;
export type CancelBoatIntent = z.infer<typeof CancelBoatIntentSchema>;
export type AllianceRequestIntent = z.infer<typeof AllianceRequestIntentSchema>;
export type AllianceRequestReplyIntent = z.infer<
typeof AllianceRequestReplyIntentSchema
>;
export type AllianceRejectIntent = z.infer<typeof AllianceRejectIntentSchema>;
export type BreakAllianceIntent = z.infer<typeof BreakAllianceIntentSchema>;
export type TargetPlayerIntent = z.infer<typeof TargetPlayerIntentSchema>;
export type EmojiIntent = z.infer<typeof EmojiIntentSchema>;
Expand Down Expand Up @@ -311,10 +309,9 @@ export const AllianceRequestIntentSchema = z.object({
recipient: ID,
});

export const AllianceRequestReplyIntentSchema = z.object({
type: z.literal("allianceRequestReply"),
requestor: ID, // The one who made the original alliance request
accept: z.boolean(),
export const AllianceRejectIntentSchema = z.object({
type: z.literal("allianceReject"),
requestor: ID,
});

export const BreakAllianceIntentSchema = z.object({
Expand Down Expand Up @@ -426,7 +423,7 @@ const IntentSchema = z.discriminatedUnion("type", [
BoatAttackIntentSchema,
CancelBoatIntentSchema,
AllianceRequestIntentSchema,
AllianceRequestReplyIntentSchema,
AllianceRejectIntentSchema,
BreakAllianceIntentSchema,
TargetPlayerIntentSchema,
EmojiIntentSchema,
Expand Down
10 changes: 3 additions & 7 deletions src/core/execution/ExecutionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { PseudoRandom } from "../PseudoRandom";
import { ClientID, GameID, StampedIntent, Turn } from "../Schemas";
import { simpleHash } from "../Util";
import { AllianceExtensionExecution } from "./alliance/AllianceExtensionExecution";
import { AllianceRejectExecution } from "./alliance/AllianceRejectExecution";
import { AllianceRequestExecution } from "./alliance/AllianceRequestExecution";
import { AllianceRequestReplyExecution } from "./alliance/AllianceRequestReplyExecution";
import { BreakAllianceExecution } from "./alliance/BreakAllianceExecution";
import { AttackExecution } from "./AttackExecution";
import { BoatRetreatExecution } from "./BoatRetreatExecution";
Expand Down Expand Up @@ -75,12 +75,8 @@ export class Executor {
return new TransportShipExecution(player, intent.dst, intent.troops);
case "allianceRequest":
return new AllianceRequestExecution(player, intent.recipient);
case "allianceRequestReply":
return new AllianceRequestReplyExecution(
intent.requestor,
player,
intent.accept,
);
case "allianceReject":
return new AllianceRejectExecution(intent.requestor, player);
case "breakAlliance":
return new BreakAllianceExecution(player, intent.recipient);
case "targetPlayer":
Expand Down
45 changes: 45 additions & 0 deletions src/core/execution/alliance/AllianceRejectExecution.ts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to just remove this execution? And then events display and radial menu can just send AllianceRequest? Then all alliance logic would be in a single file, and we can move cancelNukesBetweenAlliedPlayers there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I centralised acceptance logic in AllianceRequestExecution and created a new AllianceRejectExecution to handle rejections, because:

  • semantics: you'll never look for the rejection path inside Alliance Request Execution
  • SRP: adding a new parameter to AllianceRequestExecution to branch out its logic and handle rejections felt really wrong to me

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Execution, Game, Player, PlayerID } from "../../game/Game";

export class AllianceRejectExecution implements Execution {
private active = true;

constructor(
private requestorID: PlayerID,
private recipient: Player,
) {}

init(mg: Game, ticks: number): void {
if (!mg.hasPlayer(this.requestorID)) {
console.warn(
`AllianceRejectExecution requester ${this.requestorID} not found`,
);
this.active = false;
return;
}
const requestor = mg.player(this.requestorID);

if (requestor.isFriendly(this.recipient)) {
console.warn("already allied");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here and on line 28 can you log something like:

console.warn("[AllianceRejectExecution] Player ${this.requestorID} cannot reject alliance with ${this.recipient.id}, already allied"

} else {
const request = requestor
.outgoingAllianceRequests()
.find((ar) => ar.recipient() === this.recipient);
if (request === undefined) {
console.warn("no alliance request found");
} else {
request.reject();
}
}
this.active = false;
}

tick(ticks: number): void {}

isActive(): boolean {
return this.active;
}

activeDuringSpawnPhase(): boolean {
return false;
}
}
53 changes: 53 additions & 0 deletions src/core/execution/alliance/AllianceRequestExecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,63 @@ import {
AllianceRequest,
Execution,
Game,
MessageType,
Player,
PlayerID,
UnitType,
} from "../../game/Game";

export function cancelNukesBetweenAlliedPlayers(
game: Game,
p1: Player,
p2: Player,
): void {
const neutralized = new Map<Player, number>();

const players = [p1, p2];

for (const launcher of players) {
for (const unit of launcher.units(
UnitType.AtomBomb,
UnitType.HydrogenBomb,
)) {
if (!unit.isActive() || unit.reachedTarget()) continue;

const targetTile = unit.targetTile();
if (!targetTile) continue;

const targetOwner = game.owner(targetTile);
if (!targetOwner.isPlayer()) continue;

const other = launcher === p1 ? p2 : p1;
if (targetOwner !== other) continue;

unit.delete(false);
neutralized.set(launcher, (neutralized.get(launcher) ?? 0) + 1);
}
}

for (const [launcher, count] of neutralized) {
const other = launcher === p1 ? p2 : p1;

game.displayMessage(
"events_display.alliance_nukes_destroyed_outgoing",
MessageType.ALLIANCE_ACCEPTED,
launcher.id(),
undefined,
{ name: other.displayName(), count },
);

game.displayMessage(
"events_display.alliance_nukes_destroyed_incoming",
MessageType.ALLIANCE_ACCEPTED,
other.id(),
undefined,
{ name: launcher.displayName(), count },
);
}
}

export class AllianceRequestExecution implements Execution {
private req: AllianceRequest | null = null;
private active = true;
Expand Down
117 changes: 0 additions & 117 deletions src/core/execution/alliance/AllianceRequestReplyExecution.ts

This file was deleted.

8 changes: 8 additions & 0 deletions src/core/game/GameImpl.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { renderNumber } from "../../client/Utils";
import { Config } from "../configuration/Config";
import { cancelNukesBetweenAlliedPlayers } from "../execution/alliance/AllianceRequestExecution";
import {
AbstractGraph,
AbstractGraphBuilder,
Expand Down Expand Up @@ -332,12 +333,19 @@ export class GameImpl implements Game {
request,
);

// Update relations
requestor.updateRelation(recipient, 100);
recipient.updateRelation(requestor, 100);

// Automatically remove embargoes only if they were automatically created
if (requestor.hasEmbargoAgainst(recipient))
requestor.endTemporaryEmbargo(recipient);
if (recipient.hasEmbargoAgainst(requestor))
recipient.endTemporaryEmbargo(requestor);

// Cancel incoming nukes between players
cancelNukesBetweenAlliedPlayers(this, requestor, recipient);

Comment on lines +346 to +348
Copy link
Collaborator

@evanpelle evanpelle Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also adds a lot of logic into GameImpl, gameimpl should ideally just store & update state. Can this be removed?

Same with line 337 & 338

this.addUpdate({
type: GameUpdateType.AllianceRequestReply,
request: request.toUpdate(),
Expand Down
Loading
Loading