mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 15:30:43 +00:00
thread-split events display
This commit is contained in:
@@ -2,11 +2,14 @@ import { LitElement, html, css } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import {
|
||||
AllianceExpiredUpdate,
|
||||
AllianceRequestReplyUpdate,
|
||||
AllianceRequestUpdate,
|
||||
AllPlayers,
|
||||
BrokeAllianceUpdate,
|
||||
DisplayMessageUpdate,
|
||||
EmojiUpdate,
|
||||
GameUpdateType,
|
||||
MessageType,
|
||||
TargetPlayerUpdate,
|
||||
} from "../../../core/game/Game";
|
||||
@@ -139,6 +142,14 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
}
|
||||
`;
|
||||
|
||||
private updateMap = new Map([
|
||||
[GameUpdateType.DisplayEvent, u => this.onDisplayMessageEvent(u)],
|
||||
[GameUpdateType.AllianceRequestReply, u => this.onAllianceRequestReplyEvent(u)],
|
||||
[GameUpdateType.BrokeAlliance, u => this.onBrokeAllianceEvent(u)],
|
||||
[GameUpdateType.TargetPlayer, u => this.onTargetPlayerEvent(u)],
|
||||
[GameUpdateType.EmojiUpdate, u => this.onEmojiMessageEvent(u)]
|
||||
])
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.events = [];
|
||||
@@ -148,6 +159,12 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
tick() {
|
||||
const updates = this.game.updatesSinceLastTick()
|
||||
for (const [ut, fn] of this.updateMap) {
|
||||
updates[ut]?.forEach(u => fn(u))
|
||||
}
|
||||
|
||||
|
||||
let remainingEvents = this.events.filter(event => {
|
||||
const shouldKeep = this.game.ticks() - event.createdAt < 80;
|
||||
if (!shouldKeep && event.onDelete) {
|
||||
@@ -235,96 +252,102 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
});
|
||||
}
|
||||
|
||||
// onAllianceRequestReplyEvent(event: AllianceRequestReplyUpdate) {
|
||||
// const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
// if (!myPlayer || event.allianceRequest.requestor() !== myPlayer) {
|
||||
// return;
|
||||
// }
|
||||
onAllianceRequestReplyEvent(update: AllianceRequestReplyUpdate) {
|
||||
const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
if (!myPlayer || update.request.requestorID !== myPlayer.smallID()) {
|
||||
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(),
|
||||
// });
|
||||
// }
|
||||
const recipient = this.game.playerBySmallID(update.request.recipientID)
|
||||
|
||||
// onBrokeAllianceEvent(event: BrokeAllianceEvent) {
|
||||
// const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
// if (!myPlayer) return;
|
||||
this.addEvent({
|
||||
description: `${recipient.name()} ${update.accepted ? "accepted" : "rejected"} your alliance request`,
|
||||
type: update.accepted ? MessageType.SUCCESS : 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(),
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
onBrokeAllianceEvent(update: BrokeAllianceUpdate) {
|
||||
const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
if (!myPlayer) return;
|
||||
|
||||
// onAllianceExpiredEvent(event: AllianceExpiredEvent) {
|
||||
// const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
// if (!myPlayer) return;
|
||||
const betrayed = this.game.playerBySmallID(update.betrayedID)
|
||||
const traitor = this.game.playerBySmallID(update.traitorID)
|
||||
|
||||
// const other = event.player1 === myPlayer ? event.player2 : event.player2 === myPlayer ? event.player1 : null;
|
||||
// if (!other || !myPlayer.isAlive() || !other.isAlive()) return;
|
||||
if (!betrayed.isTraitor() && traitor === myPlayer) {
|
||||
this.addEvent({
|
||||
description: `You broke your alliance with ${betrayed.name()}, making you a TRAITOR`,
|
||||
type: MessageType.ERROR,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
});
|
||||
} else if (betrayed === myPlayer) {
|
||||
this.addEvent({
|
||||
description: `${traitor.name()}, broke their alliance with you`,
|
||||
type: MessageType.ERROR,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// this.addEvent({
|
||||
// description: `Your alliance with ${other.name()} expired`,
|
||||
// type: MessageType.WARN,
|
||||
// highlight: true,
|
||||
// createdAt: this.game.ticks(),
|
||||
// });
|
||||
// }
|
||||
onAllianceExpiredEvent(update: AllianceExpiredUpdate) {
|
||||
const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
if (!myPlayer) return;
|
||||
|
||||
// onTargetPlayerEvent(event: TargetPlayerUpdate) {
|
||||
// const other = this.game.playerBySmallID(event.playerID)
|
||||
// const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
// if (!myPlayer || !myPlayer.isAlliedWith(other)) return;
|
||||
const otherID = update.player1ID === myPlayer.smallID() ? update.player2ID : update.player2ID === myPlayer.smallID() ? update.player1ID : null;
|
||||
const other = this.game.playerBySmallID(otherID)
|
||||
if (!other || !myPlayer.isAlive() || !other.isAlive()) return;
|
||||
|
||||
// const target = this.game.playerBySmallID(event.targetID)
|
||||
this.addEvent({
|
||||
description: `Your alliance with ${other.name()} expired`,
|
||||
type: MessageType.WARN,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
});
|
||||
}
|
||||
|
||||
// this.addEvent({
|
||||
// description: `${other.name()} requests you attack ${target.name()}`,
|
||||
// type: MessageType.INFO,
|
||||
// highlight: true,
|
||||
// createdAt: this.game.ticks(),
|
||||
// });
|
||||
// }
|
||||
onTargetPlayerEvent(event: TargetPlayerUpdate) {
|
||||
const other = this.game.playerBySmallID(event.playerID)
|
||||
const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
if (!myPlayer || !myPlayer.isAlliedWith(other)) return;
|
||||
|
||||
// onEmojiMessageEvent(update: EmojiUpdate) {
|
||||
// const myPlayer = this.game.playerByClientID(this.clientID);
|
||||
// if (!myPlayer) return;
|
||||
const target = this.game.playerBySmallID(event.targetID)
|
||||
|
||||
// const recipient = update.recipientID == AllPlayers ? AllPlayers : this.game.playerBySmallID(update.recipientID)
|
||||
// const sender = this.game.playerBySmallID(update.senderID)
|
||||
this.addEvent({
|
||||
description: `${other.name()} requests you attack ${target.name()}`,
|
||||
type: MessageType.INFO,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
});
|
||||
}
|
||||
|
||||
// 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(),
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
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) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getConfig } from "./configuration/Config";
|
||||
import { EventBus } from "./EventBus";
|
||||
import { Executor } from "./execution/ExecutionManager";
|
||||
import { WinCheckExecution } from "./execution/WinCheckExecution";
|
||||
import { Cell, DisplayMessageUpdate, Game, GameUpdateType, MessageType, MutableGame, MutableTile, NameViewData, Player, PlayerActions, PlayerID, Tile, TileUpdate, UnitType, UnitUpdate } from "./game/Game";
|
||||
import { Cell, DisplayMessageUpdate, Game, GameUpdateType, MessageType, MutableGame, MutableTile, NameViewData, Player, PlayerActions, PlayerID, PlayerProfile, Tile, TileUpdate, UnitType, UnitUpdate } from "./game/Game";
|
||||
import { createGame } from "./game/GameImpl";
|
||||
import { loadTerrainMap } from "./game/TerrainMapLoader";
|
||||
import { GameConfig, Turn } from "./Schemas";
|
||||
@@ -105,6 +105,12 @@ export class GameRunner {
|
||||
return actions
|
||||
}
|
||||
|
||||
public playerProfile(playerID: number): PlayerProfile {
|
||||
return {
|
||||
relations: this.game.players().filter(p => p.smallID() == playerID)[0]?.allRelationsSorted()
|
||||
}
|
||||
}
|
||||
|
||||
private canBoat(myPlayer: Player, tile: Tile): boolean {
|
||||
const other = tile.owner()
|
||||
if (myPlayer.units(UnitType.TransportShip).length >= this.game.config().boatMaxNumber()) {
|
||||
|
||||
@@ -271,6 +271,10 @@ export class GameView {
|
||||
}
|
||||
}
|
||||
|
||||
public updatesSinceLastTick(): GameUpdates {
|
||||
return this.lastUpdate.updates
|
||||
}
|
||||
|
||||
public update(gu: GameUpdateViewData) {
|
||||
this.lastUpdate = gu
|
||||
|
||||
|
||||
@@ -412,6 +412,11 @@ export interface PlayerActions {
|
||||
interaction?: PlayerInteraction
|
||||
}
|
||||
|
||||
export interface PlayerProfile {
|
||||
relations: Record<number, Relation>
|
||||
// TODO: add alliances etc
|
||||
}
|
||||
|
||||
export interface PlayerInteraction {
|
||||
sharedBorder: boolean
|
||||
canSendEmoji: boolean
|
||||
@@ -495,8 +500,8 @@ export interface BrokeAllianceUpdate {
|
||||
|
||||
export interface AllianceExpiredUpdate {
|
||||
type: GameUpdateType.AllianceExpired
|
||||
player1: number
|
||||
player2: number
|
||||
player1ID: number
|
||||
player2ID: number
|
||||
}
|
||||
|
||||
export interface TargetPlayerUpdate {
|
||||
|
||||
@@ -473,8 +473,8 @@ export class GameImpl implements MutableGame {
|
||||
this.alliances_ = this.alliances_.filter(a => a != alliances[0])
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.AllianceExpired,
|
||||
player1: alliance.requestor().smallID(),
|
||||
player2: alliance.recipient().smallID()
|
||||
player1ID: alliance.requestor().smallID(),
|
||||
player2ID: alliance.recipient().smallID()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,24 @@ ctx.addEventListener('message', async (e: MessageEvent<MainThreadMessage>) => {
|
||||
throw error;
|
||||
}
|
||||
break;
|
||||
case 'player_profile':
|
||||
if (!gameRunner) {
|
||||
throw new Error('Game runner not initialized');
|
||||
}
|
||||
|
||||
try {
|
||||
const actions = (await gameRunner).playerActions(message.playerID, message.x, message.y)
|
||||
sendMessage({
|
||||
type: 'player_actions_result',
|
||||
id: message.id,
|
||||
result: actions
|
||||
} as PlayerActionsResultMessage);
|
||||
} catch (error) {
|
||||
console.error('Failed to check borders:', error);
|
||||
throw error;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
console.warn('Unknown message :', message);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PlayerActions, PlayerID, Tile } from "../game/Game";
|
||||
import { PlayerActions, PlayerID, PlayerInfo, PlayerProfile, Tile } from "../game/Game";
|
||||
import { GameUpdateViewData } from "../GameView";
|
||||
import { GameConfig, GameID, Turn } from "../Schemas";
|
||||
import { generateID } from "../Util";
|
||||
@@ -91,6 +91,30 @@ export class WorkerClient {
|
||||
});
|
||||
}
|
||||
|
||||
playerInfo(playerID: number): Promise<PlayerProfile> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.isInitialized) {
|
||||
reject(new Error('Worker not initialized'));
|
||||
return;
|
||||
}
|
||||
|
||||
const messageId = generateID()
|
||||
|
||||
this.messageHandlers.set(messageId, (message) => {
|
||||
if (message.type === 'player_profile_result' && message.result !== undefined) {
|
||||
resolve(message.result);
|
||||
}
|
||||
});
|
||||
|
||||
this.worker.postMessage({
|
||||
type: 'player_profile',
|
||||
id: messageId,
|
||||
playerID: playerID,
|
||||
});
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
playerInteraction(playerID: PlayerID, tile: Tile): Promise<PlayerActions> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.isInitialized) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GameUpdateViewData } from "../GameView";
|
||||
import { GameConfig, GameID, Turn } from "../Schemas";
|
||||
import { PlayerActions, PlayerID } from "../game/Game";
|
||||
import { PlayerActions, PlayerID, PlayerProfile } from "../game/Game";
|
||||
|
||||
export type WorkerMessageType =
|
||||
| 'heartbeat'
|
||||
@@ -9,7 +9,9 @@ export type WorkerMessageType =
|
||||
| 'turn'
|
||||
| 'game_update'
|
||||
| 'player_actions'
|
||||
| 'player_actions_result';
|
||||
| 'player_actions_result'
|
||||
| 'player_profile'
|
||||
| 'player_profile_result'
|
||||
|
||||
// Base interface for all messages
|
||||
interface BaseWorkerMessage {
|
||||
@@ -55,8 +57,18 @@ export interface PlayerActionsResultMessage extends BaseWorkerMessage {
|
||||
result: PlayerActions;
|
||||
}
|
||||
|
||||
export interface PlayerProfileMessage extends BaseWorkerMessage {
|
||||
type: 'player_profile'
|
||||
playerID: number
|
||||
}
|
||||
|
||||
export interface PlayerProfileResultMessage extends BaseWorkerMessage {
|
||||
type: 'player_profile_result'
|
||||
result: PlayerProfile
|
||||
}
|
||||
|
||||
// Union types for type safety
|
||||
export type MainThreadMessage = HeartbeatMessage | InitMessage | TurnMessage | PlayerActionsMessage
|
||||
export type MainThreadMessage = HeartbeatMessage | InitMessage | TurnMessage | PlayerActionsMessage | PlayerProfileMessage
|
||||
|
||||
// Message send from worker
|
||||
export type WorkerMessage = InitializedMessage | GameUpdateMessage | PlayerActionsResultMessage;
|
||||
export type WorkerMessage = InitializedMessage | GameUpdateMessage | PlayerActionsResultMessage | PlayerProfileResultMessage
|
||||
Reference in New Issue
Block a user