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

@ -1,5 +0,0 @@
{
"projects": {
"default": "decla-red
}
}

View file

@ -1,16 +0,0 @@
{
"hosting": {
"public": "dist",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}

View file

@ -1,11 +1,11 @@
{
"name": "decla.red-frontend",
"name": "doppler-frontend",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "decla.red-frontend",
"name": "doppler-frontend",
"version": "0.0.0",
"devDependencies": {
"@plausible-analytics/tracker": "^0.4.5",

View file

@ -1,5 +1,5 @@
{
"name": "decla.red-frontend",
"name": "doppler-frontend",
"version": "0.0.0",
"description": "",
"keywords": [],

View file

@ -9,13 +9,13 @@
/>
<meta name="theme-color" content="#b7455e" />
<meta property="og:url" content="https://decla.red" />
<meta property="og:image" content="https://decla.red/og-image.png" />
<meta property="og:url" content="https://doppler.schmelczer.dev" />
<meta property="og:image" content="https://doppler.schmelczer.dev/og-image.png" />
<meta property="og:image:width" content="2086" />
<meta property="og:image:height" content="940" />
<meta
property="og:image:alt"
content="Dimly lit planets surrounding a text saying 'decla.red'"
content="Dimly lit planets surrounding a text saying 'doppler'"
/>
<meta name="theme-color" content="#b33951" />
<meta
@ -27,7 +27,7 @@
<link rel="icon" type="image/png" sizes="32x32" href="static/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="static/favicon-16x16.png" />
<title>decla.red</title>
<title>doppler</title>
</head>
<body>
@ -37,7 +37,7 @@
<div id="overlay"></div>
<section id="landing-ui">
<h1>decla.<span class="red">red</span></h1>
<h1>doppler</h1>
<form id="join-game-form">
<fieldset class="content">
<legend>Join a game</legend>

View file

@ -41,6 +41,18 @@ h1 {
font-size: 6rem;
text-align: center;
padding-bottom: $medium-padding;
width: fit-content;
margin-inline: auto;
background: linear-gradient(90deg, $bright-blue, $bright-red);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
&::selection {
color: white;
-webkit-text-fill-color: white;
background-color: $accent;
}
@media (max-width: $height-breakpoint) {
font-size: 4.5rem;
@ -51,15 +63,6 @@ h1 {
}
}
.red {
color: $accent;
&::selection {
color: $accent;
background-color: white;
}
}
html,
body,
canvas {
@ -126,14 +129,14 @@ body {
border-radius: 1000px;
transition: transform 200ms;
&.decla {
color: $bright-decla;
&.blue {
color: $bright-blue;
.health {
background-color: $bright-decla;
background-color: $bright-blue;
&:before {
background-color: $bright-decla;
background-color: $bright-blue;
opacity: 0.3;
}
}
@ -211,8 +214,8 @@ body {
font-weight: 700;
animation: falling-point 2s ease-out forwards;
&.decla {
color: $bright-decla;
&.blue {
color: $bright-blue;
}
&.red {
@ -232,8 +235,8 @@ body {
mask-image: url('../static/chevron.svg');
mask-size: contain;
&.decla {
background-color: $bright-decla;
&.blue {
background-color: $bright-blue;
}
&.red {
@ -324,8 +327,8 @@ body {
display: none;
}
.decla {
color: $bright-decla;
.blue {
color: $bright-blue;
}
.red {
@ -374,10 +377,10 @@ body {
transition: width $animation-time;
}
.fill.decla {
.fill.blue {
right: 50%;
background: $bright-decla;
color: $bright-decla;
background: $bright-blue;
color: $bright-blue;
border-radius: 1000px 0 0 1000px;
}
@ -425,7 +428,7 @@ body {
font-size $animation-time;
}
.score.decla {
.score.blue {
right: calc(50% + #{$small-padding});
}
@ -438,10 +441,10 @@ body {
font-size: 1.2rem;
}
.score.decla.leading {
.score.blue.leading {
text-shadow:
0 0 3px rgba(0, 0, 0, 0.95),
0 0 8px $bright-decla;
0 0 8px $bright-blue;
}
.score.red.leading {
@ -471,9 +474,9 @@ body {
border-bottom: 5px solid currentColor;
}
&.decla {
&.blue {
right: calc(50% + #{$small-padding});
color: $bright-decla;
color: $bright-blue;
}
&.red {

View file

@ -1,7 +1,7 @@
import { init as plausibleInit } from '@plausible-analytics/tracker';
const ANALYTICS_AUTO_CAPTURE_PAGEVIEWS = true;
const ANALYTICS_DOMAIN = 'decla.red';
const ANALYTICS_DOMAIN = 'doppler.schmelczer.dev';
const ANALYTICS_ENDPOINT = 'https://stats.schmelczer.dev/status';
const ANALYTICS_LOGGING = process.env.NODE_ENV !== 'production';

View file

@ -1,18 +1,20 @@
/**
* Hardcoded list of game servers the landing page offers to players.
*
* Each entry is the public origin of a dockerized `declared-server` instance.
* Each entry is the public origin of a dockerized `doppler-server` instance.
* The join screen polls `<origin>/state` (see `serverInformationEndpoint`) and
* only shows a server once it responds, so listing an offline origin here is
* harmless. Add or remove origins as you deploy more server containers.
*/
// This origin predates the rebrand; update it once the game server is
// redeployed under a doppler subdomain.
const productionServers: Array<string> = ['https://declared.schmelczer.dev'];
/**
* When the page is served from localhost (i.e. the webpack-dev-server), also
* offer the local backend so a `npm start` server can be joined during
* development. The backend's default port is 3000 (see backend/src/options.ts).
* In production the hostname is `declared.schmelczer.dev`, so this never leaks.
* In production the page is served from its own hostname, so this never leaks.
*/
const isDevelopment =
typeof location !== 'undefined' &&

View file

@ -8,35 +8,35 @@ import { CharacterTeam, UpdateGameState, clamp, settings } from 'shared';
export class Scoreboard {
public readonly element = document.createElement('div');
private readonly declaFill = document.createElement('div');
private readonly blueFill = document.createElement('div');
private readonly redFill = document.createElement('div');
private readonly declaScore = document.createElement('span');
private readonly blueScore = document.createElement('span');
private readonly redScore = document.createElement('span');
private readonly youMarker = document.createElement('div');
constructor() {
this.element.className = 'planet-progress';
this.declaFill.className = 'fill decla';
this.blueFill.className = 'fill blue';
this.redFill.className = 'fill red';
this.declaScore.className = 'score decla';
this.blueScore.className = 'score blue';
this.redScore.className = 'score red';
this.youMarker.className = 'you-marker';
this.youMarker.innerText = 'YOU';
this.element.append(
this.declaFill,
this.blueFill,
this.redFill,
this.declaScore,
this.blueScore,
this.redScore,
this.youMarker,
);
}
public update(
{ declaCount, redCount, limit }: UpdateGameState,
{ blueCount, redCount, limit }: UpdateGameState,
localTeam: CharacterTeam | undefined,
) {
const { scoreboardHalfWidthPercent, scoreboardMinFillPercent } = settings;
@ -46,23 +46,23 @@ export class Scoreboard {
clamp(count / limit, 0, 1) * scoreboardHalfWidthPercent,
);
this.declaFill.style.width = fraction(declaCount) + '%';
this.blueFill.style.width = fraction(blueCount) + '%';
this.redFill.style.width = fraction(redCount) + '%';
const isMatchPoint = (count: number) =>
count / limit >= settings.matchPointScoreRatio;
this.declaFill.classList.toggle('match-point', isMatchPoint(declaCount));
this.blueFill.classList.toggle('match-point', isMatchPoint(blueCount));
this.redFill.classList.toggle('match-point', isMatchPoint(redCount));
this.declaScore.innerText = String(Math.round(declaCount));
this.blueScore.innerText = String(Math.round(blueCount));
this.redScore.innerText = String(Math.round(redCount));
this.declaScore.classList.toggle('leading', declaCount > redCount);
this.redScore.classList.toggle('leading', redCount > declaCount);
this.blueScore.classList.toggle('leading', blueCount > redCount);
this.redScore.classList.toggle('leading', redCount > blueCount);
if (localTeam === CharacterTeam.decla || localTeam === CharacterTeam.red) {
if (localTeam === CharacterTeam.blue || localTeam === CharacterTeam.red) {
this.youMarker.style.display = '';
this.youMarker.classList.toggle('decla', localTeam === CharacterTeam.decla);
this.youMarker.classList.toggle('blue', localTeam === CharacterTeam.blue);
this.youMarker.classList.toggle('red', localTeam === CharacterTeam.red);
} else {
this.youMarker.style.display = 'none';

View file

@ -86,7 +86,7 @@ export class Tutorial extends CommandReceiver {
}
if (standing) {
const drift = standing.ownership - this.standingBaseline;
const progress = player.team === CharacterTeam.decla ? -drift : drift;
const progress = player.team === CharacterTeam.blue ? -drift : drift;
if (progress > captureProgressThreshold) {
this.finish();
}

View file

@ -11,12 +11,12 @@ $breakpoint: 700px;
$height-breakpoint: 500px;
$large-icon: 48px;
$small-icon: 32px;
$bright-decla: #4069a5;
$bright-blue: #4069a5;
$bright-red: #d15652;
$bright-neutral: #ccc;
:root {
--bright-decla: #{$bright-decla};
--bright-blue: #{$bright-blue};
--bright-red: #{$bright-red};
--bright-neutral: #{$bright-neutral};
}