mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 11:20:37 +00:00
create EventsDisplay file
This commit is contained in:
@@ -10,7 +10,7 @@ import {TerrainMap} from "../core/game/TerrainMapLoader";
|
||||
import {and, bfs, dist, manhattanDist} from "../core/Util";
|
||||
import {TerrainLayer} from "./graphics/layers/TerrainLayer";
|
||||
import {WinCheckExecution} from "../core/execution/WinCheckExecution";
|
||||
import {SendAllianceRequestEvent} from "./graphics/layers/UILayer";
|
||||
import {SendAllianceRequestUIEvent} from "./graphics/layers/UILayer";
|
||||
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ export class ClientGame {
|
||||
|
||||
this.eventBus.on(PlayerEvent, (e) => this.playerEvent(e))
|
||||
this.eventBus.on(MouseUpEvent, (e) => this.inputEvent(e))
|
||||
this.eventBus.on(SendAllianceRequestEvent, (e) => this.onSendAllianceRequest(e))
|
||||
this.eventBus.on(SendAllianceRequestUIEvent, (e) => this.onSendAllianceRequest(e))
|
||||
|
||||
this.renderer.initialize()
|
||||
this.input.initialize()
|
||||
@@ -271,7 +271,7 @@ export class ClientGame {
|
||||
}
|
||||
}
|
||||
|
||||
private onSendAllianceRequest(event: SendAllianceRequestEvent) {
|
||||
private onSendAllianceRequest(event: SendAllianceRequestUIEvent) {
|
||||
this.sendIntent({
|
||||
type: "allianceRequest",
|
||||
clientID: this.id,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {UILayer} from "./layers/UILayer";
|
||||
import {EventBus} from "../../core/EventBus";
|
||||
import {TransformHandler} from "./TransformHandler";
|
||||
import {Layer} from "./layers/Layer";
|
||||
import {EventsDisplay} from "./layers/EventsDisplay";
|
||||
|
||||
|
||||
export function createRenderer(game: Game, eventBus: EventBus, clientID: ClientID): GameRenderer {
|
||||
@@ -18,7 +19,8 @@ export function createRenderer(game: Game, eventBus: EventBus, clientID: ClientI
|
||||
new TerrainLayer(game),
|
||||
new TerritoryLayer(game, eventBus),
|
||||
new NameLayer(game, game.config().theme(), transformHandler, clientID),
|
||||
new UILayer(eventBus, game, game.config().theme(), clientID, transformHandler)
|
||||
new UILayer(eventBus, game, clientID, transformHandler),
|
||||
new EventsDisplay(eventBus, game, clientID)
|
||||
]
|
||||
|
||||
return new GameRenderer(game, eventBus, canvas, transformHandler, layers)
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import {nullable} from "zod";
|
||||
import {EventBus} from "../../../core/EventBus";
|
||||
import {AllianceRequestEvent, AllianceRequestReplyEvent, Game} from "../../../core/game/Game";
|
||||
import {ClientID} from "../../../core/Schemas";
|
||||
import {Layer} from "./Layer";
|
||||
|
||||
interface Event {
|
||||
description: string;
|
||||
buttons?: {
|
||||
text: string
|
||||
className: string
|
||||
action: () => void
|
||||
}[];
|
||||
highlight?: boolean;
|
||||
}
|
||||
|
||||
export class EventsDisplay implements Layer {
|
||||
private container: HTMLElement;
|
||||
private events: Event[];
|
||||
private tableContainer: HTMLDivElement;
|
||||
|
||||
|
||||
constructor(private eventBus: EventBus, private game: Game, private clientID: ClientID) {
|
||||
const element = document.getElementById("app");
|
||||
element.style.zIndex = "1000"
|
||||
if (!element) throw new Error(`Container element with id app not found`);
|
||||
this.container = element;
|
||||
this.events = [];
|
||||
this.createTableContainer()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.eventBus.on(AllianceRequestEvent, a => this.onAllianceRequestEvent(a))
|
||||
this.eventBus.on(AllianceRequestReplyEvent, a => this.onAllianceRequestReplyEvent(a))
|
||||
this.renderTable()
|
||||
}
|
||||
|
||||
tick() {
|
||||
}
|
||||
|
||||
private createTableContainer() {
|
||||
this.tableContainer = document.createElement('div');
|
||||
this.tableContainer.id = 'table-container';
|
||||
this.tableContainer.style.position = 'fixed';
|
||||
this.tableContainer.style.bottom = '0px'; // Distance from bottom
|
||||
this.tableContainer.style.right = '0px'; // Distance from right
|
||||
this.tableContainer.style.zIndex = '1000';
|
||||
this.tableContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.0)';
|
||||
this.tableContainer.style.padding = '20px';
|
||||
this.tableContainer.style.boxShadow = '0 0 10px rgba(0,0,0,0.0)';
|
||||
document.body.appendChild(this.tableContainer);
|
||||
this.tableContainer.style.minWidth = '400px'; // Set minimum width
|
||||
}
|
||||
|
||||
shouldTransform(): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
onAllianceRequestEvent(event: AllianceRequestEvent): void {
|
||||
const myPlayer = this.game.playerByClientID(this.clientID)
|
||||
if (myPlayer == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.allianceRequest.recipient() != myPlayer) {
|
||||
return
|
||||
}
|
||||
|
||||
this.addEvent({
|
||||
description: `${event.allianceRequest.requestor().name()} requests an alliance!`,
|
||||
buttons: [
|
||||
{
|
||||
text: "Accept",
|
||||
className: "btn",
|
||||
action: () => alert('accepted'),
|
||||
},
|
||||
{
|
||||
text: "Reject",
|
||||
className: "btn btn-info",
|
||||
action: () => alert('rejected'),
|
||||
}
|
||||
],
|
||||
highlight: true
|
||||
});
|
||||
this.renderTable()
|
||||
}
|
||||
|
||||
onAllianceRequestReplyEvent(event: AllianceRequestReplyEvent) {
|
||||
const myPlayer = this.game.playerByClientID(this.clientID)
|
||||
if (myPlayer == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.allianceRequest.requestor() != myPlayer) {
|
||||
return
|
||||
}
|
||||
|
||||
this.addEvent({
|
||||
description: `${event.allianceRequest.recipient().name()} ${event.accepted ? "accepted" : "rejected"} your alliance request`,
|
||||
highlight: true
|
||||
});
|
||||
this.renderTable()
|
||||
}
|
||||
|
||||
addEvent(event: Event): void {
|
||||
this.events.push(event);
|
||||
}
|
||||
|
||||
removeEvent(index: number): void {
|
||||
this.events.splice(index, 1);
|
||||
}
|
||||
|
||||
updateEvent(index: number, event: Event): void {
|
||||
this.events[index] = event;
|
||||
}
|
||||
|
||||
render(): void { }
|
||||
|
||||
renderTable(): void {
|
||||
let tableHtml = `
|
||||
<table class="events-table">
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
this.events.forEach((event, eventIndex) => {
|
||||
tableHtml += `
|
||||
<tr${event.highlight ? ' style="background-color: rgba(255, 255, 0, 0.1);"' : ''}>
|
||||
<td>
|
||||
${event.description}
|
||||
${event.buttons ? '<br>' + event.buttons.map((btn, btnIndex) =>
|
||||
`<button class="${btn.className}" data-event-index="${eventIndex}" data-button-index="${btnIndex}">${btn.text}</button>`
|
||||
).join('') : ''}
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
tableHtml += `
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
this.tableContainer.innerHTML = tableHtml;
|
||||
|
||||
// Add event listeners to buttons
|
||||
this.tableContainer.querySelectorAll('button').forEach(button => {
|
||||
button.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation(); // Prevent the event from reaching the canvas
|
||||
const target = e.target as HTMLElement;
|
||||
const eventIndex = parseInt(target.getAttribute('data-event-index') || '');
|
||||
const buttonIndex = parseInt(target.getAttribute('data-button-index') || '');
|
||||
|
||||
if (!isNaN(eventIndex) && !isNaN(buttonIndex)) {
|
||||
const event = this.events[eventIndex];
|
||||
const buttonAction = event.buttons?.[buttonIndex]?.action;
|
||||
if (buttonAction) {
|
||||
buttonAction();
|
||||
// Optionally, you might want to remove the event after the action is performed
|
||||
this.removeEvent(eventIndex);
|
||||
this.renderTable(); // Re-render the table if you remove the event
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,11 @@ import {EventBus, GameEvent} from "../../../core/EventBus";
|
||||
import {WinEvent} from "../../../core/execution/WinCheckExecution";
|
||||
import {AllianceRequest, AllianceRequestReplyEvent, Game, Player} from "../../../core/game/Game";
|
||||
import {ClientID} from "../../../core/Schemas";
|
||||
import {renderTroops} from "../Utils";
|
||||
import winModalHtml from '../WinModal.html';
|
||||
import {RightClickEvent} from "../../InputHandler";
|
||||
import {Layer} from "./Layer";
|
||||
import {TransformHandler} from "../TransformHandler";
|
||||
|
||||
export class SendAllianceRequestEvent implements GameEvent {
|
||||
export class SendAllianceRequestUIEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly requestor: Player,
|
||||
public readonly recipient: Player
|
||||
@@ -28,7 +26,12 @@ export class UILayer implements Layer {
|
||||
private customMenu = document.getElementById('customMenu');
|
||||
|
||||
|
||||
constructor(private eventBus: EventBus, private game: Game, private theme: Theme, private clientID: ClientID, private transformHandler: TransformHandler) {
|
||||
constructor(
|
||||
private eventBus: EventBus,
|
||||
private game: Game,
|
||||
private clientID: ClientID,
|
||||
private transformHandler: TransformHandler
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
@@ -63,7 +66,6 @@ export class UILayer implements Layer {
|
||||
this.initRightClickMenu()
|
||||
this.eventBus.on(WinEvent, (e) => this.onWinEvent(e))
|
||||
this.eventBus.on(RightClickEvent, (e) => this.onRightClick(e))
|
||||
this.eventBus.on(AllianceRequestReplyEvent, (e) => this.onAllianceRequestReplyEvent(e))
|
||||
}
|
||||
|
||||
initRightClickMenu() {
|
||||
@@ -193,17 +195,6 @@ export class UILayer implements Layer {
|
||||
this.showWinModal(event.winner)
|
||||
}
|
||||
|
||||
onAllianceRequestReplyEvent(event: AllianceRequestReplyEvent) {
|
||||
if (event.allianceRequest.requestor().clientID() == this.clientID) {
|
||||
const recipient = event.allianceRequest.recipient().name()
|
||||
if (event.accepted) {
|
||||
alert(`${recipient} accepted your alliance request`)
|
||||
} else {
|
||||
alert(`${recipient} rejected your alliance request`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showWinModal(winner: Player) {
|
||||
if (this.winModal) {
|
||||
const message = this.winModal.querySelector('#winMessage');
|
||||
@@ -265,7 +256,7 @@ export class UILayer implements Layer {
|
||||
label: "Request Alliance",
|
||||
action: (): void => {
|
||||
this.eventBus.emit(
|
||||
new SendAllianceRequestEvent(myPlayer, owner)
|
||||
new SendAllianceRequestUIEvent(myPlayer, owner)
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="app"></div>
|
||||
|
||||
<style>
|
||||
body {
|
||||
|
||||
@@ -231,4 +231,71 @@ h3 {
|
||||
|
||||
#customMenu ul li:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
#table-container {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* Events Table Styles */
|
||||
.events-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.events-table th,
|
||||
.events-table td {
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.0);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.events-table th {
|
||||
background-color: rgba(0, 0, 0, 0.0);
|
||||
font-size: 1.2em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.events-table tr:hover {
|
||||
background-color: rgba(255, 255, 255, 0.0);
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
margin: 5px 10px 5px 0;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
.btn-info:hover {
|
||||
background-color: #0b7dda;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
|
||||
.events-table th,
|
||||
.events-table td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,13 @@ export const devConfig = new class extends DefaultConfig {
|
||||
return 80
|
||||
}
|
||||
numSpawnPhaseTurns(): number {
|
||||
return 40
|
||||
return 80
|
||||
}
|
||||
gameCreationRate(): number {
|
||||
return 2 * 1000
|
||||
return 10 * 1000
|
||||
}
|
||||
lobbyLifetime(): number {
|
||||
return 2 * 1000
|
||||
return 10 * 1000
|
||||
}
|
||||
turnIntervalMs(): number {
|
||||
return 100
|
||||
|
||||
@@ -3,8 +3,8 @@ import {AllianceRequest, Execution, MutableGame, MutablePlayer, Player, PlayerID
|
||||
export class AllianceRequestExecution implements Execution {
|
||||
private active = true
|
||||
private mg: MutableGame = null
|
||||
private requestor: Player;
|
||||
private recipient: Player
|
||||
private requestor: MutablePlayer;
|
||||
private recipient: MutablePlayer
|
||||
|
||||
constructor(private requestorID: PlayerID, private recipientID: PlayerID) { }
|
||||
|
||||
@@ -18,7 +18,7 @@ export class AllianceRequestExecution implements Execution {
|
||||
if (this.requestor.alliedWith(this.recipient)) {
|
||||
console.warn('already allied')
|
||||
} else {
|
||||
this.mg.createAllianceRequest(this.requestor, this.recipient)
|
||||
this.requestor.createAllianceRequest(this.recipient)
|
||||
}
|
||||
this.active = false
|
||||
}
|
||||
|
||||
@@ -157,12 +157,14 @@ export interface MutablePlayer extends Player {
|
||||
outgoingAllianceRequests(): MutableAllianceRequest[]
|
||||
alliances(): MutableAlliance[]
|
||||
breakAllianceWith(other: Player): void
|
||||
createAllianceRequest(recipient: Player): MutableAllianceRequest
|
||||
addBoat(troops: number, tile: Tile, target: Player | TerraNullius): MutableBoat
|
||||
}
|
||||
|
||||
export interface Game {
|
||||
// Throws exception is player not found
|
||||
player(id: PlayerID): Player
|
||||
playerByClientID(id: ClientID): Player | null
|
||||
hasPlayer(id: PlayerID): boolean
|
||||
players(): Player[]
|
||||
tile(cell: Cell): Tile
|
||||
@@ -183,11 +185,10 @@ export interface Game {
|
||||
|
||||
export interface MutableGame extends Game {
|
||||
player(id: PlayerID): MutablePlayer
|
||||
playerByClientID(id: ClientID): MutablePlayer | null
|
||||
players(): MutablePlayer[]
|
||||
addPlayer(playerInfo: PlayerInfo, troops: number): MutablePlayer
|
||||
executions(): Execution[]
|
||||
// todo move to player.
|
||||
createAllianceRequest(requestor: Player, recipient: Player): MutableAllianceRequest
|
||||
}
|
||||
|
||||
export class TileEvent implements GameEvent {
|
||||
@@ -202,6 +203,10 @@ export class BoatEvent implements GameEvent {
|
||||
constructor(public readonly boat: Boat, public oldTile: Tile) { }
|
||||
}
|
||||
|
||||
export class AllianceRequestEvent implements GameEvent {
|
||||
constructor(public readonly allianceRequest: AllianceRequest) { }
|
||||
}
|
||||
|
||||
export class AllianceRequestReplyEvent implements GameEvent {
|
||||
constructor(public readonly allianceRequest: AllianceRequest, public readonly accepted: boolean) { }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import {info} from "console";
|
||||
import {Config} from "../configuration/Config";
|
||||
import {EventBus} from "../EventBus";
|
||||
import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, BoatEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent} from "./Game";
|
||||
import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, BoatEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent, AllianceRequestEvent} from "./Game";
|
||||
import {TerrainMap} from "./TerrainMapLoader";
|
||||
import {PlayerImpl} from "./PlayerImpl";
|
||||
import {TerraNulliusImpl} from "./TerraNulliusImpl";
|
||||
import {TileImpl} from "./TileImpl";
|
||||
import {AllianceRequestImpl} from "./AllianceRequestImpl";
|
||||
import {AllianceImpl} from "./AllianceImpl";
|
||||
import {ClientID} from "../Schemas";
|
||||
|
||||
export function createGame(terrainMap: TerrainMap, eventBus: EventBus, config: Config): Game {
|
||||
return new GameImpl(terrainMap, eventBus, config)
|
||||
@@ -50,6 +51,7 @@ export class GameImpl implements MutableGame {
|
||||
createAllianceRequest(requestor: Player, recipient: Player): MutableAllianceRequest {
|
||||
const ar = new AllianceRequestImpl(requestor, recipient, this._ticks, this)
|
||||
this.allianceRequests.push(ar)
|
||||
this.eventBus.emit(new AllianceRequestEvent(ar))
|
||||
return ar
|
||||
}
|
||||
|
||||
@@ -192,6 +194,16 @@ export class GameImpl implements MutableGame {
|
||||
return this._players.get(id)
|
||||
}
|
||||
|
||||
playerByClientID(id: ClientID): MutablePlayer | null {
|
||||
for (const [pID, player] of this._players) {
|
||||
if (player.clientID() == id) {
|
||||
return player
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
tile(cell: Cell): Tile {
|
||||
this.assertIsOnMap(cell)
|
||||
return this.map[cell.x][cell.y]
|
||||
|
||||
@@ -150,6 +150,10 @@ export class PlayerImpl implements MutablePlayer {
|
||||
this.gs.breakAlliance(this, other)
|
||||
}
|
||||
|
||||
createAllianceRequest(recipient: Player): MutableAllianceRequest {
|
||||
return this.gs.createAllianceRequest(this, recipient)
|
||||
}
|
||||
|
||||
hash(): number {
|
||||
return simpleHash(this.id()) * (this.troops() + this.numTilesOwned());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user