Add KD stats and joystick deadzone

This commit is contained in:
schmelczerandras 2020-10-23 22:10:39 +02:00
parent c74b2f14a0
commit 1cd8f5fbe6
10 changed files with 123 additions and 44 deletions

View file

@ -95,12 +95,14 @@ export class PlayerCharacterPhysical
constructor(
name: string,
killCount: number,
deathCount: number,
public readonly colorIndex: number,
team: CharacterTeam,
private readonly container: PhysicalContainer,
startPosition: vec2,
) {
super(id(), name, colorIndex, team, settings.playerMaxHealth);
super(id(), name, killCount, deathCount, colorIndex, team, settings.playerMaxHealth);
this.head = new CirclePhysical(
vec2.add(vec2.create(), startPosition, PlayerCharacterPhysical.headOffset),
@ -133,7 +135,13 @@ export class PlayerCharacterPhysical
}
public calculateUpdates(): UpdateObjectMessage {
return new UpdateGameObjectMessage(this, ['head', 'leftFoot', 'rightFoot', 'health']);
return new UpdateGameObjectMessage(this, [
'head',
'leftFoot',
'rightFoot',
'health',
'killCount',
]);
}
public handleMovementAction(c: MoveActionCommand) {
@ -141,11 +149,16 @@ export class PlayerCharacterPhysical
}
public onCollision(other: GameObject) {
if (other instanceof ProjectilePhysical && other.team !== this.team) {
if (
other instanceof ProjectilePhysical &&
other.team !== this.team &&
other.isAlive
) {
other.destroy();
this.health -= other.strength;
if (this.health <= 0) {
this.destroy();
other.originator.killCount++;
}
}
}
@ -167,6 +180,7 @@ export class PlayerCharacterPhysical
strength,
this.team,
velocity,
this,
this.container,
);
this.container.addObject(projectile);

View file

@ -38,6 +38,7 @@ export class ProjectilePhysical
public strength: number,
public team: CharacterTeam,
private velocity: vec2,
public readonly originator: PlayerCharacterPhysical,
readonly container: PhysicalContainer,
) {
super(id(), center, radius, colorIndex, strength);
@ -46,6 +47,10 @@ export class ProjectilePhysical
this.moveOutsideOfObject();
}
public get isAlive(): boolean {
return !this.isDestroyed;
}
private moveOutsideOfObject() {
let wasCollision = true;
const delta = vec2.scale(

View file

@ -37,6 +37,9 @@ export class Player extends CommandReceiver {
private aspectRatio: number = 16 / 9;
private isActive = true;
private sumKills = 0;
private sumDeaths = 0;
private objectsPreviouslyInViewArea: Array<GameObject> = [];
private pingTime?: number;
@ -64,17 +67,20 @@ export class Player extends CommandReceiver {
};
private findEmptyPositionForPlayer(): vec2 {
let possibleCenter = this.players.find((p) => p.team === this.team)?.center;
let possibleCenter = this.players.find(
(p) => p.character?.isAlive && p.team === this.team,
)?.center;
if (!possibleCenter) {
possibleCenter = vec2.create();
}
let rotation = Math.atan2(possibleCenter.y, possibleCenter.x);
let radius = vec2.length(possibleCenter);
let rotation = 0;
let radius = 0;
for (;;) {
const playerPosition = vec2.fromValues(
radius * Math.cos(rotation),
radius * Math.sin(rotation),
radius * Math.cos(rotation) + possibleCenter.x,
radius * Math.sin(rotation) + possibleCenter.y,
);
const playerBoundingCircle = new Circle(
@ -121,6 +127,8 @@ export class Player extends CommandReceiver {
private createCharacter() {
this.character = new PlayerCharacterPhysical(
this.playerInfo.name.slice(0, 20),
this.sumKills,
this.sumDeaths,
this.colorIndex,
this.team,
this.objects,
@ -143,6 +151,9 @@ export class Player extends CommandReceiver {
this.center = this.character?.center;
if (!this.character.isAlive) {
this.sumDeaths++;
this.sumKills = this.character.killCount;
this.socket.emit(
TransportEvents.ServerToPlayer,
serialize(new PlayerDiedCommand(settings.playerDiedTimeout)),

View file

@ -10,7 +10,7 @@
margin: 0;
box-sizing: border-box;
color: white;
font-family: 'Open Sans', sans-serif;
font-family: 'Open Sans', 'Segoe UI Emoji', sans-serif;
&::selection {
color: white;
@ -43,10 +43,6 @@ h1 {
}
}
h2 {
font-size: 4rem;
}
.red {
color: $red;
&::selection {
@ -97,10 +93,6 @@ body {
left: 0;
}
h2 {
margin: $large-padding 0;
}
.player-tag {
transform: translateX(-50%) translateY(-50%) rotate(-15deg);
transition: left 200ms, top 200ms;
@ -108,8 +100,7 @@ body {
&.decla {
color: $bright-decla;
div {
div:first-child {
background-color: $bright-decla;
&:before {
background-color: $bright-decla;
@ -120,7 +111,7 @@ body {
&.red {
color: $bright-red;
div {
div:first-child {
background-color: $bright-red;
&:before {
background-color: $bright-red;
@ -129,7 +120,7 @@ body {
}
}
div {
div:first-child {
position: relative;
height: 5px;
border-radius: 1000px;
@ -143,6 +134,11 @@ body {
border-radius: 1000px;
}
}
div:not(:first-child) {
letter-spacing: 2px;
font-size: 0.8em;
}
}
.ownership {
@ -190,6 +186,17 @@ body {
}
}
.announcement {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
font-size: 3rem;
@include background;
z-index: 1000;
padding: $medium-padding;
border-radius: 16px;
}
.planet-progress {
position: absolute;
top: $small-padding;

View file

@ -73,11 +73,19 @@ export class TouchJoystickListener extends CommandGenerator {
this.joystickButton.style.transform = `translateX(${movement.x}px) translateY(${movement.y}px) translateX(-50%) translateY(-50%)`;
vec2.set(movement, movement.x, -movement.y);
if (vec2.squaredLength(movement) > 0) {
vec2.normalize(movement, movement);
if (length > 10) {
this.sendCommandToSubcribers(
new MoveActionCommand(movement, OptionsHandler.options.relativeMovementEnabled),
new MoveActionCommand(
vec2.normalize(movement, movement),
OptionsHandler.options.relativeMovementEnabled,
),
);
} else {
this.sendCommandToSubcribers(
new MoveActionCommand(
vec2.create(),
OptionsHandler.options.relativeMovementEnabled,
),
);
}
});

View file

@ -41,12 +41,14 @@ export class Game {
private declaPlanetCountElement = document.createElement('div');
private redPlanetCountElement = document.createElement('div');
private neutralPlanetCountElement = document.createElement('div');
private announcementText = document.createElement('h2');
constructor(
private readonly playerDecision: PlayerDecision,
private readonly canvas: HTMLCanvasElement,
private readonly overlay: HTMLElement,
) {
this.announcementText.className = 'announcement';
const progressBar = document.createElement('div');
progressBar.className = 'planet-progress';
overlay.appendChild(progressBar);
@ -75,7 +77,7 @@ export class Game {
if (OptionsHandler.options.vibrationEnabled) {
navigator.vibrate(150);
}
this.overlay.appendChild(this.announcmentText);
this.overlay.appendChild(this.announcementText);
} else if (command instanceof UpdateGameState) {
const all = command.declaCount + command.redCount + command.neutralCount;
this.declaPlanetCountElement.style.width = (command.declaCount / all) * 100 + '%';
@ -84,13 +86,13 @@ export class Game {
this.redPlanetCountElement.style.width = (command.redCount / all) * 100 + '%';
if (command.declaCount > all * 0.5) {
this.overlay.appendChild(this.announcmentText);
this.announcmentText.innerText = 'Decla team won 🎉';
this.overlay.appendChild(this.announcementText);
this.announcementText.innerText = 'Decla team won 🎉';
}
if (command.redCount > all * 0.5) {
this.overlay.appendChild(this.announcmentText);
this.announcmentText.innerText = 'Red team won 🎉';
this.overlay.appendChild(this.announcementText);
this.announcementText.innerText = 'Red team won 🎉';
}
this.arrowElements
@ -233,7 +235,6 @@ export class Game {
this.isActive = false;
}
private announcmentText = document.createElement('h2');
private gameLoop(
renderer: Renderer,
currentTime: DOMHighResTimeStamp,
@ -242,9 +243,9 @@ export class Game {
this.renderer = renderer;
if ((this.deadTimeout -= deltaTime / 1000) > 0) {
this.announcmentText.innerText = `Respawning in ${Math.floor(this.deadTimeout)} `;
this.announcementText.innerText = `Reviving in ${Math.floor(this.deadTimeout)}`;
} else {
this.announcmentText.parentElement?.removeChild(this.announcmentText);
this.announcementText.parentElement?.removeChild(this.announcementText);
}
this.gameObjects.stepObjects(deltaTime);

View file

@ -21,6 +21,9 @@ export class GameObjectContainer extends CommandReceiver {
protected commandExecutors: CommandExecutors = {
[CreatePlayerCommand.type]: (c: CreatePlayerCommand) => {
if (this.camera) {
this.deleteObject(this.camera.id);
}
this.player = c.character as PlayerCharacterView;
this.camera = new Camera(this.game);
this.addObject(this.player);

View file

@ -8,14 +8,17 @@ import { ViewObject } from './view-object';
export class PlayerCharacterView extends PlayerCharacterBase implements ViewObject {
private shape: BlobShape;
private nameElement: HTMLElement;
private healthElement: HTMLElement;
private nameElement: HTMLElement = document.createElement('div');
private statsElement: HTMLElement = document.createElement('div');
private healthElement: HTMLElement = document.createElement('div');
private timeSinceLastNameElementUpdate = 0;
private previousHealth;
constructor(
id: Id,
name: string,
killCount: number,
deathCount: number,
colorIndex: number,
team: CharacterTeam,
health: number,
@ -23,15 +26,25 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
leftFoot?: Circle,
rightFoot?: Circle,
) {
super(id, name, colorIndex, team, health, head, leftFoot, rightFoot);
super(
id,
name,
killCount,
deathCount,
colorIndex,
team,
health,
head,
leftFoot,
rightFoot,
);
this.shape = new BlobShape(colorIndex);
this.previousHealth = this.health;
this.nameElement = document.createElement('div');
this.nameElement.className = 'player-tag ' + this.team;
this.nameElement.innerText = this.name;
this.healthElement = document.createElement('div');
this.nameElement.appendChild(this.healthElement);
this.nameElement.appendChild(this.statsElement);
}
public get position(): vec2 {
@ -41,10 +54,11 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
public step(deltaTimeInMilliseconds: number): void {
this.timeSinceLastNameElementUpdate += deltaTimeInMilliseconds;
this.healthElement.style.width = (50 * this.health) / settings.playerMaxHealth + 'px';
this.statsElement.innerText = this.getStatsText();
if (this.previousHealth > this.health) {
this.previousHealth = this.health;
if (OptionsHandler.options.vibrationEnabled) {
navigator.vibrate(50);
navigator.vibrate(75);
}
}
this.previousHealth = this.health;
@ -72,6 +86,10 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
renderer.addDrawable(this.shape);
}
private getStatsText(): string {
return `${this.killCount}⚔/${this.deathCount}`;
}
private calculateTextPosition(): vec2 {
const footAverage = vec2.add(
vec2.create(),
@ -82,7 +100,7 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
const headFeetDelta = vec2.subtract(footAverage, this.head!.center, footAverage);
vec2.normalize(headFeetDelta, headFeetDelta);
const textOffset = vec2.scale(headFeetDelta, headFeetDelta, this.head!.radius + 60);
const textOffset = vec2.scale(headFeetDelta, headFeetDelta, this.head!.radius + 80);
return vec2.add(textOffset, this.head!.center, textOffset);
}
}

View file

@ -43,6 +43,7 @@
top: 0;
right: $medium-padding + $small-icon + $small-padding;
}
pointer-events: none;
overflow: hidden;
}
@ -61,11 +62,10 @@
opacity: 0;
transition: transform $animation-time, opacity $animation-time;
pointer-events: none;
&.open {
transform: none;
opacity: 1;
pointer-events: inherit;
pointer-events: all;
}
img,

View file

@ -9,6 +9,8 @@ export class PlayerCharacterBase extends CharacterBase {
constructor(
id: Id,
public name: string,
public killCount: number,
public deathCount: number,
colorIndex: number,
team: CharacterTeam,
health: number,
@ -20,7 +22,17 @@ export class PlayerCharacterBase extends CharacterBase {
}
public toArray(): Array<any> {
const { id, name, colorIndex, team, health, head, leftFoot, rightFoot } = this;
return [id, name, colorIndex, team, health, head, leftFoot, rightFoot];
return [
this.id,
this.name,
this.killCount,
this.deathCount,
this.colorIndex,
this.team,
this.health,
this.head,
this.leftFoot,
this.rightFoot,
];
}
}