This commit is contained in:
Evan
2025-01-08 20:58:08 -08:00
parent 162b6dc349
commit 459fc50dae
12 changed files with 176 additions and 168 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ import { and, bfs, dist, generateID, manhattanDist } from "../core/Util";
import { SendAttackIntentEvent, SendSpawnIntentEvent, Transport } from "./Transport";
import { createCanvas } from "./Utils";
import { MessageType } from '../core/game/Game';
import { DisplayMessageEvent } from '../core/game/Game';
import { DisplayMessageUpdate } from '../core/game/Game';
import { WorkerClient } from "../core/worker/WorkerClient";
import { consolex, initRemoteSender } from "../core/Consolex";
import { getConfig, getServerConfig } from "../core/configuration/Config";
+5 -3
View File
@@ -30,7 +30,9 @@ export class SendBreakAllianceIntentEvent implements GameEvent {
export class SendAllianceReplyIntentEvent implements GameEvent {
constructor(
public readonly allianceRequest: AllianceRequest,
// The original alliance requestor
public readonly requestor: Player,
public readonly recipient: Player,
public readonly accepted: boolean
) { }
}
@@ -262,8 +264,8 @@ export class Transport {
this.sendIntent({
type: "allianceRequestReply",
clientID: this.lobbyConfig.clientID,
requestor: event.allianceRequest.requestor().id(),
recipient: event.allianceRequest.recipient().id(),
requestor: event.requestor.id(),
recipient: event.recipient.id(),
accept: event.accepted,
})
}
+103 -90
View File
@@ -2,8 +2,13 @@ import { LitElement, html, css } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { EventBus } from "../../../core/EventBus";
import {
AllianceRequestReplyUpdate,
AllianceRequestUpdate,
AllPlayers,
DisplayMessageUpdate,
EmojiUpdate,
MessageType,
TargetPlayerUpdate,
} from "../../../core/game/Game";
import { ClientID } from "../../../core/Schemas";
import { Layer } from "./Layer";
@@ -140,13 +145,6 @@ export class EventsDisplay extends LitElement implements Layer {
}
init() {
this.eventBus.on(AllianceRequestEvent, a => this.onAllianceRequestEvent(a));
this.eventBus.on(AllianceRequestReplyEvent, a => this.onAllianceRequestReplyEvent(a));
this.eventBus.on(DisplayMessageEvent, e => this.onDisplayMessageEvent(e));
this.eventBus.on(BrokeAllianceEvent, e => this.onBrokeAllianceEvent(e));
this.eventBus.on(AllianceExpiredEvent, e => this.onAllianceExpiredEvent(e));
this.eventBus.on(TargetPlayerEvent, e => this.onTargetPlayerEvent(e));
this.eventBus.on(EmojiMessageEvent, e => this.onEmojiMessageEvent(e));
}
tick() {
@@ -186,9 +184,9 @@ export class EventsDisplay extends LitElement implements Layer {
renderLayer(): void { }
onDisplayMessageEvent(event: DisplayMessageEvent) {
onDisplayMessageEvent(event: DisplayMessageUpdate) {
const myPlayer = this.game.playerByClientID(this.clientID);
if (event.playerID != null && (!myPlayer || myPlayer.id() !== event.playerID)) {
if (event.playerID != null && (!myPlayer || myPlayer.smallID() !== event.playerID)) {
return;
}
@@ -196,122 +194,137 @@ export class EventsDisplay extends LitElement implements Layer {
description: event.message,
createdAt: this.game.ticks(),
highlight: true,
type: event.type,
type: event.messageType,
unsafeDescription: true,
});
}
onAllianceRequestEvent(event: AllianceRequestEvent) {
onAllianceRequestEvent(update: AllianceRequestUpdate) {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer || event.allianceRequest.recipient() !== myPlayer) {
if (!myPlayer || update.recipientID !== myPlayer.smallID()) {
return;
}
const requestor = this.game.playerBySmallID(update.requestorID)
const recipient = this.game.playerBySmallID(update.recipientID)
this.addEvent({
description: `${event.allianceRequest.requestor().name()} requests an alliance!`,
description: `${requestor.name()} requests an alliance!`,
buttons: [
{
text: "Accept",
className: "btn",
action: () => this.eventBus.emit(new SendAllianceReplyIntentEvent(event.allianceRequest, true)),
action: () => this.eventBus.emit(
new SendAllianceReplyIntentEvent(requestor, recipient, true)
),
},
{
text: "Reject",
className: "btn btn-info",
action: () => this.eventBus.emit(new SendAllianceReplyIntentEvent(event.allianceRequest, false)),
action: () => this.eventBus.emit(
new SendAllianceReplyIntentEvent(requestor, recipient, false)
),
}
],
highlight: true,
type: MessageType.INFO,
createdAt: this.game.ticks(),
onDelete: () => this.eventBus.emit(new SendAllianceReplyIntentEvent(event.allianceRequest, false))
onDelete: () => this.eventBus.emit(
new SendAllianceReplyIntentEvent(requestor, recipient, false)
)
});
}
onAllianceRequestReplyEvent(event: AllianceRequestReplyEvent) {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer || event.allianceRequest.requestor() !== myPlayer) {
return;
}
// onAllianceRequestReplyEvent(event: AllianceRequestReplyUpdate) {
// const myPlayer = this.game.playerByClientID(this.clientID);
// if (!myPlayer || event.allianceRequest.requestor() !== myPlayer) {
// return;
// }
this.addEvent({
description: `${event.allianceRequest.recipient().name()} ${event.accepted ? "accepted" : "rejected"} your alliance request`,
type: event.accepted ? MessageType.SUCCESS : MessageType.ERROR,
highlight: true,
createdAt: this.game.ticks(),
});
}
// this.addEvent({
// description: `${event.allianceRequest.recipient().name()} ${event.accepted ? "accepted" : "rejected"} your alliance request`,
// type: event.accepted ? MessageType.SUCCESS : MessageType.ERROR,
// highlight: true,
// createdAt: this.game.ticks(),
// });
// }
onBrokeAllianceEvent(event: BrokeAllianceEvent) {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer) return;
// onBrokeAllianceEvent(event: BrokeAllianceEvent) {
// const myPlayer = this.game.playerByClientID(this.clientID);
// if (!myPlayer) return;
if (!event.betrayed.isTraitor() && event.traitor === myPlayer) {
this.addEvent({
description: `You broke your alliance with ${event.betrayed.name()}, making you a TRAITOR`,
type: MessageType.ERROR,
highlight: true,
createdAt: this.game.ticks(),
});
} else if (event.betrayed === myPlayer) {
this.addEvent({
description: `${event.traitor.name()}, broke their alliance with you`,
type: MessageType.ERROR,
highlight: true,
createdAt: this.game.ticks(),
});
}
}
// if (!event.betrayed.isTraitor() && event.traitor === myPlayer) {
// this.addEvent({
// description: `You broke your alliance with ${event.betrayed.name()}, making you a TRAITOR`,
// type: MessageType.ERROR,
// highlight: true,
// createdAt: this.game.ticks(),
// });
// } else if (event.betrayed === myPlayer) {
// this.addEvent({
// description: `${event.traitor.name()}, broke their alliance with you`,
// type: MessageType.ERROR,
// highlight: true,
// createdAt: this.game.ticks(),
// });
// }
// }
onAllianceExpiredEvent(event: AllianceExpiredEvent) {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer) return;
// onAllianceExpiredEvent(event: AllianceExpiredEvent) {
// const myPlayer = this.game.playerByClientID(this.clientID);
// if (!myPlayer) return;
const other = event.player1 === myPlayer ? event.player2 : event.player2 === myPlayer ? event.player1 : null;
if (!other || !myPlayer.isAlive() || !other.isAlive()) return;
// const other = event.player1 === myPlayer ? event.player2 : event.player2 === myPlayer ? event.player1 : null;
// if (!other || !myPlayer.isAlive() || !other.isAlive()) return;
this.addEvent({
description: `Your alliance with ${other.name()} expired`,
type: MessageType.WARN,
highlight: true,
createdAt: this.game.ticks(),
});
}
// this.addEvent({
// description: `Your alliance with ${other.name()} expired`,
// type: MessageType.WARN,
// highlight: true,
// createdAt: this.game.ticks(),
// });
// }
onTargetPlayerEvent(event: TargetPlayerEvent) {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer || !myPlayer.isAlliedWith(event.player)) return;
// onTargetPlayerEvent(event: TargetPlayerUpdate) {
// const other = this.game.playerBySmallID(event.playerID)
// const myPlayer = this.game.playerByClientID(this.clientID);
// if (!myPlayer || !myPlayer.isAlliedWith(other)) return;
this.addEvent({
description: `${event.player.name()} requests you attack ${event.target.name()}`,
type: MessageType.INFO,
highlight: true,
createdAt: this.game.ticks(),
});
}
// const target = this.game.playerBySmallID(event.targetID)
onEmojiMessageEvent(event: EmojiMessageEvent) {
const myPlayer = this.game.playerByClientID(this.clientID);
if (!myPlayer) return;
// this.addEvent({
// description: `${other.name()} requests you attack ${target.name()}`,
// type: MessageType.INFO,
// highlight: true,
// createdAt: this.game.ticks(),
// });
// }
if (event.message.recipient === myPlayer) {
this.addEvent({
description: `${event.message.sender.displayName()}:${event.message.emoji}`,
unsafeDescription: true,
type: MessageType.INFO,
highlight: true,
createdAt: this.game.ticks(),
});
} else if (event.message.sender === myPlayer && event.message.recipient !== AllPlayers) {
this.addEvent({
description: `Sent ${event.message.recipient.displayName()}: ${event.message.emoji}`,
unsafeDescription: true,
type: MessageType.INFO,
highlight: true,
createdAt: this.game.ticks(),
});
}
}
// onEmojiMessageEvent(update: EmojiUpdate) {
// const myPlayer = this.game.playerByClientID(this.clientID);
// if (!myPlayer) return;
// const recipient = update.recipientID == AllPlayers ? AllPlayers : this.game.playerBySmallID(update.recipientID)
// const sender = this.game.playerBySmallID(update.senderID)
// if (recipient == myPlayer) {
// this.addEvent({
// description: `${sender.displayName()}:${update.message}`,
// unsafeDescription: true,
// type: MessageType.INFO,
// highlight: true,
// createdAt: this.game.ticks(),
// });
// } else if (sender === myPlayer && recipient !== AllPlayers) {
// this.addEvent({
// description: `Sent ${recipient.displayName()}: ${update.message}`,
// unsafeDescription: true,
// type: MessageType.INFO,
// highlight: true,
// createdAt: this.game.ticks(),
// });
// }
// }
render() {
if (this.events.length === 0) {
+17 -17
View File
@@ -1,6 +1,6 @@
import { colord, Colord } from "colord";
import { Theme } from "../../../core/configuration/Config";
import { Unit, UnitEvent, Cell, Game, Tile, UnitType } from "../../../core/game/Game";
import { Unit, Cell, Game, Tile, UnitType } from "../../../core/game/Game";
import { bfs, dist, euclDist } from "../../../core/Util";
import { Layer } from "./Layer";
import { EventBus } from "../../../core/EventBus";
@@ -70,7 +70,7 @@ export class StructureLayer implements Layer {
}
tick() {
this.game.units().forEach(u => this.handleUnitRendering(new UnitEvent(u, u.tile())))
this.game.units().forEach(u => this.handleUnitRendering(u))
}
init() {
@@ -83,7 +83,7 @@ export class StructureLayer implements Layer {
this.context = this.canvas.getContext("2d", { alpha: true });
this.canvas.width = this.game.width();
this.canvas.height = this.game.height();
this.game.units().forEach(u => this.handleUnitRendering(new UnitEvent(u, u.tile())))
this.game.units().forEach(u => this.handleUnitRendering(u))
}
renderLayer(context: CanvasRenderingContext2D) {
@@ -100,15 +100,15 @@ export class StructureLayer implements Layer {
return unitType in this.unitConfigs;
}
private handleUnitRendering(event: UnitEvent) {
const unitType = event.unit.type();
private handleUnitRendering(unit: Unit) {
const unitType = unit.type();
if (!this.isUnitTypeSupported(unitType)) return;
if (event.unit.isActive() && this.seenUnits.has(event.unit)) {
if (unit.isActive() && this.seenUnits.has(unit)) {
// Already rendered, so don't do anything.
return
}
if (!event.unit.isActive() && !this.seenUnits.has(event.unit)) {
if (!unit.isActive() && !this.seenUnits.has(unit)) {
// Has been deleted and render is cleared so don't do anything.
return
}
@@ -119,14 +119,14 @@ export class StructureLayer implements Layer {
if (!config || !unitImage) return;
// Clear previous rendering
bfs(event.unit.tile(), euclDist(event.unit.tile(), config.borderRadius))
bfs(unit.tile(), euclDist(unit.tile(), config.borderRadius))
.forEach(t => this.clearCell(t.cell()));
if (!event.unit.isActive()) {
this.seenUnits.delete(event.unit)
if (!unit.isActive()) {
this.seenUnits.delete(unit)
return;
}
this.seenUnits.add(event.unit)
this.seenUnits.add(unit)
// Create temporary canvas for icon processing
const tempCanvas = document.createElement('canvas');
@@ -138,19 +138,19 @@ export class StructureLayer implements Layer {
tempContext.drawImage(unitImage, 0, 0);
const iconData = tempContext.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
const cell = event.unit.tile().cell();
const cell = unit.tile().cell();
const startX = cell.x - Math.floor(tempCanvas.width / 2);
const startY = cell.y - Math.floor(tempCanvas.height / 2);
// Draw border and territory
bfs(event.unit.tile(), euclDist(event.unit.tile(), config.borderRadius))
.forEach(t => this.paintCell(t.cell(), this.theme.borderColor(event.unit.owner().info()), 255));
bfs(unit.tile(), euclDist(unit.tile(), config.borderRadius))
.forEach(t => this.paintCell(t.cell(), this.theme.borderColor(unit.owner().info()), 255));
bfs(event.unit.tile(), euclDist(event.unit.tile(), config.territoryRadius))
.forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(event.unit.owner().info()), 130));
bfs(unit.tile(), euclDist(unit.tile(), config.territoryRadius))
.forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(unit.owner().info()), 130));
// Draw the icon
this.renderIcon(iconData, startX, startY, tempCanvas.width, tempCanvas.height, event.unit);
this.renderIcon(iconData, startX, startY, tempCanvas.width, tempCanvas.height, unit);
}
private renderIcon(
+1 -14
View File
@@ -1,5 +1,5 @@
import { PriorityQueue } from "@datastructures-js/priority-queue";
import { Cell, Game, Player, PlayerType, Tile, TileEvent, UnitEvent, UnitType } from "../../../core/game/Game";
import { Cell, Game, Player, PlayerType, Tile, Unit, UnitType, UnitUpdate } from "../../../core/game/Game";
import { PseudoRandom } from "../../../core/PseudoRandom";
import { colord, Colord } from "colord";
import { bfs, dist, euclDist, euclideanDist } from "../../../core/Util";
@@ -197,19 +197,6 @@ export class TerritoryLayer implements Layer {
this.imageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent)
}
unitEvent(event: UnitEvent) {
if (event.unit.type() == UnitType.DefensePost) {
bfs(
event.unit.tile(),
dist(event.unit.tile(), this.game.config().defensePostRange())
).forEach(t => {
if (t.isBorder()) {
this.enqueueTile(t)
}
})
}
}
enqueueTile(tile: Tile) {
this.tileToRenderQueue.push({ tile: tile, lastUpdate: this.game.ticks() + this.random.nextFloat(0, .5) })
}
+14 -10
View File
@@ -14,6 +14,10 @@ enum Relationship {
Enemy
}
class UnitEvent {
constructor(public unit: Unit, public oldTile: Tile) { }
}
export class UnitLayer implements Layer {
private canvas: HTMLCanvasElement;
private context: CanvasRenderingContext2D;
@@ -176,22 +180,22 @@ export class UnitLayer implements Layer {
}
private handleTradeShipEvent(unit: Unit) {
const rel = this.relationship(unit)
bfs(unit.oldTile, euclDist(unit.oldTile, 3)).forEach(t => {
private handleTradeShipEvent(event: UnitEvent) {
const rel = this.relationship(event.unit)
bfs(event.oldTile, euclDist(event.oldTile, 3)).forEach(t => {
this.clearCell(t.cell());
});
if (unit.isActive()) {
bfs(unit.tile(), dist(unit.tile(), 2))
.forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(unit.owner().info()), 255));
if (event.unit.isActive()) {
bfs(event.unit.tile(), dist(event.unit.tile(), 2))
.forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(event.unit.owner().info()), 255));
}
if (unit.isActive()) {
bfs(unit.tile(), dist(unit.tile(), 1))
.forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(unit.owner().info()), 255));
if (event.unit.isActive()) {
bfs(event.unit.tile(), dist(event.unit.tile(), 1))
.forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(event.unit.owner().info()), 255));
}
}
private handleBoatEvent(event: Unit) {
private handleBoatEvent(event: UnitEvent) {
const rel = this.relationship(event.unit)
if (!this.boatToTrail.has(event.unit)) {
this.boatToTrail.set(event.unit, new Set<Tile>());