Merge branch 'main' into features/footer

This commit is contained in:
q8gazy
2025-02-10 02:54:37 +03:00
committed by GitHub
17 changed files with 176 additions and 134 deletions
+27 -9
View File
@@ -20,7 +20,7 @@ export class PublicLobby extends LitElement {
this.fetchAndUpdateLobbies();
this.lobbiesInterval = window.setInterval(
() => this.fetchAndUpdateLobbies(),
1000,
1000
);
}
@@ -60,6 +60,11 @@ export class PublicLobby extends LitElement {
const lobby = this.lobbies[0];
const timeRemaining = Math.max(0, Math.floor(lobby.msUntilStart / 1000));
// Format time to show minutes and seconds
const minutes = Math.floor(timeRemaining / 60);
const seconds = timeRemaining % 60;
const timeDisplay = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
return html`
<button
@click=${() => this.lobbyClicked(lobby)}
@@ -68,12 +73,25 @@ export class PublicLobby extends LitElement {
: "bg-gradient-to-r from-blue-600 to-blue-500"} text-white font-medium rounded-xl transition-opacity duration-200 hover:opacity-90"
>
<div class="text-lg md:text-2xl font-semibold mb-2">Next Game</div>
<div
class="flex flex-col gap-1 md:gap-2 text-blue-100 text-s md:text-lg"
>
<div>Starts in: ${timeRemaining}s</div>
<div>Players: ${lobby.numClients}</div>
<div>ID: ${lobby.id}</div>
<div class="flex items-center justify-center gap-4">
<div class="flex flex-col items-start">
<div class="text-md font-medium text-blue-100">
${lobby.gameConfig.gameMap}
</div>
</div>
<div class="flex flex-col items-start">
<div class="text-md font-medium text-blue-100">
${lobby.numClients}
${lobby.numClients === 1 ? "Player" : "Players"} waiting
</div>
</div>
<div class="flex items-center">
<div
class="min-w-20 text-sm font-medium px-2 py-1 bg-white/10 rounded-xl text-blue-100 text-center"
>
${timeDisplay}
</div>
</div>
</div>
</button>
`;
@@ -93,7 +111,7 @@ export class PublicLobby extends LitElement {
},
bubbles: true,
composed: true,
}),
})
);
} else {
this.dispatchEvent(
@@ -101,7 +119,7 @@ export class PublicLobby extends LitElement {
detail: { lobby: this.currLobby },
bubbles: true,
composed: true,
}),
})
);
this.currLobby = null;
}
+61 -43
View File
@@ -135,62 +135,80 @@ export class ControlPanel extends LitElement implements Layer {
</div>
</div>
<div class="relative mb-4 lg:mb-4 h-6 lg:h-6">
<div class="relative mb-4 lg:mb-4">
<label class="block text-white mb-1"
>Troops: ${renderTroops(this._troops)} | Workers:
${renderTroops(this._workers)}</label
>
<div
class="absolute h-2 bg-blue-500/60 rounded top-6 transition-all duration-300"
style="width: ${this.currentTroopRatio * 100}%"
></div>
<div
class="absolute w-4 h-4 bg-white border-2 border-blue-500 rounded-full top-5 -ml-2 cursor-pointer hover:scale-110 transition-transform"
style="left: ${this.targetTroopRatio * 100}%"
></div>
<input
type="range"
min="1"
max="100"
.value=${this.targetTroopRatio * 100}
@input=${(e: Event) => {
this.targetTroopRatio =
parseInt((e.target as HTMLInputElement).value) / 100;
this.onTroopChange(this.targetTroopRatio);
}}
class="absolute w-full top-3 m-0 opacity-0 cursor-pointer"
/>
<div class="relative h-8">
<!-- Background track -->
<div
class="absolute left-0 right-0 top-3 h-2 bg-white/20 rounded"
></div>
<!-- Fill track -->
<div
class="absolute left-0 top-3 h-2 bg-blue-500/60 rounded transition-all duration-300"
style="width: ${this.currentTroopRatio * 100}%"
></div>
<!-- Range input - exactly overlaying the visual elements -->
<input
type="range"
min="1"
max="100"
.value=${this.targetTroopRatio * 100}
@input=${(e: Event) => {
this.targetTroopRatio =
parseInt((e.target as HTMLInputElement).value) / 100;
this.onTroopChange(this.targetTroopRatio);
}}
class="absolute left-0 right-0 top-2 m-0 h-4 opacity-0 cursor-pointer"
/>
<!-- Handle -->
<div
class="absolute top-2 w-4 h-4 bg-white border-2 border-blue-500 rounded-full -ml-2 cursor-pointer hover:scale-110 transition-transform pointer-events-none"
style="left: ${this.targetTroopRatio * 100}%"
></div>
</div>
</div>
<div class="relative mb:0 lg:mb-4 h-10 lg:h-12">
<div class="relative mb-0 lg:mb-4">
<label class="block text-white mb-1"
>Attack Ratio: ${(this.attackRatio * 100).toFixed(0)}%</label
>
<div class="absolute w-full h-2 bg-white/20 rounded top-6"></div>
<div
class="absolute h-2 bg-red-500/60 rounded top-6 transition-all duration-300"
style="width: ${this.attackRatio * 100}%"
></div>
<div
class="absolute w-4 h-4 bg-white border-2 border-red-500 rounded-full top-5 -ml-2 cursor-pointer hover:scale-110 transition-transform"
style="left: ${this.attackRatio * 100}%"
></div>
<input
type="range"
min="1"
max="100"
.value=${this.attackRatio * 100}
@input=${(e: Event) => {
this.attackRatio =
parseInt((e.target as HTMLInputElement).value) / 100;
this.onAttackRatioChange(this.attackRatio);
}}
class="absolute w-full top-3 m-0 opacity-0 cursor-pointer"
/>
<div class="relative h-8">
<!-- Background track -->
<div
class="absolute left-0 right-0 top-3 h-2 bg-white/20 rounded"
></div>
<!-- Fill track -->
<div
class="absolute left-0 top-3 h-2 bg-red-500/60 rounded transition-all duration-300"
style="width: ${this.attackRatio * 100}%"
></div>
<!-- Range input - exactly overlaying the visual elements -->
<input
type="range"
min="1"
max="100"
.value=${this.attackRatio * 100}
@input=${(e: Event) => {
this.attackRatio =
parseInt((e.target as HTMLInputElement).value) / 100;
this.onAttackRatioChange(this.attackRatio);
}}
class="absolute left-0 right-0 top-2 m-0 h-4 opacity-0 cursor-pointer"
/>
<!-- Handle -->
<div
class="absolute top-2 w-4 h-4 bg-white border-2 border-red-500 rounded-full -ml-2 cursor-pointer hover:scale-110 transition-transform pointer-events-none"
style="left: ${this.attackRatio * 100}%"
></div>
</div>
</div>
</div>
`;
}
createRenderRoot() {
return this; // Disable shadow DOM to allow Tailwind styles
}
+14 -24
View File
@@ -25,7 +25,7 @@ class RenderInfo {
public lastRenderCalc: number,
public location: Cell,
public fontSize: number,
public element: HTMLElement,
public element: HTMLElement
) {}
}
@@ -49,7 +49,7 @@ export class NameLayer implements Layer {
private game: GameView,
private theme: Theme,
private transformHandler: TransformHandler,
private clientID: ClientID,
private clientID: ClientID
) {
this.traitorIconImage = new Image();
this.traitorIconImage.src = traitorIcon;
@@ -100,13 +100,7 @@ export class NameLayer implements Layer {
if (!this.seenPlayers.has(player)) {
this.seenPlayers.add(player);
this.renders.push(
new RenderInfo(
player,
0,
null,
0,
this.createPlayerElement(player),
),
new RenderInfo(player, 0, null, 0, this.createPlayerElement(player))
);
}
}
@@ -115,11 +109,11 @@ export class NameLayer implements Layer {
public renderLayer(mainContex: CanvasRenderingContext2D) {
const screenPosOld = this.transformHandler.worldToScreenCoordinates(
new Cell(0, 0),
new Cell(0, 0)
);
const screenPos = new Cell(
screenPosOld.x - window.innerWidth / 2,
screenPosOld.y - window.innerHeight / 2,
screenPosOld.y - window.innerHeight / 2
);
this.container.style.transform = `translate(${screenPos.x}px, ${screenPos.y}px) scale(${this.transformHandler.scale})`;
@@ -136,7 +130,7 @@ export class NameLayer implements Layer {
0,
0,
mainContex.canvas.width,
mainContex.canvas.height,
mainContex.canvas.height
);
}
@@ -191,7 +185,7 @@ export class NameLayer implements Layer {
const oldLocation = render.location;
render.location = new Cell(
render.player.nameLocation().x,
render.player.nameLocation().y,
render.player.nameLocation().y
);
// Calculate base size and scale
@@ -230,7 +224,7 @@ export class NameLayer implements Layer {
if (render.player === this.firstPlace) {
if (!existingCrown) {
iconsDiv.appendChild(
this.createIconElement(this.crownIconImage.src, iconSize, "crown"),
this.createIconElement(this.crownIconImage.src, iconSize, "crown")
);
}
} else if (existingCrown) {
@@ -242,11 +236,7 @@ export class NameLayer implements Layer {
if (render.player.isTraitor()) {
if (!existingTraitor) {
iconsDiv.appendChild(
this.createIconElement(
this.traitorIconImage.src,
iconSize,
"traitor",
),
this.createIconElement(this.traitorIconImage.src, iconSize, "traitor")
);
}
} else if (existingTraitor) {
@@ -261,8 +251,8 @@ export class NameLayer implements Layer {
this.createIconElement(
this.allianceIconImage.src,
iconSize,
"alliance",
),
"alliance"
)
);
}
} else if (existingAlliance) {
@@ -277,7 +267,7 @@ export class NameLayer implements Layer {
) {
if (!existingTarget) {
iconsDiv.appendChild(
this.createIconElement(this.targetIconImage.src, iconSize, "target"),
this.createIconElement(this.targetIconImage.src, iconSize, "target")
);
}
} else if (existingTarget) {
@@ -291,7 +281,7 @@ export class NameLayer implements Layer {
.filter(
(emoji) =>
emoji.recipientID == AllPlayers ||
emoji.recipientID == myPlayer?.smallID(),
emoji.recipientID == myPlayer?.smallID()
);
if (emojis.length > 0) {
@@ -324,7 +314,7 @@ export class NameLayer implements Layer {
private createIconElement(
src: string,
size: number,
id: string,
id: string
): HTMLImageElement {
const icon = document.createElement("img");
icon.src = src;
+9 -6
View File
@@ -11,6 +11,7 @@ import {
manhattanDistFN,
TileRef,
} from "../../../core/game/GameMap";
import { GameUpdateType } from "../../../core/game/GameUpdates";
enum Relationship {
Self,
@@ -48,9 +49,9 @@ export class UnitLayer implements Layer {
if (this.myPlayer == null) {
this.myPlayer = this.game.playerByClientID(this.clientID);
}
for (const unit of this.game.units()) {
if (unit.wasUpdated()) this.onUnitEvent(unit);
}
this.game.updatesSinceLastTick()?.[GameUpdateType.Unit]?.forEach((unit) => {
this.onUnitEvent(this.game.unit(unit.id));
});
}
init() {
@@ -79,9 +80,11 @@ export class UnitLayer implements Layer {
this.canvas.width = this.game.width();
this.canvas.height = this.game.height();
for (const unit of this.game.units()) {
this.onUnitEvent(unit);
}
this.game
?.updatesSinceLastTick()
?.[GameUpdateType.Unit]?.forEach((unit) => {
this.onUnitEvent(this.game.unit(unit.id));
});
}
private relationship(unit: UnitView): Relationship {
+21 -17
View File
@@ -74,6 +74,11 @@
gtag("js", new Date());
gtag("config", "AW-16702609763");
</script>
<script
async
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7035513310742290"
crossorigin="anonymous"
></script>
</head>
<body
@@ -81,19 +86,18 @@
>
<!-- Main container with responsive padding -->
<!-- Logo section remains the same -->
<div
class="container mx-auto px-4 sm:px-6 lg:px-8 py-4 sm:py-6 lg:py-8 flex-grow"
>
<img
src="../../resources/images/OpenFrontLogo.svg"
alt="OpenFront.io"
class="pt-6 md:pt-12 h-auto w-3/4 md:w-1/2 lg:w-1/3 mx-auto transform sm:scale-125 md:scale-150 lg:scale-175"
/>
<h3
class="font-sans text-center text-black pb-6 md:pb-12 text-sm sm:text-base lg:text-lg"
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-4 sm:py-6 lg:py-8 flex-grow">
<div class="flex justify-center">
<img
src="../../resources/images/OpenFrontLogo.png"
alt="OpenFront.io"
/>
</div>
<div
class="flex justify-center text-sm font-bold mt-[-10px] pb-6 md:pb-12"
>
(v0.15.0)
</h3>
v0.15.0
</div>
<div
class="max-w-sm sm:max-w-md lg:max-w-lg xl:max-w-xl mx-auto p-2 pb-4"
@@ -161,15 +165,15 @@
class="bottom-0 w-full flex-col-reverse sm:flex-row z-50"
style="position: fixed; pointer-events: none"
>
<div class="" style="pointer-events: auto; max-width: max-content;">
<control-panel></control-panel>
</div>
<div
class="sm:fixed sm:right-0 sm:bottom-0 sm:flex justify-end"
style="pointer-events: auto; max-width: max-content;"
class="w-full sm:w-2/3 sm:fixed sm:right-0 sm:bottom-0 sm:flex justify-end"
style="pointer-events: auto"
>
<events-display></events-display>
</div>
<div class="w-full sm:w-1/3" style="pointer-events: auto">
<control-panel></control-panel>
</div>
</div>
<!-- Footer section -->