Skip to content

Commit

Permalink
adding players to tab hud
Browse files Browse the repository at this point in the history
  • Loading branch information
webdevcody committed Jan 24, 2025
1 parent 6bbc6ec commit 8c42442
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 1 deletion.
6 changes: 6 additions & 0 deletions packages/game-client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ export class GameClient {
onToggleInstructions: () => {
this.hud.toggleInstructions();
},
onShowPlayerList: () => {
this.hud.setShowPlayerList(true);
},
onHidePlayerList: () => {
this.hud.setShowPlayerList(false);
},
onDown: (inputs: Input) => {
if (this.craftingTable.isVisible()) {
this.craftingTable.onDown();
Expand Down
14 changes: 14 additions & 0 deletions packages/game-client/src/entities/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export class PlayerClient extends ClientEntity implements IClientEntity, Rendera
private previousHealth: number | undefined;
private damageFlashUntil: number = 0;
private skin: SkinType = SKIN_TYPES.DEFAULT;
private kills: number = 0;
private ping: number = 0;

private input: Input = {
facing: Direction.Right,
Expand All @@ -57,6 +59,8 @@ export class PlayerClient extends ClientEntity implements IClientEntity, Rendera
this.activeItem = data.activeItem;
this.input = data.input;
this.skin = data.skin || SKIN_TYPES.DEFAULT;
this.kills = data.kills || 0;
this.ping = data.ping || 0;
}

private getPlayerAssetKey(): string {
Expand Down Expand Up @@ -257,12 +261,22 @@ export class PlayerClient extends ClientEntity implements IClientEntity, Rendera
ctx.drawImage(image, renderPosition.x + 2, renderPosition.y);
}

public getKills(): number {
return this.kills;
}

public getPing(): number {
return this.ping;
}

deserialize(data: RawEntity): void {
super.deserialize(data);
this.inventory = data.inventory;
this.isCrafting = data.isCrafting;
this.activeItem = data.activeItem;
this.input = data.input;
this.skin = data.skin || SKIN_TYPES.DEFAULT;
this.kills = data.kills || 0;
this.ping = data.ping || 0;
}
}
10 changes: 10 additions & 0 deletions packages/game-client/src/managers/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export interface InputManagerOptions {
onInteract?: (inputs: Input) => void;
onDrop?: (inputs: Input) => void;
onToggleInstructions?: () => void;
onShowPlayerList?: () => void;
onHidePlayerList?: () => void;
}

export class InputManager {
Expand Down Expand Up @@ -69,6 +71,10 @@ export class InputManager {
case "i":
callbacks.onToggleInstructions?.();
break;
case "tab":
e.preventDefault(); // Prevent tab from changing focus
callbacks.onShowPlayerList?.();
break;
}

this.updateDirection();
Expand Down Expand Up @@ -115,6 +121,10 @@ export class InputManager {
case "f":
this.inputs.consume = false;
break;
case "tab":
e.preventDefault(); // Prevent tab from changing focus
callbacks.onHidePlayerList?.();
break;
}

this.updateDirection();
Expand Down
179 changes: 179 additions & 0 deletions packages/game-client/src/ui/hud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,43 @@ const HUD_SETTINGS = {
poor: "rgb(255, 0, 0)", // Red: > 150ms
},
},
PlayerList: {
background: "rgba(20, 20, 20, 0.95)",
color: "#ffffff",
headerBackground: "rgba(40, 40, 40, 0.95)",
font: "24px 'Arial'",
lineHeight: 60,
padding: {
bottom: 32,
left: 40,
right: 40,
top: 32,
},
title: "Players Online",
titleFont: "bold 32px 'Arial'",
rowBackground: {
even: "rgba(40, 40, 40, 0.3)",
odd: "rgba(40, 40, 40, 0.1)",
},
killCount: {
color: "#ffd700",
font: "bold 24px 'Arial'",
},
ping: {
excellent: "rgb(0, 255, 0)", // Green: < 50ms
good: "rgb(255, 255, 0)", // Yellow: 50-100ms
fair: "rgb(255, 165, 0)", // Orange: 100-150ms
poor: "rgb(255, 0, 0)", // Red: > 150ms
font: "20px 'Arial'",
width: 80,
},
borderRadius: 12,
},
};

export class Hud {
private showInstructions: boolean = true;
private showPlayerList: boolean = false;
private gameMessages: { message: string; timestamp: number }[] = [];
private messageTimeout: number = 5000;
private mapManager: MapManager;
Expand Down Expand Up @@ -179,8 +212,14 @@ export class Hud {
if (myPlayer) {
const health = myPlayer.getHealth();
const healthText = `Health: ${health}`;
const kills = myPlayer.getKills();
const killsText = `Kills: ${kills}`;

const healthTextWidth = ctx.measureText(healthText).width;
const killsTextWidth = ctx.measureText(killsText).width;

ctx.fillText(healthText, width - healthTextWidth - margin, margin + gap * 2);
ctx.fillText(killsText, width - killsTextWidth - margin, margin + gap * 5);
}

// Render ping
Expand All @@ -196,6 +235,7 @@ export class Hud {

this.renderControlsList(ctx, gameState);
this.renderGameMessages(ctx);
this.renderPlayerList(ctx, gameState);
}

public addMessage(message: string): void {
Expand Down Expand Up @@ -514,4 +554,143 @@ export class Hud {

ctx.restore();
}

public setShowPlayerList(show: boolean): void {
this.showPlayerList = show;
}

private renderPlayerList(ctx: CanvasRenderingContext2D, gameState: GameState): void {
if (!this.showPlayerList) return;

const settings = HUD_SETTINGS.PlayerList;
const players = gameState.entities.filter((entity) => entity instanceof PlayerClient);

ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);

// Calculate dimensions
ctx.font = settings.font;
let maxWidth = 600; // Increased minimum width to accommodate ping

// Calculate player list width
players.forEach((player) => {
const idText = player.getId();
const killText = `${player.getKills()}`;
const metrics = ctx.measureText(idText);
const killMetrics = ctx.measureText(killText);
maxWidth = Math.max(maxWidth, metrics.width + killMetrics.width + settings.ping.width + 120); // Extra padding
});

const width = maxWidth + settings.padding.left + settings.padding.right;
const height =
settings.padding.top + settings.padding.bottom + settings.lineHeight * (players.length + 1);

// Center the overlay
const x = (ctx.canvas.width - width) / 2;
const y = (ctx.canvas.height - height) / 2;

// Draw main background with rounded corners
ctx.fillStyle = settings.background;
this.roundRect(ctx, x, y, width, height, settings.borderRadius);

// Draw header background
ctx.fillStyle = settings.headerBackground;
this.roundRect(
ctx,
x,
y,
width,
settings.lineHeight + settings.padding.top,
settings.borderRadius,
true,
false
);

// Draw title
ctx.fillStyle = settings.color;
ctx.font = settings.titleFont;
ctx.textBaseline = "middle";
ctx.fillText(
settings.title,
x + settings.padding.left,
y + (settings.lineHeight + settings.padding.top) / 2
);

// Draw player list
ctx.textBaseline = "middle";
players.forEach((player, index) => {
const rowY = y + settings.lineHeight * (index + 1) + settings.padding.top;

// Draw row background
ctx.fillStyle = index % 2 === 0 ? settings.rowBackground.even : settings.rowBackground.odd;
ctx.fillRect(x, rowY, width, settings.lineHeight);

// Draw player ID
ctx.font = settings.font;
ctx.fillStyle = settings.color;
ctx.fillText(player.getId(), x + settings.padding.left, rowY + settings.lineHeight / 2);

// Draw ping with color based on value
const ping = player.getPing();
const pingText = `${ping}ms`;
ctx.font = settings.ping.font;
ctx.fillStyle = this.getPingColor(ping);
const pingX = x + width - settings.padding.right - settings.ping.width;
ctx.fillText(pingText, pingX, rowY + settings.lineHeight / 2);

// Draw kill count with custom styling
const killText = `${player.getKills()} kills`;
ctx.font = settings.killCount.font;
ctx.fillStyle = settings.killCount.color;
const killMetrics = ctx.measureText(killText);
ctx.fillText(
killText,
pingX - killMetrics.width - 40, // Position kills before ping
rowY + settings.lineHeight / 2
);
});

ctx.restore();
}

// Helper method for drawing rounded rectangles
private roundRect(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
width: number,
height: number,
radius: number,
topRounded: boolean = true,
bottomRounded: boolean = true
): void {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
if (topRounded) {
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
} else {
ctx.lineTo(x + width, y);
}
ctx.lineTo(x + width, y + height - radius);
if (bottomRounded) {
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
} else {
ctx.lineTo(x + width, y + height);
}
ctx.lineTo(x + radius, y + height);
if (bottomRounded) {
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
} else {
ctx.lineTo(x, y + height);
}
ctx.lineTo(x, y + radius);
if (topRounded) {
ctx.quadraticCurveTo(x, y, x + radius, y);
} else {
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fill();
}
}
20 changes: 20 additions & 0 deletions packages/game-server/src/entities/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export class Player extends Entity {
private broadcaster: Broadcaster;
private lastWeaponType: ItemType | null = null;
private skin: SkinType = SKIN_TYPES.DEFAULT;
private kills: number = 0;
private ping: number = 0;

constructor(gameManagers: IGameManagers) {
super(gameManagers, Entities.PLAYER);
Expand Down Expand Up @@ -194,6 +196,8 @@ export class Player extends Entity {
isCrafting: this.isCrafting,
input: this.input,
skin: this.skin,
kills: this.kills,
ping: this.ping,
};
}

Expand Down Expand Up @@ -464,4 +468,20 @@ export class Player extends Entity {
getSkin(): SkinType {
return this.skin;
}

incrementKills() {
this.kills++;
}

getKills(): number {
return this.kills;
}

setPing(ping: number): void {
this.ping = ping;
}

getPing(): number {
return this.ping;
}
}
19 changes: 19 additions & 0 deletions packages/game-server/src/entities/projectiles/bullet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { IEntity } from "@/entities/types";
import { RawEntity } from "@/types/entity";
import Vector2 from "@/util/vector2";
import { Line, Rectangle, Circle } from "@/util/shape";
import { Player } from "@/entities/player";

const MAX_TRAVEL_DISTANCE = 400;
export const BULLET_SPEED = 100;
Expand All @@ -23,6 +24,7 @@ export class Bullet extends Entity {
private traveledDistance: number = 0;
private static readonly BULLET_SPEED = 500;
private lastPosition: Vector2;
private shooterId: string = "";

constructor(gameManagers: IGameManagers) {
super(gameManagers, Entities.BULLET);
Expand All @@ -37,6 +39,14 @@ export class Bullet extends Entity {
this.lastPosition = this.getPosition();
}

setShooterId(id: string) {
this.shooterId = id;
}

getShooterId(): string {
return this.shooterId;
}

setDirection(direction: Direction) {
const normalized = normalizeDirection(direction);
this.getExt(Movable).setVelocity(
Expand Down Expand Up @@ -136,7 +146,16 @@ export class Bullet extends Entity {
if (bulletPath.intersects(hitbox)) {
this.getEntityManager().markEntityForRemoval(this);
const destructible = enemy.getExt(Destructible);
const wasAlive = !destructible.isDead();
destructible.damage(1);

// If the enemy died from this hit, increment the shooter's kill count
if (wasAlive && destructible.isDead()) {
const shooter = this.getEntityManager().getEntityById(this.shooterId);
if (shooter instanceof Player) {
shooter.incrementKills();
}
}
return;
}
}
Expand Down
Loading

0 comments on commit 8c42442

Please sign in to comment.