Move player info panel to top of the screen & simplify (#3087)

related to #2260

## Description:

* Moves the player info panel from the right to the top of the screen
* Disable the header ad for now because it would cover up the player
info, we'll find a better place for it in the future
* Remove the collapsable button/functionality. It's hard to even click
the button because the panel disappears when you move away from a
player, and I think the info is too valuable to ever need to be
collapsed.
* Removed the "land" and "irradiated land" since it didn't add much
value
* Remove all alt text & translation, you can't hover over the player
overlay so it's irrelevant.
* put troop info inside the troop bar to reduce amount of text


<img width="479" height="88" alt="Screenshot 2026-02-01 at 8 57 33 PM"
src="https://github.com/user-attachments/assets/3b72eb16-2efa-4c00-a4d0-5e085548fa78"
/>

<img width="438" height="136" alt="Screenshot 2026-02-01 at 8 58 06 PM"
src="https://github.com/user-attachments/assets/285bb2c9-6deb-4ee8-bcc8-743cccd6b77e"
/>

## 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:

evan
This commit is contained in:
Evan
2026-02-02 09:09:52 -08:00
committed by GitHub
parent 6b80337aa9
commit 7d3ec0fcb8
6 changed files with 125 additions and 223 deletions
+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 125" xmlns:v="https://vecta.io/nano"><path d="M86.946 70.119l-2.417-15.098c-.286-1.419-1.511-2.495-2.982-2.495-.899 0-1.687.385-2.268.997L68.104 63.997c-.176.156-.352.338-.515.56-1.049 1.433-.749 3.465.684 4.527.273.215.573.345.866.462l2.808.931c-2.697 7.641-9.313 13.45-17.417 15.059l.014-45.638h5.992c1.889 0 3.412-1.538 3.412-3.426 0-1.875-1.523-3.413-3.412-3.413h-5.992v-6.78c3.979-1.687 6.721-5.634 6.721-10.206C61.264 9.95 56.314 5 50.192 5c-6.097 0-11.073 4.95-11.073 11.073 0 4.578 2.847 8.52 6.8 10.206l.007 6.78h-6.468c-1.882 0-3.413 1.538-3.413 3.413 0 1.889 1.531 3.426 3.413 3.426h6.468l.021 45.71c-8.292-1.485-15.15-7.354-17.926-15.131l2.827-.931c.299-.117.6-.247.879-.456 1.427-1.067 1.753-3.1.671-4.533-.155-.222-.313-.404-.508-.554L20.706 53.524c-.553-.612-1.361-.997-2.246-.997-1.486 0-2.743 1.075-3.009 2.495L13.027 70.12c-.053.241-.064.508-.064.762 0 1.791 1.446 3.236 3.229 3.236.339 0 .658-.032.959-.137l2.624-.853C23.956 85.816 35.901 95 49.997 95s26.053-9.184 30.221-21.891l2.619.872c.299.105.618.137.963.137 1.785 0 3.237-1.446 3.237-3.236 0-.255-.025-.522-.091-.763M45.659 16.073c0-2.521 2.044-4.553 4.533-4.553 2.527 0 4.552 2.032 4.552 4.553 0 2.514-2.024 4.559-4.552 4.559-2.489 0-4.533-2.046-4.533-4.559"/><path d="M46.821306 94.770829c-9.672125-.972647-18.52814-6.513856-23.771656-14.873922-.868866-1.385288-2.385574-4.487112-2.870545-5.870561l-.343618-.96102c-.0095-.01668-.705224.192731-1.545948.46535s-1.804312.494255-2.141309.492526c-1.598809-.0082-3.122304-1.547171-3.115278-3.146913.000864-.196879.574575-3.918388 1.274912-8.270022 1.419295-8.818969 1.428076-8.850837 2.627499-9.536632.834583-.477189 1.483957-.563323 2.337029-.309989.644845.191499 1.336272.793564 6.866372 5.978939 4.549564 4.26596 6.233135 5.932201 6.471488 6.40487.603214 1.196211.325525 2.693184-.677921 3.654548-.288018.275937-1.002559.607386-2.183926 1.013042-2.007091.68919-1.921853.529349-1.189152 2.229934 2.313522 5.369652 7.064022 9.988932 12.577448 12.230032 1.263767.513697 3.384288 1.159414 4.275471 1.30192l.550027.08795V62.733077 39.805269H42.37799c-3.958889 0-4.236614-.04887-5.126792-.902159-.76147-.729916-1.085823-1.473414-1.081006-2.477941.0071-1.483668.947583-2.699422 2.43431-3.146861.49772-.149791 1.545965-.202707 4.01562-.202707h3.342077v-3.423483-3.423485l-1.109679-.604511c-.793899-.432485-1.482722-.979822-2.420583-1.923389-1.146297-1.15327-1.401076-1.505996-2.029023-2.809061-.858345-1.781165-1.170382-3.086543-1.170382-4.896174 0-6.4184795 5.839733-11.6186738 12.164919-10.8326779 2.656895.3301574 4.668838 1.3296881 6.615711 3.286675 1.070143 1.0757025 1.339007 1.4513238 1.957431 2.7346549.865772 1.796619 1.169155 3.085051 1.169155 4.965255 0 4.023175-2.191435 7.679165-5.755536 9.602011l-.969271.522925-.0024 3.389446-.0024 3.389446 3.472222.04702c3.946037.05344 4.168331.108713 5.177124 1.287261 1.316051 1.53751.926562 3.821223-.837885 4.912821l-.700444.433339-3.555509.04279-3.555508.0428v22.902508l.107388 22.902148c.332248-.0011 2.935079-.780331 4.075018-1.219944 5.752163-2.218302 10.721311-7.144396 13.059805-12.946652.204466-.507318.322918-.966456.263226-1.020307s-.65621-.274921-1.325597-.491268c-1.73484-.560706-2.51001-1.013168-2.998016-1.749923-.680235-1.026966-.727607-2.0483-.14813-3.19361.309132-.610985 12.085183-11.683542 12.854449-12.08653 1.199252-.628243 2.81487-.181005 3.651625 1.010847.392869.559594.481176 1.003292 1.695561 8.519473.875547 5.419011 1.264717 8.190005 1.229335 8.753186-.06941 1.104804-.613524 1.897274-1.670778 2.433394-1.027413.520988-1.635513.502208-3.442467-.106313-.816705-.275039-1.512424-.472563-1.546044-.438944s-.379938.854657-.769596 1.824527c-2.13365 5.310705-5.61865 9.924534-10.123033 13.401993-6.338429 4.893379-14.56767 7.254819-22.501513 6.456977zm4.86827-74.330261c1.140586-.346728 2.230937-1.344337 2.770928-2.535241.21803-.480845.29704-.95557.301498-1.811516.0055-1.049248-.04177-1.253646-.495106-2.142377-1.123467-2.202486-3.642798-3.073429-5.980881-2.067613-.987511.424816-2.068224 1.59611-2.409954 2.611949-1.248434 3.711132 2.053637 7.087771 5.813515 5.944798z" fill="#fff"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" xmlns:v="https://vecta.io/nano"><path d="M86.946 70.119l-2.417-15.098c-.286-1.419-1.511-2.495-2.982-2.495-.899 0-1.687.385-2.268.997L68.104 63.997c-.176.156-.352.338-.515.56-1.049 1.433-.749 3.465.684 4.527.273.215.573.345.866.462l2.808.931c-2.697 7.641-9.313 13.45-17.417 15.059l.014-45.638h5.992c1.889 0 3.412-1.538 3.412-3.426 0-1.875-1.523-3.413-3.412-3.413h-5.992v-6.78c3.979-1.687 6.721-5.634 6.721-10.206C61.264 9.95 56.314 5 50.192 5c-6.097 0-11.073 4.95-11.073 11.073 0 4.578 2.847 8.52 6.8 10.206l.007 6.78h-6.468c-1.882 0-3.413 1.538-3.413 3.413 0 1.889 1.531 3.426 3.413 3.426h6.468l.021 45.71c-8.292-1.485-15.15-7.354-17.926-15.131l2.827-.931c.299-.117.6-.247.879-.456 1.427-1.067 1.753-3.1.671-4.533-.155-.222-.313-.404-.508-.554L20.706 53.524c-.553-.612-1.361-.997-2.246-.997-1.486 0-2.743 1.075-3.009 2.495L13.027 70.12c-.053.241-.064.508-.064.762 0 1.791 1.446 3.236 3.229 3.236.339 0 .658-.032.959-.137l2.624-.853C23.956 85.816 35.901 95 49.997 95s26.053-9.184 30.221-21.891l2.619.872c.299.105.618.137.963.137 1.785 0 3.237-1.446 3.237-3.236 0-.255-.025-.522-.091-.763M45.659 16.073c0-2.521 2.044-4.553 4.533-4.553 2.527 0 4.552 2.032 4.552 4.553 0 2.514-2.024 4.559-4.552 4.559-2.489 0-4.533-2.046-4.533-4.559"/><path d="M46.821306 94.770829c-9.672125-.972647-18.52814-6.513856-23.771656-14.873922-.868866-1.385288-2.385574-4.487112-2.870545-5.870561l-.343618-.96102c-.0095-.01668-.705224.192731-1.545948.46535s-1.804312.494255-2.141309.492526c-1.598809-.0082-3.122304-1.547171-3.115278-3.146913.000864-.196879.574575-3.918388 1.274912-8.270022 1.419295-8.818969 1.428076-8.850837 2.627499-9.536632.834583-.477189 1.483957-.563323 2.337029-.309989.644845.191499 1.336272.793564 6.866372 5.978939 4.549564 4.26596 6.233135 5.932201 6.471488 6.40487.603214 1.196211.325525 2.693184-.677921 3.654548-.288018.275937-1.002559.607386-2.183926 1.013042-2.007091.68919-1.921853.529349-1.189152 2.229934 2.313522 5.369652 7.064022 9.988932 12.577448 12.230032 1.263767.513697 3.384288 1.159414 4.275471 1.30192l.550027.08795V62.733077 39.805269H42.37799c-3.958889 0-4.236614-.04887-5.126792-.902159-.76147-.729916-1.085823-1.473414-1.081006-2.477941.0071-1.483668.947583-2.699422 2.43431-3.146861.49772-.149791 1.545965-.202707 4.01562-.202707h3.342077v-3.423483-3.423485l-1.109679-.604511c-.793899-.432485-1.482722-.979822-2.420583-1.923389-1.146297-1.15327-1.401076-1.505996-2.029023-2.809061-.858345-1.781165-1.170382-3.086543-1.170382-4.896174 0-6.4184795 5.839733-11.6186738 12.164919-10.8326779 2.656895.3301574 4.668838 1.3296881 6.615711 3.286675 1.070143 1.0757025 1.339007 1.4513238 1.957431 2.7346549.865772 1.796619 1.169155 3.085051 1.169155 4.965255 0 4.023175-2.191435 7.679165-5.755536 9.602011l-.969271.522925-.0024 3.389446-.0024 3.389446 3.472222.04702c3.946037.05344 4.168331.108713 5.177124 1.287261 1.316051 1.53751.926562 3.821223-.837885 4.912821l-.700444.433339-3.555509.04279-3.555508.0428v22.902508l.107388 22.902148c.332248-.0011 2.935079-.780331 4.075018-1.219944 5.752163-2.218302 10.721311-7.144396 13.059805-12.946652.204466-.507318.322918-.966456.263226-1.020307s-.65621-.274921-1.325597-.491268c-1.73484-.560706-2.51001-1.013168-2.998016-1.749923-.680235-1.026966-.727607-2.0483-.14813-3.19361.309132-.610985 12.085183-11.683542 12.854449-12.08653 1.199252-.628243 2.81487-.181005 3.651625 1.010847.392869.559594.481176 1.003292 1.695561 8.519473.875547 5.419011 1.264717 8.190005 1.229335 8.753186-.06941 1.104804-.613524 1.897274-1.670778 2.433394-1.027413.520988-1.635513.502208-3.442467-.106313-.816705-.275039-1.512424-.472563-1.546044-.438944s-.379938.854657-.769596 1.824527c-2.13365 5.310705-5.61865 9.924534-10.123033 13.401993-6.338429 4.893379-14.56767 7.254819-22.501513 6.456977zm4.86827-74.330261c1.140586-.346728 2.230937-1.344337 2.770928-2.535241.21803-.480845.29704-.95557.301498-1.811516.0055-1.049248-.04177-1.253646-.495106-2.142377-1.123467-2.202486-3.642798-3.073429-5.980881-2.067613-.987511.424816-2.068224 1.59611-2.409954 2.611949-1.248434 3.711132 2.053637 7.087771 5.813515 5.944798z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1200pt" height="1200pt" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
<path d="m417.86 237.12v-237.12h-40.664v432.45h78.738v-195.34z"/>
<path d="m708.33 392.05h302.69v69.617l38.762-0.14453-0.046875-5.3555s-0.5-48.523-0.5-68.785c0-113.14-85.332-204.45-189.74-204.45-104.5 0-190.45 93.094-190.45 206.26 0 20.477-0.5 70.215-0.5 70.238l-0.046875 2.0703h39.809z"/>
<path d="m735.59 418.45c-0.26172 2.9062-0.66797 5.7852-0.66797 8.7383 0 68.832 55.809 124.57 124.59 124.57 68.785 0 124.55-55.762 124.55-124.57 0-2.9766-0.45312-5.8555-0.64453-8.7383z"/>
<path d="m1030.2 573.67h-331.57c-96.855 0-169.98 56.047-169.98 154.98v409.93c0 33.191 26.953 60.023 60.094 60.023 33.191 0 60.047-26.883 60.047-60.023l0.046875-367.07h37.738v428.5h355.53v-428.52h37.547l0.19141 367.07c0 33.191 26.953 60.023 60.023 60.023 33.238 0 60.094-26.883 60.094-60.023v-409.95c0.046875-98.926-72.953-154.93-169.76-154.93zm-47.5 207-48.785-25.668-48.762 25.668 9.3086-54.309-39.43-38.43 54.5-7.9297 24.383-49.406 24.355 49.406 54.523 7.9297-39.453 38.43z"/>
<path d="m489.07 1162-0.90625-396.02-30.668 0.070312-0.71094-311.24-78.523 0.19141 0.71484 293.79s-24.262 41.57-100.19 41.57c-69.332 0-225.52-90.809-225.52-90.809l-53.262 131.71s112.19 55.883 173.67 65.43c23 3.5938 47.617 5.1172 71.191 5.4766-13.883 15.453-22.547 35.668-22.547 58.023 0 28.168 13.617 52.953 34.383 68.953l-133.91 67.977 55.07 97.906 136.24-93.93s13.93-9.8555 26.285-3.332c11.117 5.8828 12.617 20.57 12.617 20.57l0.11719 44.047 46.07-0.30859-0.57031 37.191h53.309l-0.023437-37.262zm-180-159.05c-23.43 0-42.406-19-42.406-42.406s18.977-42.406 42.406-42.406c18.023 0 33.332 11.309 39.477 27.168h-43.57v33.477h42.238c-6.832 14.238-21.262 24.168-38.145 24.168z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

-23
View File
@@ -733,29 +733,6 @@
"show_control": "Show Control",
"show_units": "Show Units"
},
"player_info_overlay": {
"type": "Type",
"bot": "Bot",
"nation": "Nation",
"player": "Player",
"team": "Team",
"alliance_timeout": "Alliance ends in",
"troops": "Troops",
"maxtroops": "Max troops",
"a_troops": "Attacking troops",
"gold": "Gold",
"ports": "Ports",
"cities": "Cities",
"factories": "Factories",
"missile_launchers": "Missile launchers",
"sams": "SAMs",
"warships": "Warships",
"health": "Health",
"attitude": "Attitude",
"levels": "Levels",
"wilderness_title": "Wilderness",
"irradiated_wilderness_title": "Irradiated Wilderness"
},
"events_display": {
"retreating": "retreating",
"retaliate": "Retaliate",
+2 -1
View File
@@ -21,7 +21,8 @@ export class InGameHeaderAd extends LitElement implements Layer {
}
init() {
this.showHeaderAd();
// TODO: move ad and re-enable.
// this.showHeaderAd();
}
private showHeaderAd(): void {
+111 -196
View File
@@ -32,6 +32,7 @@ import goldCoinIcon from "/images/GoldCoinIcon.svg?url";
import missileSiloIcon from "/images/MissileSiloIconWhite.svg?url";
import portIcon from "/images/PortIcon.svg?url";
import samLauncherIcon from "/images/SamLauncherIconWhite.svg?url";
import soldierIcon from "/images/SoldierIcon.svg?url";
function euclideanDistWorld(
coord: { x: number; y: number },
@@ -73,12 +74,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
@state()
private unit: UnitView | null = null;
@state()
private isWilderness: boolean = false;
@state()
private isIrradiatedWilderness: boolean = false;
@state()
private _isInfoVisible: boolean = false;
@@ -86,8 +81,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
private lastMouseUpdate = 0;
private showDetails = true;
init() {
this.eventBus.on(MouseMoveEvent, (e: MouseMoveEvent) =>
this.onMouseEvent(e),
@@ -112,8 +105,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
this.setVisible(false);
this.unit = null;
this.player = null;
this.isWilderness = false;
this.isIrradiatedWilderness = false;
}
public maybeShow(x: number, y: number) {
@@ -134,13 +125,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
this.playerProfile = p;
});
this.setVisible(true);
} else if (owner && !owner.isPlayer() && this.game.isLand(tile)) {
if (this.game.hasFallout(tile)) {
this.isIrradiatedWilderness = true;
} else {
this.isWilderness = true;
}
this.setVisible(true);
} else if (!this.game.isLand(tile)) {
const units = this.game
.units(UnitType.Warship, UnitType.TradeShip, UnitType.TransportShip)
@@ -201,28 +185,17 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
}
}
private displayUnitCount(
player: PlayerView,
type: UnitType,
icon: string,
description: string,
) {
private displayUnitCount(player: PlayerView, type: UnitType, icon: string) {
return !this.game.config().isUnitDisabled(type)
? html`<div
class="flex p-1 w-[calc(50%-0.13rem)] border rounded-md border-gray-500
items-center gap-2 text-sm"
class="flex items-center justify-center gap-0.5 lg:gap-1 p-0.5 lg:p-1 border rounded-md border-gray-500 text-[10px] lg:text-xs w-9 lg:w-12 h-6 lg:h-7"
translate="no"
>
<img
src=${icon}
width="20"
height="20"
alt="${translateText(description)}"
class="align-middle"
class="w-3 h-3 lg:w-4 lg:h-4 object-contain shrink-0"
/>
<span class="w-full text-right p-1"
>${player.totalUnitLevels(type)}</span
>
<span>${player.totalUnitLevels(type)}</span>
</div>`
: "";
}
@@ -268,7 +241,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
const myPlayer = this.game.myPlayer();
const isFriendly = myPlayer?.isFriendly(player);
const isAllied = myPlayer?.isAlliedWith(player);
let relationHtml: TemplateResult | null = null;
let allianceHtml: TemplateResult | null = null;
const maxTroops = this.game.config().maxTroops(player);
const attackingTroops = player
.outgoingAttacks()
@@ -276,34 +249,17 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
.reduce((a, b) => a + b, 0);
const totalTroops = player.troops();
if (player.type() === PlayerType.Nation && myPlayer !== null && !isAllied) {
const relation =
this.playerProfile?.relations[myPlayer.smallID()] ?? Relation.Neutral;
const relationClass = this.getRelationClass(relation);
const relationName = this.getRelationName(relation);
relationHtml = html`
<span class="ml-auto mr-0 ${relationClass}">${relationName}</span>
`;
}
if (isAllied) {
const alliance = myPlayer
?.alliances()
.find((alliance) => alliance.other === player.id());
if (alliance !== undefined) {
relationHtml = html` <span
class="flex gap-2 ml-auto mr-0 text-sm font-bold"
allianceHtml = html` <div
class="flex flex-col items-center ml-auto mr-0 text-sm font-bold leading-tight"
>
<img
src=${allianceIcon}
alt=${translateText("player_info_overlay.alliance_timeout")}
width="20"
height="20"
class="align-middle"
/>
<img src=${allianceIcon} width="20" height="20" />
${this.allianceExpirationText(alliance)}
</span>`;
</div>`;
}
}
let playerType = "";
@@ -320,128 +276,83 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
}
return html`
<div class="p-2">
<button
class="items-center text-bold text-sm lg:text-lg font-bold mb-1 inline-flex break-all ${isFriendly
? "text-green-500"
: "text-white"}"
@click=${() => {
this.showDetails = !this.showDetails;
this.requestUpdate?.();
}}
>
${player.cosmetics.flag
? player.cosmetics.flag!.startsWith("!")
? html`<div
class="h-8 mr-1 aspect-3/4 player-flag"
${ref((el) => {
if (el instanceof HTMLElement) {
requestAnimationFrame(() => {
renderPlayerFlag(player.cosmetics.flag!, el);
});
}
})}
></div>`
: html`<img
class="h-8 mr-1 aspect-3/4"
src=${"/flags/" + player.cosmetics.flag! + ".svg"}
/>`
: html``}
<span>${player.name()}</span>
${this.renderPlayerNameIcons(player)}
</button>
<!-- Collapsible section -->
${this.showDetails
? html`
${player.team() !== null
? html`<div class="text-sm">
${translateText("player_info_overlay.team")}:
${player.team()}
</div>`
: ""}
<div class="flex text-sm">${playerType} ${relationHtml}</div>
${player.troops() >= 1
? html`<div class="flex gap-2 text-sm" translate="no">
${translateText("player_info_overlay.troops")}
<span class="ml-auto mr-0 font-bold">
${renderTroops(player.troops())}
</span>
</div>`
: ""}
${maxTroops >= 1
? html`<div class="flex gap-2 text-sm" translate="no">
${translateText("player_info_overlay.maxtroops")}
<span class="ml-auto mr-0 font-bold">
${renderTroops(maxTroops)}
</span>
</div>`
: ""}
${attackingTroops >= 1
? html`<div class="flex gap-2 text-sm" translate="no">
${translateText("player_info_overlay.a_troops")}
<span class="ml-auto mr-0 text-red-400 font-bold">
${renderTroops(attackingTroops)}
</span>
</div>`
: ""}
${this.renderTroopBar(totalTroops, attackingTroops, maxTroops)}
<div
class="flex p-1 mb-1 mt-1 w-full border rounded-md border-yellow-400
font-bold text-yellow-400 text-sm"
translate="no"
>
<img
src=${goldCoinIcon}
alt=${translateText("player_info_overlay.gold")}
width="15"
height="15"
class="align-middle"
/>
<span class="w-full text-center"
>${renderNumber(player.gold())}</span
>
</div>
<div class="flex flex-wrap max-w-3xl gap-1">
${this.displayUnitCount(
player,
UnitType.City,
cityIcon,
"player_info_overlay.cities",
)}
${this.displayUnitCount(
player,
UnitType.Factory,
factoryIcon,
"player_info_overlay.factories",
)}
${this.displayUnitCount(
player,
UnitType.Port,
portIcon,
"player_info_overlay.ports",
)}
${this.displayUnitCount(
player,
UnitType.MissileSilo,
missileSiloIcon,
"player_info_overlay.missile_launchers",
)}
${this.displayUnitCount(
player,
UnitType.SAMLauncher,
samLauncherIcon,
"player_info_overlay.sams",
)}
${this.displayUnitCount(
player,
UnitType.Warship,
warshipIcon,
"player_info_overlay.warships",
)}
</div>
`
: ""}
<div class="flex items-start gap-2 lg:gap-3 p-1.5 lg:p-2">
<!-- Left: Gold & Troop bar -->
<div class="flex flex-col gap-1 shrink-0 w-28">
<div
class="flex items-center justify-center p-1 border rounded-md border-yellow-400 font-bold text-yellow-400 text-xs w-28"
translate="no"
>
<img src=${goldCoinIcon} width="13" height="13" />
<span class="px-0.5">${renderNumber(player.gold())}</span>
</div>
<div class="w-28" translate="no">
${this.renderTroopBar(totalTroops, attackingTroops, maxTroops)}
</div>
</div>
<!-- Right: Player identity + Units below -->
<div class="flex flex-col justify-between self-stretch">
<div
class="flex items-center gap-2 font-bold text-sm lg:text-lg ${isFriendly
? "text-green-500"
: "text-white"}"
>
${player.cosmetics.flag
? player.cosmetics.flag!.startsWith("!")
? html`<div
class="h-6 aspect-3/4 player-flag"
${ref((el) => {
if (el instanceof HTMLElement) {
requestAnimationFrame(() => {
renderPlayerFlag(player.cosmetics.flag!, el);
});
}
})}
></div>`
: html`<img
class="h-6 aspect-3/4"
src=${"/flags/" + player.cosmetics.flag! + ".svg"}
/>`
: html``}
<span>${player.name()}</span>
${player.team() !== null && player.type() !== PlayerType.Bot
? html`<div class="flex flex-col leading-tight">
<span class="text-gray-400 text-xs font-normal"
>${playerType}</span
>
<span class="text-xs font-normal text-gray-400"
>[<span
style="color: ${this.game
.config()
.theme()
.teamColor(player.team()!)
.toHex()}"
>${player.team()}</span
>]</span
>
</div>`
: html`<span class="text-gray-400 text-xs font-normal"
>${playerType}</span
>`}
${this.renderPlayerNameIcons(player)} ${allianceHtml ?? ""}
</div>
<div class="flex gap-0.5 lg:gap-1 items-center mt-1">
${this.displayUnitCount(player, UnitType.City, cityIcon)}
${this.displayUnitCount(player, UnitType.Factory, factoryIcon)}
${this.displayUnitCount(player, UnitType.Port, portIcon)}
${this.displayUnitCount(
player,
UnitType.MissileSilo,
missileSiloIcon,
)}
${this.displayUnitCount(
player,
UnitType.SAMLauncher,
samLauncherIcon,
)}
${this.displayUnitCount(player, UnitType.Warship, warshipIcon)}
</div>
</div>
</div>
`;
}
@@ -463,7 +374,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
return html`
<div
class="w-full mt-2 mb-2 h-5 border border-gray-600 rounded-md bg-gray-900/60 overflow-hidden"
class="w-full mt-1 lg:mt-2 h-5 lg:h-6 border border-gray-600 rounded-md bg-gray-900/60 overflow-hidden relative"
>
<div class="h-full flex">
${greenPercent > 0
@@ -479,6 +390,25 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
></div>`
: ""}
</div>
<div
class="absolute inset-0 flex items-center justify-between px-1.5 text-xs font-bold leading-none pointer-events-none"
translate="no"
>
<span class="text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]"
>${renderTroops(totalTroops)}</span
>
<span class="text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]"
>${renderTroops(maxTroops)}</span
>
</div>
<img
src=${soldierIcon}
alt=""
aria-hidden="true"
width="14"
height="14"
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 brightness-0 invert drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)] pointer-events-none"
/>
</div>
`;
}
@@ -497,18 +427,12 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
<div class="mt-1">
<div class="text-sm opacity-80">${unit.type()}</div>
${unit.hasHealth()
? html`
<div class="text-sm">
${translateText("player_info_overlay.health")}:
${unit.health()}
</div>
`
? html` <div class="text-sm">Health: ${unit.health()}</div> `
: ""}
${unit.type() === UnitType.TransportShip
? html`
<div class="text-sm">
${translateText("player_info_overlay.troops")}:
${renderTroops(unit.troops())}
Troops: ${renderTroops(unit.troops())}
</div>
`
: ""}
@@ -528,21 +452,12 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
return html`
<div
class="block lg:flex fixed top-37.5 right-4 w-full z-50 flex-col max-w-45"
class="fixed top-0 lg:top-[10px] left-0 right-0 sm:left-1/2 sm:right-auto sm:-translate-x-1/2 z-[1001]"
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
<div
class="bg-gray-800/70 backdrop-blur-xs shadow-xs rounded-lg shadow-lg transition-all duration-300 text-white text-lg md:text-base ${containerClasses}"
class="bg-gray-800/70 backdrop-blur-xs shadow-xs lg:rounded-lg shadow-lg transition-all duration-300 text-white text-lg lg:text-base w-full sm:w-auto sm:min-w-[400px] overflow-hidden ${containerClasses}"
>
${this.isWilderness || this.isIrradiatedWilderness
? html`<div class="p-2 font-bold">
${translateText(
this.isIrradiatedWilderness
? "player_info_overlay.irradiated_wilderness_title"
: "player_info_overlay.wilderness_title",
)}
</div>`
: ""}
${this.player !== null ? this.renderPlayerInfo(this.player) : ""}
${this.unit !== null ? this.renderUnitInfo(this.unit) : ""}
</div>
+3 -2
View File
@@ -13,6 +13,7 @@ import { Layer } from "./Layer";
import warshipIcon from "/images/BattleshipIconWhite.svg?url";
import cityIcon from "/images/CityIconWhite.svg?url";
import factoryIcon from "/images/FactoryIconWhite.svg?url";
import goldCoinIcon from "/images/GoldCoinIcon.svg?url";
import mirvIcon from "/images/MIRVIcon.svg?url";
import missileSiloIcon from "/images/MissileSiloIconWhite.svg?url";
import hydrogenBombIcon from "/images/MushroomCloudIconWhite.svg?url";
@@ -256,11 +257,11 @@ export class UnitDisplay extends LitElement implements Layer {
<div class="p-2">
${translateText("build_menu.desc." + structureKey)}
</div>
<div>
<div class="flex items-center justify-center gap-1">
<img src=${goldCoinIcon} width="13" height="13" />
<span class="text-yellow-300"
>${renderNumber(this.cost(unitType))}</span
>
${translateText("player_info_overlay.gold")}
</div>
</div>
`