Chose one of two main-menu backgrounds at launch

This commit is contained in:
2026-09-21 12:57:57 +02:00
parent a6b49a5d5a
commit a33dfe8132
5 changed files with 104 additions and 6 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

+14 -6
View File
@@ -35,9 +35,6 @@ html, body {
align-items: flex-start;
justify-content: flex-start;
padding: 28px 34px;
background:
radial-gradient(circle at 30% 0%, rgba(40, 70, 110, 0.25), transparent 60%),
#05070c;
overflow: hidden;
}
.loading-overlay.hidden { display: none; }
@@ -91,14 +88,25 @@ html, body {
}
#screen-menu.active, #screen-new.active, #screen-join.active, #screen-choose.active { display: flex; }
/* The main menu sits on the game's key art, dimmed so the card stays legible;
the setup, join and choose screens keep the plain gradient above. */
#screen-menu {
/* The main menu sits on one of the game's key-art pieces, dimmed so the card
stays legible; the setup, join and choose screens keep the plain gradient
above. The piece is picked at launch (client/js/menu_background.js) by the
class on <body>, and the same art stays behind the boot log while the world
loads. The default below is also what paints before the script runs. */
#screen-menu,
.loading-overlay {
background:
radial-gradient(circle at 50% 25%, rgba(60, 90, 140, 0.25), transparent 60%),
linear-gradient(180deg, rgba(5, 7, 12, 0.45), rgba(5, 7, 12, 0.85)),
url("../assets/main_menu_background.png") center / cover no-repeat;
}
body.menu-bg-1 #screen-menu,
body.menu-bg-1 .loading-overlay {
background:
radial-gradient(circle at 50% 25%, rgba(60, 90, 140, 0.25), transparent 60%),
linear-gradient(180deg, rgba(5, 7, 12, 0.45), rgba(5, 7, 12, 0.85)),
url("../assets/main_menu_background2.png") center / cover no-repeat;
}
.menu-card {
width: 380px;
+7
View File
@@ -4,6 +4,7 @@
import { Network } from "./net.js";
import { GameScreen } from "./game_screen.js";
import { bootLog } from "./loading.js";
import { chooseMenuBackground } from "./menu_background.js";
import { CIVILISATIONS } from "../../shared/data.js";
const network = new Network();
@@ -40,6 +41,12 @@ function status(id, message) {
$(`#${id}`).text(message);
}
// ---------------------------------------------------------- appearance ----
// Two pieces of key art ship; pick one for this launch. It backs the main menu
// and stays behind the boot log while a world loads.
chooseMenuBackground();
// -------------------------------------------------------------- menu ------
$("#menu-create").on("click", () => showScreen("screen-new"));
+25
View File
@@ -0,0 +1,25 @@
// The main menu's key art. Two pieces ship and one is picked at launch; the
// chosen piece also stays behind the boot log while a world loads. The URL
// itself lives in css/style.css, keyed by the class the choice adds to <body>.
export const MENU_BACKGROUNDS = [
{ class: "menu-bg-0", source: "assets/main_menu_background.png" },
{ class: "menu-bg-1", source: "assets/main_menu_background2.png" },
];
// Picks one of the backgrounds. `random` is injectable so a test can pin it.
export function pickMenuBackground(random = Math.random) {
const index = Math.floor(random() * MENU_BACKGROUNDS.length);
return MENU_BACKGROUNDS[Math.min(index, MENU_BACKGROUNDS.length - 1)];
}
// Swaps the background class on <body>, removing whichever one was set before.
export function applyMenuBackground(choice, body = document.body) {
for (const entry of MENU_BACKGROUNDS) body.classList.remove(entry.class);
body.classList.add(choice.class);
return choice;
}
export function chooseMenuBackground(random = Math.random, body = document.body) {
return applyMenuBackground(pickMenuBackground(random), body);
}
+58
View File
@@ -0,0 +1,58 @@
import { TestCase } from "./framework/test_case.js";
import { setupDom, teardownDom } from "./framework/dom.js";
import {
MENU_BACKGROUNDS,
applyMenuBackground,
pickMenuBackground,
} from "../client/js/menu_background.js";
function fakeBody() {
const classes = new Set();
return {
classList: {
add: (name) => classes.add(name),
remove: (name) => classes.delete(name),
contains: (name) => classes.has(name),
},
};
}
export class MenuBackgroundTest extends TestCase {
test_ships_two_distinct_backgrounds() {
this.assertSize(MENU_BACKGROUNDS, 2);
this.assertNotEqual(MENU_BACKGROUNDS[0].source, MENU_BACKGROUNDS[1].source);
this.assertNotEqual(MENU_BACKGROUNDS[0].class, MENU_BACKGROUNDS[1].class);
}
test_random_pick_covers_both_ends() {
this.assertEqual(pickMenuBackground(() => 0), MENU_BACKGROUNDS[0]);
this.assertEqual(pickMenuBackground(() => 0.499), MENU_BACKGROUNDS[0]);
this.assertEqual(pickMenuBackground(() => 0.5), MENU_BACKGROUNDS[1]);
this.assertEqual(pickMenuBackground(() => 1), MENU_BACKGROUNDS[1]);
}
test_apply_swaps_the_class_on_the_body() {
const body = fakeBody();
applyMenuBackground(MENU_BACKGROUNDS[1], body);
this.assertTrue(body.classList.contains(MENU_BACKGROUNDS[1].class));
this.assertFalse(body.classList.contains(MENU_BACKGROUNDS[0].class));
applyMenuBackground(MENU_BACKGROUNDS[0], body);
this.assertTrue(body.classList.contains(MENU_BACKGROUNDS[0].class));
this.assertFalse(body.classList.contains(MENU_BACKGROUNDS[1].class));
}
async test_app_launch_picks_a_background() {
const env = await setupDom();
try {
await import(`../client/js/app.js?background=${Date.now()}`);
const classes = env.window.document.body.classList;
this.assert(
classes.contains("menu-bg-0") || classes.contains("menu-bg-1"),
"a background class is applied at launch"
);
} finally {
teardownDom(env);
}
}
}