mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 13:10:42 +00:00
feat: stats system to see number of nukes sent by a player in PlayerPanel
This commit is contained in:
@@ -4,7 +4,12 @@ import { EventBus } from "../../../core/EventBus";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { MouseUpEvent } from "../../InputHandler";
|
||||
import { AllPlayers, Player, PlayerActions } from "../../../core/game/Game";
|
||||
import {
|
||||
AllPlayers,
|
||||
Player,
|
||||
PlayerActions,
|
||||
UnitType,
|
||||
} from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { renderNumber, renderTroops } from "../../Utils";
|
||||
import targetIcon from "../../../../resources/images/TargetIconWhite.svg";
|
||||
@@ -128,6 +133,24 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
getTotalNukesSent(): number {
|
||||
const stats = this.actions.interaction?.stats;
|
||||
if (!stats) {
|
||||
return 0;
|
||||
}
|
||||
let sum = 0;
|
||||
const nukes = stats.sentNukes[this.g.myPlayer().id()];
|
||||
if (!nukes) {
|
||||
return 0;
|
||||
}
|
||||
for (const nukeType in nukes) {
|
||||
if (nukeType != UnitType.MIRVWarhead) {
|
||||
sum += nukes[nukeType];
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.isVisible) {
|
||||
return html``;
|
||||
@@ -165,7 +188,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
<!-- Close button -->
|
||||
<button
|
||||
@click=${this.handleClose}
|
||||
class="absolute -top-2 -right-2 w-6 h-6 flex items-center justify-center
|
||||
class="absolute -top-2 -right-2 w-6 h-6 flex items-center justify-center
|
||||
bg-red-500 hover:bg-red-600 text-white rounded-full
|
||||
text-sm font-bold transition-colors"
|
||||
>
|
||||
@@ -177,7 +200,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
<div class="flex items-center gap-1 lg:gap-2">
|
||||
<div
|
||||
class="px-4 h-8 lg:h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 text-opacity-90 text-white
|
||||
bg-opacity-50 bg-gray-700 text-opacity-90 text-white
|
||||
rounded text-sm lg:text-xl w-full"
|
||||
>
|
||||
${other?.name()}
|
||||
@@ -212,6 +235,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Embargo -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="text-white text-opacity-80 text-sm px-2">
|
||||
Embargo against you
|
||||
@@ -221,12 +245,22 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="text-white text-opacity-80 text-sm px-2">
|
||||
Nukes sent by them to you
|
||||
</div>
|
||||
<div class="bg-opacity-50 bg-gray-700 rounded p-2 text-white">
|
||||
${this.getTotalNukesSent()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action buttons -->
|
||||
<div class="flex justify-center gap-2">
|
||||
${canTarget
|
||||
? html`<button
|
||||
@click=${(e) => this.handleTargetClick(e, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
text-white rounded-lg transition-colors"
|
||||
>
|
||||
@@ -237,8 +271,8 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
? html`<button
|
||||
@click=${(e) =>
|
||||
this.handleBreakAllianceClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
text-white rounded-lg transition-colors"
|
||||
>
|
||||
<img
|
||||
@@ -252,8 +286,8 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
? html`<button
|
||||
@click=${(e) =>
|
||||
this.handleAllianceClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
text-white rounded-lg transition-colors"
|
||||
>
|
||||
<img src=${allianceIcon} alt="Alliance" class="w-6 h-6" />
|
||||
@@ -262,8 +296,8 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
${canDonate
|
||||
? html`<button
|
||||
@click=${(e) => this.handleDonateClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
text-white rounded-lg transition-colors"
|
||||
>
|
||||
<img src=${donateIcon} alt="Donate" class="w-6 h-6" />
|
||||
@@ -272,8 +306,8 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
${canSendEmoji
|
||||
? html`<button
|
||||
@click=${(e) => this.handleEmojiClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
|
||||
text-white rounded-lg transition-colors"
|
||||
>
|
||||
<img src=${emojiIcon} alt="Emoji" class="w-6 h-6" />
|
||||
|
||||
@@ -168,6 +168,7 @@ export class GameRunner {
|
||||
canBreakAlliance: player.isAlliedWith(other),
|
||||
canDonate: player.canDonate(other),
|
||||
canEmbargo: !player.hasEmbargoAgainst(other),
|
||||
stats: this.game.stats().getPlayerStats(other.id()),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,14 @@ export class MirvExecution implements Execution {
|
||||
this.pathFinder = PathFinder.Mini(mg, 10_000, true);
|
||||
this.player = mg.player(this.senderID);
|
||||
this.targetPlayer = this.mg.owner(this.dst);
|
||||
|
||||
this.mg
|
||||
.stats()
|
||||
.increaseNukeCount(
|
||||
this.player.id(),
|
||||
this.targetPlayer.id(),
|
||||
UnitType.MIRV,
|
||||
);
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
UnitType,
|
||||
TerraNullius,
|
||||
MessageType,
|
||||
NukeType,
|
||||
} from "../game/Game";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { consolex } from "../Consolex";
|
||||
@@ -22,10 +23,7 @@ export class NukeExecution implements Execution {
|
||||
private random: PseudoRandom;
|
||||
|
||||
constructor(
|
||||
private type:
|
||||
| UnitType.AtomBomb
|
||||
| UnitType.HydrogenBomb
|
||||
| UnitType.MIRVWarhead,
|
||||
private type: NukeType,
|
||||
private senderID: PlayerID,
|
||||
private dst: TileRef,
|
||||
private src?: TileRef,
|
||||
@@ -74,6 +72,14 @@ export class NukeExecution implements Execution {
|
||||
target.id(),
|
||||
);
|
||||
}
|
||||
|
||||
this.mg
|
||||
.stats()
|
||||
.increaseNukeCount(
|
||||
this.senderID,
|
||||
target.id(),
|
||||
this.nuke.type() as NukeType,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.waitTicks > 0) {
|
||||
@@ -157,6 +163,7 @@ export class NukeExecution implements Execution {
|
||||
const prev = attacked.get(mp);
|
||||
attacked.set(mp, prev + 1);
|
||||
}
|
||||
|
||||
if (this.mg.isLand(tile)) {
|
||||
this.mg.setFallout(tile, true);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
PlayerUpdate,
|
||||
UnitUpdate,
|
||||
} from "./GameUpdates";
|
||||
import { PlayerStats, Stats } from "./Stats";
|
||||
|
||||
export type PlayerID = string;
|
||||
export type Tick = number;
|
||||
@@ -79,6 +80,11 @@ export enum UnitType {
|
||||
MIRVWarhead = "MIRV Warhead",
|
||||
Construction = "Construction",
|
||||
}
|
||||
export type NukeType =
|
||||
| UnitType.AtomBomb
|
||||
| UnitType.HydrogenBomb
|
||||
| UnitType.MIRVWarhead
|
||||
| UnitType.MIRV;
|
||||
|
||||
export enum Relation {
|
||||
Hostile = 0,
|
||||
@@ -379,6 +385,8 @@ export interface Game extends GameMap {
|
||||
nations(): Nation[];
|
||||
|
||||
numTilesWithFallout(): number;
|
||||
// Optional as it's not initialized before the end of spawn phase
|
||||
stats(): Stats;
|
||||
}
|
||||
|
||||
export interface PlayerActions {
|
||||
@@ -408,6 +416,7 @@ export interface PlayerInteraction {
|
||||
canTarget: boolean;
|
||||
canDonate: boolean;
|
||||
canEmbargo: boolean;
|
||||
stats: PlayerStats;
|
||||
}
|
||||
|
||||
export interface EmojiMessage {
|
||||
|
||||
@@ -30,7 +30,8 @@ import { UnitImpl } from "./UnitImpl";
|
||||
import { consolex } from "../Consolex";
|
||||
import { GameMap, GameMapImpl, TileRef, TileUpdate } from "./GameMap";
|
||||
import { DefenseGrid } from "./DefensePostGrid";
|
||||
import { simpleHash } from "../Util";
|
||||
import { StatsImpl } from "./StatsImpl";
|
||||
import { Stats } from "./Stats";
|
||||
|
||||
export function createGame(
|
||||
gameMap: GameMap,
|
||||
@@ -67,6 +68,9 @@ export class GameImpl implements Game {
|
||||
private updates: GameUpdates = createGameUpdatesMap();
|
||||
private defenseGrid: DefenseGrid;
|
||||
|
||||
// Not initialized until the game has finished spawning
|
||||
private _stats: StatsImpl = new StatsImpl();
|
||||
|
||||
constructor(
|
||||
private _map: GameMap,
|
||||
private miniGameMap: GameMap,
|
||||
@@ -639,6 +643,9 @@ export class GameImpl implements Game {
|
||||
numTilesWithFallout(): number {
|
||||
return this._map.numTilesWithFallout();
|
||||
}
|
||||
stats(): Stats {
|
||||
return this._stats;
|
||||
}
|
||||
}
|
||||
|
||||
// Or a more dynamic approach that will catch new enum values:
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NukeType, PlayerID } from "./Game";
|
||||
|
||||
export interface PlayerStats {
|
||||
sentNukes: {
|
||||
// target
|
||||
[key: PlayerID]: {
|
||||
[key in NukeType]: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface Stats {
|
||||
increaseNukeCount(sender: PlayerID, target: PlayerID, type: NukeType): void;
|
||||
getPlayerStats(player: PlayerID): PlayerStats;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { NukeType, Player, PlayerID, UnitType } from "./Game";
|
||||
import { PlayerStats, Stats } from "./Stats";
|
||||
|
||||
interface StatsInternalData {
|
||||
// player
|
||||
[key: PlayerID]: PlayerStats;
|
||||
}
|
||||
|
||||
export class StatsImpl implements Stats {
|
||||
data: StatsInternalData = {};
|
||||
|
||||
_createUserData(sender: PlayerID, target: PlayerID): void {
|
||||
if (!this.data[sender]) {
|
||||
this.data[sender] = { sentNukes: {} };
|
||||
}
|
||||
if (!this.data[sender].sentNukes[target]) {
|
||||
this.data[sender].sentNukes[target] = {
|
||||
[UnitType.MIRV]: 0,
|
||||
[UnitType.MIRVWarhead]: 0,
|
||||
[UnitType.AtomBomb]: 0,
|
||||
[UnitType.HydrogenBomb]: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
increaseNukeCount(sender: PlayerID, target: PlayerID, type: NukeType): void {
|
||||
this._createUserData(sender, target);
|
||||
this.data[sender].sentNukes[target][type]++;
|
||||
}
|
||||
|
||||
getPlayerStats(player: PlayerID): PlayerStats {
|
||||
return this.data[player];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user