This commit is contained in:
Andras Schmelczer 2026-06-12 21:46:47 +01:00
parent ec579650d7
commit 1f10b9c750
28 changed files with 134 additions and 220 deletions

View file

@ -2,7 +2,7 @@ import { Command } from 'shared';
export class GeneratePointsCommand extends Command {
public constructor(
public readonly decla: number,
public readonly blue: number,
public readonly red: number,
) {
super();

View file

@ -30,7 +30,7 @@ export class GameServer extends CommandReceiver {
private deltaTimes!: Array<number>;
private deltaTimeCalculator!: DeltaTimeCalculator;
private declaPoints = 0;
private bluePoints = 0;
private redPoints = 0;
private matchPointAnnounced: Partial<Record<CharacterTeam, boolean>> = {};
@ -52,7 +52,7 @@ export class GameServer extends CommandReceiver {
);
this.deltaTimeCalculator = new DeltaTimeCalculator();
this.deltaTimes = [];
this.declaPoints = 0;
this.bluePoints = 0;
this.redPoints = 0;
this.matchPointAnnounced = {};
this.isInEndGame = false;
@ -118,19 +118,19 @@ export class GameServer extends CommandReceiver {
this.handlePhysics();
}
private addPoints({ decla, red }: GeneratePointsCommand) {
private addPoints({ blue, red }: GeneratePointsCommand) {
if (this.isInEndGame) {
return;
}
this.declaPoints += decla;
this.bluePoints += blue;
this.redPoints += red;
if (this.declaPoints >= this.options.scoreLimit) {
this.endGame(CharacterTeam.decla);
if (this.bluePoints >= this.options.scoreLimit) {
this.endGame(CharacterTeam.blue);
} else if (this.redPoints >= this.options.scoreLimit) {
this.endGame(CharacterTeam.red);
} else {
this.announceMatchPointOnce(CharacterTeam.decla, this.declaPoints);
this.announceMatchPointOnce(CharacterTeam.blue, this.bluePoints);
this.announceMatchPointOnce(CharacterTeam.red, this.redPoints);
}
}
@ -179,7 +179,7 @@ export class GameServer extends CommandReceiver {
if ((this.timeSinceLastPointUpdate += delta) > 0.5) {
this.timeSinceLastPointUpdate = 0;
this.players.queueCommandForEachClient(
new UpdateGameState(this.declaPoints, this.redPoints, this.options.scoreLimit),
new UpdateGameState(this.bluePoints, this.redPoints, this.options.scoreLimit),
);
}
@ -224,7 +224,7 @@ export class GameServer extends CommandReceiver {
}
private get gameProgress(): number {
return (Math.max(this.declaPoints, this.redPoints) / this.options.scoreLimit) * 100;
return (Math.max(this.bluePoints, this.redPoints) / this.options.scoreLimit) * 100;
}
public get serverInfo(): ServerInformation {

View file

@ -151,10 +151,10 @@ export class CharacterPhysical extends CharacterBase implements DynamicPhysical
private getPoints(game: CommandReceiver) {
if (!this.isAlive && !this.hasGeneratedPoints) {
this.hasGeneratedPoints = true;
const decla = this.team === CharacterTeam.decla ? 0 : settings.playerKillPoint;
const blue = this.team === CharacterTeam.blue ? 0 : settings.playerKillPoint;
const red = this.team === CharacterTeam.red ? 0 : settings.playerKillPoint;
game.handleCommand(new GeneratePointsCommand(decla, red));
game.handleCommand(new GeneratePointsCommand(blue, red));
}
}
@ -405,7 +405,7 @@ export class CharacterPhysical extends CharacterBase implements DynamicPhysical
this.projectileStrength = Math.min(
settings.playerMaxStrength,
this.projectileStrength +
settings.playerStrengthRegenerationPerSeconds * deltaTimeInSeconds,
settings.playerStrengthRegenerationPerSeconds * deltaTimeInSeconds,
);
this.regenerateHealth(deltaTimeInSeconds);
@ -443,6 +443,8 @@ export class CharacterPhysical extends CharacterBase implements DynamicPhysical
this.setDirection(vec2.length(sumForce) === 0 ? vec2.fromValues(0, -1) : sumForce);
} else {
this.carryWithRotatingPlanet(deltaTimeInSeconds);
const leftFootGravity = this.currentPlanet!.getForce(this.leftFoot.center);
const rightFootGravity = this.currentPlanet!.getForce(this.rightFoot.center);
@ -493,6 +495,38 @@ export class CharacterPhysical extends CharacterBase implements DynamicPhysical
);
}
// While standing on a planet, ride its spin: rigidly rotate the whole body
// around the planet centre by the same per-tick angle the renderer and the
// collision SDF use, so the player is carried around and turned with the
// surface instead of it sliding frictionlessly underneath. The positional
// change becomes velocity in setPropertyUpdates, so the motion syncs and the
// client extrapolates it smoothly.
private carryWithRotatingPlanet(deltaTimeInSeconds: number) {
const planet = this.currentPlanet;
if (!planet) {
return;
}
// Negative: the rendered/collidable surface turns by R(-rotation) — see
// PlanetPhysical.distance (toLocalFrame) and planet-shape.ts.
const angle = -planet.angularVelocity * deltaTimeInSeconds;
const center = planet.center;
this.head.center = vec2.rotate(vec2.create(), this.head.center, center, angle);
this.leftFoot.center = vec2.rotate(
vec2.create(),
this.leftFoot.center,
center,
angle,
);
this.rightFoot.center = vec2.rotate(
vec2.create(),
this.rightFoot.center,
center,
angle,
);
}
private keepPosture() {
const center = this.center;
this.springMove(

View file

@ -8,6 +8,7 @@ import {
} from 'shared';
import { BoundingBox } from '../physics/bounding-boxes/bounding-box';
import { BoundingBoxBase } from '../physics/bounding-boxes/bounding-box-base';
import { depenetrateCircle } from '../physics/functions/depenetrate-circle';
import { moveCircle } from '../physics/functions/move-circle';
import { PhysicalContainer } from '../physics/containers/physical-container';
import { DynamicPhysical } from '../physics/physicals/dynamic-physical';
@ -100,6 +101,8 @@ export class CirclePhysical extends CommandReceiver implements Circle, DynamicPh
.filter((b) => b.gameObject !== this.gameObject && b.canCollide);
this.radius -= vec2.length(delta);
depenetrateCircle(this, intersecting);
const { normal, hitSurface, hitObject } = moveCircle(this, delta, intersecting);
if (hitSurface) {

View file

@ -79,11 +79,11 @@ export class PlayerContainer {
private getTeamOfNextPlayer(isNpc = false): CharacterTeam {
const players = isNpc ? this.players : this._players;
const declaCount = players.filter((p) => p.team === CharacterTeam.decla).length;
const blueCount = players.filter((p) => p.team === CharacterTeam.blue).length;
const redCount = players.filter((p) => p.team === CharacterTeam.red).length;
if ((declaCount === redCount && Random.getRandom() >= 0.5) || declaCount < redCount) {
return CharacterTeam.decla;
if ((blueCount === redCount && Random.getRandom() >= 0.5) || blueCount < redCount) {
return CharacterTeam.blue;
} else {
return CharacterTeam.red;
}