Add critical hits, victory celebrations, and enhanced match settings

This commit is contained in:
2026-05-23 01:20:04 +09:00
parent a03dc02d01
commit 25137cf26e
12 changed files with 1202 additions and 152 deletions
+79 -14
View File
@@ -1,7 +1,8 @@
import { NICKNAME_LENGTH } from "../constants.js";
import { DEFAULT_SPAWN_PLACEMENT, NICKNAME_LENGTH } from "../constants.js";
const STORAGE_KEYS = {
names: "arena.match.playerNames",
spawnPlacement: "arena.match.spawnPlacement",
teamSize: "arena.match.teamSize",
};
@@ -11,22 +12,42 @@ export function createMatchForm() {
const appNode = document.querySelector("#app");
const statusNode = document.querySelector("#match-status");
const statusTextNodes = document.querySelectorAll("[data-status-text]");
const spawnPlacementInputs = getElements('input[name="spawnPlacement"]');
const teamSizeInput = getElement("#team-size");
const teamSizeOutput = getElement("#team-size-value");
const teamSizeNumberInput = getElement("#team-size-value");
const readMatchConfig = () => ({
names: nicknameValues(namesInput.value),
spawnPlacement: selectedSpawnPlacement(spawnPlacementInputs),
teamSize: Number(teamSizeInput.value),
});
restoreSavedMatchSettings(namesInput, teamSizeInput);
syncTeamSizeOutput(teamSizeInput, teamSizeOutput);
restoreSavedMatchSettings(namesInput, spawnPlacementInputs, teamSizeInput, teamSizeNumberInput);
syncTeamSizeInputs(teamSizeInput, teamSizeNumberInput);
namesInput.addEventListener("input", () => {
saveMatchSettings(namesInput, teamSizeInput);
saveMatchSettings(namesInput, spawnPlacementInputs, teamSizeInput);
});
teamSizeInput.addEventListener("input", () => {
syncTeamSizeOutput(teamSizeInput, teamSizeOutput);
saveMatchSettings(namesInput, teamSizeInput);
syncTeamSizeInputs(teamSizeInput, teamSizeNumberInput);
saveMatchSettings(namesInput, spawnPlacementInputs, teamSizeInput);
});
teamSizeNumberInput.addEventListener("input", () => {
if (syncTeamSizeInputs(teamSizeInput, teamSizeNumberInput, teamSizeNumberInput.value)) {
saveMatchSettings(namesInput, spawnPlacementInputs, teamSizeInput);
}
});
teamSizeNumberInput.addEventListener("change", () => {
syncTeamSizeInputs(
teamSizeInput,
teamSizeNumberInput,
teamSizeNumberInput.value || teamSizeInput.value,
);
saveMatchSettings(namesInput, spawnPlacementInputs, teamSizeInput);
});
spawnPlacementInputs.forEach((input) => {
input.addEventListener("change", () => {
saveMatchSettings(namesInput, spawnPlacementInputs, teamSizeInput);
});
});
return {
@@ -62,6 +83,16 @@ function getElement(selector) {
return element;
}
function getElements(selector) {
const elements = [...document.querySelectorAll(selector)];
if (elements.length === 0) {
throw new Error(`Missing required elements: ${selector}`);
}
return elements;
}
function nicknameValues(value) {
return value
.split(/\r?\n|,/)
@@ -69,11 +100,25 @@ function nicknameValues(value) {
.filter(Boolean);
}
function syncTeamSizeOutput(input, output) {
output.textContent = input.value;
function syncTeamSizeInputs(rangeInput, numberInput, value = rangeInput.value) {
const normalizedTeamSize = normalizeTeamSize(value, rangeInput);
if (!normalizedTeamSize) {
return "";
}
rangeInput.value = normalizedTeamSize;
numberInput.value = normalizedTeamSize;
return normalizedTeamSize;
}
function restoreSavedMatchSettings(namesInput, teamSizeInput) {
function restoreSavedMatchSettings(
namesInput,
spawnPlacementInputs,
teamSizeInput,
teamSizeNumberInput,
) {
const storage = getLocalStorage();
if (!storage) {
@@ -82,6 +127,7 @@ function restoreSavedMatchSettings(namesInput, teamSizeInput) {
try {
const savedNames = storage.getItem(STORAGE_KEYS.names);
const savedSpawnPlacement = storage.getItem(STORAGE_KEYS.spawnPlacement);
const savedTeamSize = storage.getItem(STORAGE_KEYS.teamSize);
if (savedNames !== null) {
@@ -90,15 +136,19 @@ function restoreSavedMatchSettings(namesInput, teamSizeInput) {
const normalizedTeamSize = normalizeTeamSize(savedTeamSize, teamSizeInput);
if (normalizedTeamSize) {
teamSizeInput.value = normalizedTeamSize;
}
syncTeamSizeInputs(
teamSizeInput,
teamSizeNumberInput,
normalizedTeamSize || teamSizeInput.value,
);
setSpawnPlacement(spawnPlacementInputs, savedSpawnPlacement);
} catch {
// Storage may be unavailable in private or restricted browser contexts.
}
}
function saveMatchSettings(namesInput, teamSizeInput) {
function saveMatchSettings(namesInput, spawnPlacementInputs, teamSizeInput) {
const storage = getLocalStorage();
if (!storage) {
@@ -107,12 +157,27 @@ function saveMatchSettings(namesInput, teamSizeInput) {
try {
storage.setItem(STORAGE_KEYS.names, namesInput.value);
storage.setItem(STORAGE_KEYS.spawnPlacement, selectedSpawnPlacement(spawnPlacementInputs));
storage.setItem(STORAGE_KEYS.teamSize, normalizeTeamSize(teamSizeInput.value, teamSizeInput));
} catch {
// Ignore storage failures so the match form remains usable.
}
}
function selectedSpawnPlacement(inputs) {
return inputs.find((input) => input.checked)?.value ?? DEFAULT_SPAWN_PLACEMENT;
}
function setSpawnPlacement(inputs, value) {
const savedInput = inputs.find((input) => input.value === value);
const defaultInput = inputs.find((input) => input.value === DEFAULT_SPAWN_PLACEMENT);
const nextInput = savedInput ?? defaultInput;
if (nextInput) {
nextInput.checked = true;
}
}
function normalizeTeamSize(value, input) {
const min = Number(input.min) || 1;
const max = Number(input.max) || min;