mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:50:43 +00:00
can change name after joining game
This commit is contained in:
@@ -47,8 +47,8 @@
|
||||
* improve menu (keep highlighted when click, allow deselect lobby) DONE 8/25/2024
|
||||
* give time to (re) spawn at start of game DONE 8/25/2024
|
||||
* show bar for long to respawn DONE 8/26/2024
|
||||
* store & delay tile updates for lag compensation
|
||||
* BUG: error if don't spawn and then click after spawn mode
|
||||
* store & delay tile updates for lag compensation DONE 8/26/2024
|
||||
* BUG: error if don't spawn and then click after spawn mode DONE 8/26/2024
|
||||
* BUG: change player name after join lobby
|
||||
* REFACTOR: use new priority queue
|
||||
* BUG: players attack each other same time creates islands
|
||||
|
||||
@@ -36,6 +36,12 @@ class Client {
|
||||
setFavicon()
|
||||
this.terrainMap = loadTerrainMap()
|
||||
this.startLobbyPolling()
|
||||
setupUsernameCallback((username) => {
|
||||
console.log('Username updated:', username);
|
||||
if (this.game != null) {
|
||||
this.game.playerName = username
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private startLobbyPolling(): void {
|
||||
@@ -118,6 +124,11 @@ class Client {
|
||||
g.stop();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function getUsername(): string {
|
||||
@@ -129,6 +140,21 @@ function getUsername(): string {
|
||||
return 'Anon'; // Return 'Anon' if the input element is not found
|
||||
}
|
||||
|
||||
function setupUsernameCallback(callback: (username: string) => void): void {
|
||||
const usernameInput = document.getElementById('username') as HTMLInputElement | null;
|
||||
if (usernameInput) {
|
||||
usernameInput.addEventListener('input', () => {
|
||||
const username = getUsername();
|
||||
callback(username);
|
||||
});
|
||||
} else {
|
||||
console.error('Username input element not found');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Initialize the client when the DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new Client().initialize();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Executor} from "../core/execution/Executor";
|
||||
import {Executor} from "../core/execution/ExecutionManager";
|
||||
import {Cell, MutableGame, PlayerEvent, PlayerID, MutablePlayer, TileEvent, Player, Game, BoatEvent, Tile} from "../core/Game";
|
||||
import {createGame} from "../core/GameImpl";
|
||||
import {EventBus} from "../core/EventBus";
|
||||
@@ -46,7 +46,7 @@ export class ClientGame {
|
||||
private isProcessingTurn = false
|
||||
|
||||
constructor(
|
||||
private playerName: string,
|
||||
public playerName: string,
|
||||
private id: ClientID,
|
||||
private gameID: GameID,
|
||||
private eventBus: EventBus,
|
||||
@@ -86,6 +86,13 @@ export class ClientGame {
|
||||
if (!this.isActive) {
|
||||
this.start()
|
||||
}
|
||||
this.sendIntent(
|
||||
{
|
||||
type: "updateName",
|
||||
name: this.playerName,
|
||||
clientID: this.id
|
||||
}
|
||||
)
|
||||
}
|
||||
if (message.type == "turn") {
|
||||
this.addTurn(message.turn)
|
||||
@@ -163,7 +170,7 @@ export class ClientGame {
|
||||
|
||||
private playerEvent(event: PlayerEvent) {
|
||||
console.log('received new player event!')
|
||||
if (event.player.info().clientID == this.id) {
|
||||
if (event.player.clientID() == this.id) {
|
||||
console.log('setting name')
|
||||
this.myPlayer = event.player
|
||||
}
|
||||
@@ -190,6 +197,9 @@ export class ClientGame {
|
||||
if (this.gs.inSpawnPhase()) {
|
||||
return
|
||||
}
|
||||
if (this.myPlayer == null) {
|
||||
return
|
||||
}
|
||||
|
||||
const owner = tile.owner()
|
||||
const targetID = owner.isPlayer() ? owner.id() : null;
|
||||
|
||||
@@ -32,7 +32,7 @@ export function placeName(game: Game, player: Player): [position: Cell, fontSize
|
||||
Math.floor(largestRectangle.y + largestRectangle.height / 2 + boundingBox.min.y),
|
||||
)
|
||||
|
||||
const fontSize = calculateFontSize(largestRectangle, player.info().name);
|
||||
const fontSize = calculateFontSize(largestRectangle, player.name());
|
||||
|
||||
return [center, fontSize]
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export class NameRenderer {
|
||||
|
||||
isVisible(render: RenderInfo, min: Cell, max: Cell): boolean {
|
||||
const ratio = (max.x - min.x) / Math.max(20, (render.boundingBox.max.x - render.boundingBox.min.x))
|
||||
if (render.player.info().isBot) {
|
||||
if (render.player.isBot()) {
|
||||
if (ratio > 25) {
|
||||
return false
|
||||
}
|
||||
@@ -121,7 +121,7 @@ export class NameRenderer {
|
||||
context.textAlign = 'center';
|
||||
context.textBaseline = 'middle';
|
||||
|
||||
context.fillText(render.player.info().name, nameCenterX, nameCenterY - render.fontSize / 2);
|
||||
context.fillText(render.player.name(), nameCenterX, nameCenterY - render.fontSize / 2);
|
||||
context.font = `bold ${render.fontSize}px ${this.theme.font()}`;
|
||||
let troopsStr: string = ""
|
||||
let troops = render.player.troops() / 10
|
||||
|
||||
+4
-1
@@ -82,8 +82,10 @@ export interface TerraNullius {
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
info(): PlayerInfo
|
||||
name(): string
|
||||
clientID(): ClientID
|
||||
id(): PlayerID
|
||||
isBot(): boolean
|
||||
troops(): number
|
||||
boats(): Boat[]
|
||||
ownsTile(cell: Cell): boolean
|
||||
@@ -99,6 +101,7 @@ export interface Player {
|
||||
}
|
||||
|
||||
export interface MutablePlayer extends Player {
|
||||
setName(name: string): void
|
||||
setTroops(troops: number): void
|
||||
addTroops(troops: number): void
|
||||
removeTroops(troops: number): void
|
||||
|
||||
+25
-2
@@ -1,6 +1,7 @@
|
||||
import {Config} from "./configuration/Config";
|
||||
import {EventBus} from "./EventBus";
|
||||
import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, MutableBoat, BoatEvent} from "./Game";
|
||||
import {ClientID} from "./Schemas";
|
||||
import {Terrain, TerrainMap, TerrainType} from "./TerrainMapLoader";
|
||||
import {simpleHash} from "./Util";
|
||||
|
||||
@@ -123,7 +124,30 @@ export class PlayerImpl implements MutablePlayer {
|
||||
public _boats: BoatImpl[] = []
|
||||
public _tiles: Map<CellString, Tile> = new Map<CellString, Tile>()
|
||||
|
||||
constructor(private gs: GameImpl, public readonly playerInfo: PlayerInfo, private _troops) {
|
||||
private _name: string
|
||||
|
||||
constructor(private gs: GameImpl, private readonly playerInfo: PlayerInfo, private _troops) {
|
||||
this._name = playerInfo.name
|
||||
}
|
||||
|
||||
name(): string {
|
||||
return this._name
|
||||
}
|
||||
|
||||
clientID(): ClientID {
|
||||
return this.playerInfo.clientID
|
||||
}
|
||||
|
||||
id(): PlayerID {
|
||||
return this.playerInfo.id
|
||||
}
|
||||
|
||||
isBot(): boolean {
|
||||
return this.playerInfo.isBot
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
|
||||
}
|
||||
|
||||
addBoat(troops: number, tile: Tile, target: Player | TerraNullius): BoatImpl {
|
||||
@@ -190,7 +214,6 @@ export class PlayerImpl implements MutablePlayer {
|
||||
this.gs.relinquish(tile)
|
||||
}
|
||||
info(): PlayerInfo {return this.playerInfo}
|
||||
id(): PlayerID {return this.playerInfo.id}
|
||||
troops(): number {return this._troops}
|
||||
isAlive(): boolean {return this._tiles.size > 0}
|
||||
gameState(): MutableGame {return this.gs}
|
||||
|
||||
+10
-3
@@ -3,11 +3,13 @@ import {z} from 'zod';
|
||||
export type GameID = string
|
||||
export type ClientID = string
|
||||
|
||||
export type Intent = SpawnIntent | AttackIntent | BoatAttackIntent
|
||||
export type Intent = SpawnIntent | AttackIntent | BoatAttackIntent | UpdateNameIntent
|
||||
|
||||
export type AttackIntent = z.infer<typeof AttackIntentSchema>
|
||||
export type SpawnIntent = z.infer<typeof SpawnIntentSchema>
|
||||
export type BoatAttackIntent = z.infer<typeof BoatAttackIntentSchema>
|
||||
export type UpdateNameIntent = z.infer<typeof UpdateNameIntentSchema>
|
||||
|
||||
|
||||
export type Turn = z.infer<typeof TurnSchema>
|
||||
|
||||
@@ -31,7 +33,7 @@ export interface Lobby {
|
||||
|
||||
// Zod schemas
|
||||
const BaseIntentSchema = z.object({
|
||||
type: z.enum(['attack', 'spawn', 'boat']),
|
||||
type: z.enum(['attack', 'spawn', 'boat', 'name']),
|
||||
clientID: z.string(),
|
||||
});
|
||||
|
||||
@@ -62,7 +64,12 @@ export const BoatAttackIntentSchema = BaseIntentSchema.extend({
|
||||
y: z.number(),
|
||||
})
|
||||
|
||||
const IntentSchema = z.union([AttackIntentSchema, SpawnIntentSchema, BoatAttackIntentSchema]);
|
||||
export const UpdateNameIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal('updateName'),
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
const IntentSchema = z.union([AttackIntentSchema, SpawnIntentSchema, BoatAttackIntentSchema, UpdateNameIntentSchema]);
|
||||
|
||||
const TurnSchema = z.object({
|
||||
turnNumber: z.number(),
|
||||
|
||||
@@ -56,7 +56,7 @@ export class DefaultPlayerConfig implements PlayerConfig {
|
||||
}
|
||||
|
||||
attackAmount(attacker: Player, defender: Player | TerraNullius) {
|
||||
if (attacker.info().isBot) {
|
||||
if (attacker.isBot()) {
|
||||
return attacker.troops() / 20
|
||||
} else {
|
||||
return attacker.troops() / 5
|
||||
|
||||
@@ -7,10 +7,10 @@ export const devConfig = new class extends DefaultConfig {
|
||||
return 40
|
||||
}
|
||||
gameCreationRate(): number {
|
||||
return 3 * 1000
|
||||
return 10 * 1000
|
||||
}
|
||||
lobbyLifetime(): number {
|
||||
return 3 * 1000
|
||||
return 10 * 1000
|
||||
}
|
||||
turnIntervalMs(): number {
|
||||
return 100
|
||||
|
||||
@@ -6,6 +6,7 @@ import {SpawnExecution} from "./SpawnExecution";
|
||||
import {BotSpawner} from "./BotSpawner";
|
||||
import {BoatAttackExecution} from "./BoatAttackExecution";
|
||||
import {PseudoRandom} from "../PseudoRandom";
|
||||
import {UpdateNameExecution} from "./UpdateNameExecution";
|
||||
|
||||
|
||||
export class Executor {
|
||||
@@ -40,6 +41,11 @@ export class Executor {
|
||||
new Cell(intent.x, intent.y),
|
||||
intent.troops
|
||||
)
|
||||
} else if (intent.type == "updateName") {
|
||||
return new UpdateNameExecution(
|
||||
intent.name,
|
||||
intent.clientID
|
||||
)
|
||||
} else {
|
||||
throw new Error(`intent type ${intent} not found`)
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export class SpawnExecution implements Execution {
|
||||
return
|
||||
}
|
||||
|
||||
const existing = this.mg.players().find(p => p.info().clientID != null && p.info().clientID == this.playerInfo.clientID)
|
||||
const existing = this.mg.players().find(p => p.clientID() != null && p.clientID() == this.playerInfo.clientID)
|
||||
if (existing) {
|
||||
existing.tiles().forEach(t => existing.relinquish(t))
|
||||
getSpawnCells(this.mg, this.cell).forEach(c => {
|
||||
@@ -42,7 +42,7 @@ export class SpawnExecution implements Execution {
|
||||
player.conquer(this.mg.tile(c))
|
||||
})
|
||||
this.mg.addExecution(new PlayerExecution(player.id()))
|
||||
if (player.info().isBot) {
|
||||
if (player.isBot()) {
|
||||
this.mg.addExecution(new BotExecution(player))
|
||||
}
|
||||
this.active = false
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import {Config, PlayerConfig} from "../configuration/Config"
|
||||
import {Execution, MutableGame, MutablePlayer, PlayerID} from "../Game"
|
||||
import {ClientID} from "../Schemas"
|
||||
|
||||
export class UpdateNameExecution implements Execution {
|
||||
|
||||
private active = true
|
||||
private mg: MutableGame
|
||||
|
||||
constructor(private newName: string, private clientID: ClientID) {
|
||||
}
|
||||
|
||||
init(mg: MutableGame, ticks: number) {
|
||||
this.mg = mg
|
||||
}
|
||||
|
||||
tick(ticks: number) {
|
||||
const player = this.mg.players().find(p => p.clientID() == this.clientID)
|
||||
if (player == null) {
|
||||
return
|
||||
}
|
||||
player.setName(this.newName)
|
||||
this.active = false
|
||||
}
|
||||
|
||||
owner(): MutablePlayer {
|
||||
return null
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.active
|
||||
}
|
||||
activeDuringSpawnPhase(): boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user