This commit is contained in:
Andras Schmelczer 2026-08-22 15:29:33 +01:00
commit b9274a42c7
8 changed files with 480 additions and 457 deletions

View file

@ -129,7 +129,7 @@ const main = async () => {
});
window.onpopstate = () => game.destroy();
for (; ;) {
for (;;) {
show(spinner);
hide(logoutButton, true);
show(landingUI, true, 'flex');

View file

@ -295,7 +295,7 @@ export class TouchListener extends CommandGenerator {
);
};
public update(_deltaTimeInSeconds: number) {
public update() {
if (!this.fireButton.parentElement) {
this.overlay.appendChild(this.fireButton);
}
@ -305,8 +305,9 @@ export class TouchListener extends CommandGenerator {
const character = this.game.gameObjects.player;
if (character) {
this.fireStrengthRing.style.background = `conic-gradient(rgba(255, 255, 255, 0.75) ${character.strengthFraction * 360
}deg, transparent 0deg)`;
this.fireStrengthRing.style.background = `conic-gradient(rgba(255, 255, 255, 0.75) ${
character.strengthFraction * 360
}deg, transparent 0deg)`;
}
}

View file

@ -20,16 +20,6 @@ const isDevelopment =
typeof location !== 'undefined' &&
(location.hostname === 'localhost' || location.hostname === '127.0.0.1');
const servers: Array<string> = isDevelopment
export const servers: Array<string> = isDevelopment
? [`http://${location.hostname}:3000`, ...productionServers]
: productionServers;
export abstract class Configuration {
public static async initialize(): Promise<void> {
// Kept async for call-site compatibility; the server list is static now.
}
public static get servers(): Array<string> {
return servers;
}
}

View file

@ -384,7 +384,7 @@ export class Game extends CommandReceiver {
new RenderCommand(this.renderer, this.overlay, shouldChangeLayout),
);
this.touchListener.update(deltaTime);
this.touchListener.update();
this.tutorial.step(this.gameObjects);
@ -406,7 +406,9 @@ export class Game extends CommandReceiver {
this.handleKeystoneArrow();
this.announcementText.innerHTML = this.lastAnnouncementText;
if (this.announcementText.innerHTML !== this.lastAnnouncementText) {
this.announcementText.innerHTML = this.lastAnnouncementText;
}
}
// Points an off-screen chevron toward the keystone "Heart" planet, tinted by
@ -438,14 +440,7 @@ export class Game extends CommandReceiver {
display.y >= margin &&
display.y <= height - margin;
const control = Math.abs(keystone.ownership - 0.5);
const team =
control < settings.planetControlThreshold
? 'neutral'
: keystone.ownership < 0.5
? 'blue'
: 'red';
this.keystoneArrow.className = 'keystone-arrow ' + team;
this.keystoneArrow.className = 'keystone-arrow ' + keystone.team;
if (onScreen) {
this.keystoneArrow.style.display = 'none';

View file

@ -14,8 +14,11 @@ import {
import { ClientCharacterWorld, PredictablePlanet } from './client-character-world';
import { InputHistory } from './input-history';
const stepMs = 1000 / 200; // match the server's 200 Hz fixed tick
const stepSeconds = 1 / 200;
// The server's fixed tick, read from the same place the server reads it — the
// replay integrates over this dt, so a comment promising they match is not
// enough.
const stepSeconds = settings.targetPhysicsDeltaTimeInSeconds;
const stepMs = stepSeconds * 1000;
// The client's clock: the timestamp of the frame currently being rendered, set
// once per frame by the game loop (requestAnimationFrame hands it to us

View file

@ -1,6 +1,6 @@
import { ServerInformation, serverInformationEndpoint, TransportEvents } from 'shared';
import { io, Socket } from 'socket.io-client';
import { Configuration } from './configuration';
import { servers } from './configuration';
import parser from 'socket.io-msgpack-parser';
import { SoundHandler, Sounds } from './sound-handler';
@ -69,11 +69,7 @@ export class JoinFormHandler {
private servers: Array<ServerChooserOption> = [];
private async loadServers() {
await Configuration.initialize();
const serverList = Configuration.servers.filter(
(u) => !this.servers.find((s) => s.url === u),
);
const serverList = servers.filter((u) => !this.servers.find((s) => s.url === u));
serverList.map(async (url) => {
const controller = new AbortController();

View file

@ -2,6 +2,7 @@ import { vec2, vec3 } from 'gl-matrix';
import { CircleLight, Renderer } from 'sdf-2d';
import {
characterCenter,
Circle,
Id,
CharacterBase,
@ -10,6 +11,7 @@ import {
clamp,
clamp01,
mix,
strengthToCharge,
CommandExecutors,
UpdatePropertyCommand,
} from 'shared';
@ -123,9 +125,7 @@ export class CharacterView extends CharacterBase {
}
public get bodyCenter(): vec2 {
const center = vec2.add(vec2.create(), this.head!.center, this.leftFoot!.center);
vec2.add(center, center, this.rightFoot!.center);
return vec2.scale(center, center, 1 / 3);
return characterCenter(this.head!, this.leftFoot!, this.rightFoot!);
}
public get facingDirection(): vec2 {
@ -271,10 +271,7 @@ export class CharacterView extends CharacterBase {
}
public onShoot(strength: number) {
const q = clamp01(
(strength - settings.chargeShotStrengthMin) /
(settings.chargeShotStrengthMax - settings.chargeShotStrengthMin),
);
const q = strengthToCharge(strength);
SoundHandler.play(Sounds.shoot, mix(0.55, 1, q), mix(1.15, 0.8, q));
this.muzzleFlashIntensity = mix(0.35, 1, q);
}
@ -332,16 +329,11 @@ export class CharacterView extends CharacterBase {
}
private calculateTextPosition(): vec2 {
const footAverage = vec2.add(
return vec2.scaleAndAdd(
vec2.create(),
this.leftFoot!.center,
this.rightFoot!.center,
this.head!.center,
this.facingDirection,
this.head!.radius + 80,
);
vec2.scale(footAverage, footAverage, 0.5);
const headFeetDelta = vec2.subtract(footAverage, this.head!.center, footAverage);
vec2.normalize(headFeetDelta, headFeetDelta);
const textOffset = vec2.scale(headFeetDelta, headFeetDelta, this.head!.radius + 80);
return vec2.add(textOffset, this.head!.center, textOffset);
}
}

View file

@ -69,7 +69,6 @@ export class PlanetView extends PlanetBase {
this.ownershipProgress.classList.toggle('contested', contested);
}
private renderedRotation = 0;
private rotationSpeed = 0;
// Newest streamed rotation VALUE — the server-current angle at the latest
// snapshot — as opposed to renderedRotation, which the interpolator holds
@ -87,8 +86,7 @@ export class PlanetView extends PlanetBase {
}
private step({ deltaTimeInSeconds }: StepCommand): void {
this.renderedRotation = this.rotationInterpolator.getValue(deltaTimeInSeconds);
this.shape.rotation = this.renderedRotation;
this.shape.rotation = this.rotationInterpolator.getValue(deltaTimeInSeconds);
this.shape.colorMixQ = this.ownership;
if (this.flareIntensity > 0) {
@ -167,7 +165,14 @@ export class PlanetView extends PlanetBase {
const screenPosition = renderer.worldToDisplayCoordinates(this.center);
this.ownershipProgress.style.transform = `translateX(${screenPosition.x}px) translateY(${screenPosition.y}px) translateX(-50%) translateY(-50%)`;
this.ownershipProgress.style.background = this.getGradient();
// Re-parsing a conic-gradient for every planet on screen every layout
// frame is not free, and ownership moves far more slowly than the ring
// can show.
const gradient = this.getGradient();
if (gradient !== this.lastGradient) {
this.lastGradient = gradient;
this.ownershipProgress.style.background = gradient;
}
if (this.lastGeneratedPoint !== undefined) {
const element = document.createElement('div');
@ -192,6 +197,7 @@ export class PlanetView extends PlanetBase {
}
}
private lastGradient?: string;
private getGradient(): string {
const sideBlue = this.ownership < 0.5;
// Keep the ring neutral through the same dead-band that gates scoring