mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-03 18:30:36 +00:00
Random spawn (#2375)
## Description: https://github.com/openfrontio/OpenFrontIO/issues/2352 This is my first PR in this project, and I’ll continue refining it based on feedback. <img width="1088" height="859" alt="image" src="https://github.com/user-attachments/assets/07f4f8b1-52fa-4136-add4-19b00aefd963" /> <img width="1157" height="783" alt="image" src="https://github.com/user-attachments/assets/1c5be80d-72f8-4ead-8d4b-706a3a04fd73" /> <img width="1488" height="777" alt="image" src="https://github.com/user-attachments/assets/4d743548-f0c3-4579-963b-43676f68fab1" /> <img width="1499" height="778" alt="image" src="https://github.com/user-attachments/assets/f808e44f-ef97-467f-9e41-812e2857c36e" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: nikolaj_mykola --------- Co-authored-by: Evan <evanpelle@gmail.com>
This commit is contained in:
@@ -48,6 +48,7 @@ import {
|
||||
} from "./Transport";
|
||||
import { createCanvas } from "./Utils";
|
||||
import { createRenderer, GameRenderer } from "./graphics/GameRenderer";
|
||||
import { GoToPlayerEvent } from "./graphics/layers/Leaderboard";
|
||||
import SoundManager from "./sound/SoundManager";
|
||||
|
||||
export interface LobbyConfig {
|
||||
@@ -202,6 +203,8 @@ export class ClientGameRunner {
|
||||
|
||||
private lastMessageTime: number = 0;
|
||||
private connectionCheckInterval: NodeJS.Timeout | null = null;
|
||||
private goToPlayerTimeout: NodeJS.Timeout | null = null;
|
||||
|
||||
private lastTickReceiveTime: number = 0;
|
||||
private currentTickDelay: number | undefined = undefined;
|
||||
|
||||
@@ -325,6 +328,39 @@ export class ClientGameRunner {
|
||||
if (message.type === "start") {
|
||||
this.hasJoined = true;
|
||||
console.log("starting game!");
|
||||
|
||||
if (this.gameView.config().isRandomSpawn()) {
|
||||
const goToPlayer = () => {
|
||||
const myPlayer = this.gameView.myPlayer();
|
||||
|
||||
if (this.gameView.inSpawnPhase() && !myPlayer?.hasSpawned()) {
|
||||
this.goToPlayerTimeout = setTimeout(goToPlayer, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!myPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.gameView.inSpawnPhase() && !myPlayer.hasSpawned()) {
|
||||
showErrorModal(
|
||||
"spawn_failed",
|
||||
translateText("error_modal.spawn_failed.description"),
|
||||
this.lobby.gameID,
|
||||
this.lobby.clientID,
|
||||
true,
|
||||
false,
|
||||
translateText("error_modal.spawn_failed.title"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventBus.emit(new GoToPlayerEvent(myPlayer));
|
||||
};
|
||||
|
||||
goToPlayer();
|
||||
}
|
||||
|
||||
for (const turn of message.turns) {
|
||||
if (turn.turnNumber < this.turnsSeen) {
|
||||
continue;
|
||||
@@ -402,6 +438,10 @@ export class ClientGameRunner {
|
||||
clearInterval(this.connectionCheckInterval);
|
||||
this.connectionCheckInterval = null;
|
||||
}
|
||||
if (this.goToPlayerTimeout) {
|
||||
clearTimeout(this.goToPlayerTimeout);
|
||||
this.goToPlayerTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
private inputEvent(event: MouseUpEvent) {
|
||||
@@ -420,7 +460,8 @@ export class ClientGameRunner {
|
||||
if (
|
||||
this.gameView.isLand(tile) &&
|
||||
!this.gameView.hasOwner(tile) &&
|
||||
this.gameView.inSpawnPhase()
|
||||
this.gameView.inSpawnPhase() &&
|
||||
!this.gameView.config().isRandomSpawn()
|
||||
) {
|
||||
this.eventBus.emit(new SendSpawnIntentEvent(tile));
|
||||
return;
|
||||
|
||||
@@ -48,6 +48,7 @@ export class HostLobbyModal extends LitElement {
|
||||
@state() private maxTimer: boolean = false;
|
||||
@state() private maxTimerValue: number | undefined = undefined;
|
||||
@state() private instantBuild: boolean = false;
|
||||
@state() private randomSpawn: boolean = false;
|
||||
@state() private compactMap: boolean = false;
|
||||
@state() private lobbyId = "";
|
||||
@state() private copySuccess = false;
|
||||
@@ -390,6 +391,22 @@ export class HostLobbyModal extends LitElement {
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="random-spawn"
|
||||
class="option-card ${this.randomSpawn ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="random-spawn"
|
||||
@change=${this.handleRandomSpawnChange}
|
||||
.checked=${this.randomSpawn}
|
||||
/>
|
||||
<div class="option-card-title">
|
||||
${translateText("host_modal.random_spawn")}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="donate-gold"
|
||||
class="option-card ${this.donateGold ? "selected" : ""}"
|
||||
@@ -668,6 +685,11 @@ export class HostLobbyModal extends LitElement {
|
||||
this.putGameConfig();
|
||||
}
|
||||
|
||||
private handleRandomSpawnChange(e: Event) {
|
||||
this.randomSpawn = Boolean((e.target as HTMLInputElement).checked);
|
||||
this.putGameConfig();
|
||||
}
|
||||
|
||||
private handleInfiniteGoldChange(e: Event) {
|
||||
this.infiniteGold = Boolean((e.target as HTMLInputElement).checked);
|
||||
this.putGameConfig();
|
||||
@@ -749,6 +771,7 @@ export class HostLobbyModal extends LitElement {
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
donateTroops: this.donateTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
randomSpawn: this.randomSpawn,
|
||||
gameMode: this.gameMode,
|
||||
disabledUnits: this.disabledUnits,
|
||||
playerTeams: this.teamCount,
|
||||
|
||||
@@ -44,6 +44,7 @@ export class SinglePlayerModal extends LitElement {
|
||||
@state() private maxTimer: boolean = false;
|
||||
@state() private maxTimerValue: number | undefined = undefined;
|
||||
@state() private instantBuild: boolean = false;
|
||||
@state() private randomSpawn: boolean = false;
|
||||
@state() private useRandomMap: boolean = false;
|
||||
@state() private gameMode: GameMode = GameMode.FFA;
|
||||
@state() private teamCount: TeamCountConfig = 2;
|
||||
@@ -293,6 +294,22 @@ export class SinglePlayerModal extends LitElement {
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="singleplayer-modal-random-spawn"
|
||||
class="option-card ${this.randomSpawn ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="singleplayer-modal-random-spawn"
|
||||
@change=${this.handleRandomSpawnChange}
|
||||
.checked=${this.randomSpawn}
|
||||
/>
|
||||
<div class="option-card-title">
|
||||
${translateText("single_modal.random_spawn")}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="singleplayer-modal-infinite-gold"
|
||||
class="option-card ${this.infiniteGold ? "selected" : ""}"
|
||||
@@ -440,6 +457,10 @@ export class SinglePlayerModal extends LitElement {
|
||||
this.instantBuild = Boolean((e.target as HTMLInputElement).checked);
|
||||
}
|
||||
|
||||
private handleRandomSpawnChange(e: Event) {
|
||||
this.randomSpawn = Boolean((e.target as HTMLInputElement).checked);
|
||||
}
|
||||
|
||||
private handleInfiniteGoldChange(e: Event) {
|
||||
this.infiniteGold = Boolean((e.target as HTMLInputElement).checked);
|
||||
}
|
||||
@@ -563,6 +584,7 @@ export class SinglePlayerModal extends LitElement {
|
||||
donateTroops: true,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
randomSpawn: this.randomSpawn,
|
||||
disabledUnits: this.disabledUnits
|
||||
.map((u) => Object.values(UnitType).find((ut) => ut === u))
|
||||
.filter((ut): ut is UnitType => ut !== undefined),
|
||||
|
||||
@@ -40,7 +40,9 @@ export class HeadsUpMessage extends LitElement implements Layer {
|
||||
backdrop-blur-md text-white text-md lg:text-xl p-1 lg:p-2"
|
||||
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
|
||||
>
|
||||
${translateText("heads_up_message.choose_spawn")}
|
||||
${this.game.config().isRandomSpawn()
|
||||
? translateText("heads_up_message.random_spawn")
|
||||
: translateText("heads_up_message.choose_spawn")}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user