Limit collection targets to 15 lobbies per map

This commit is contained in:
2026-10-08 16:09:12 -04:00
parent dd29b41f95
commit 63e084e38f
5 changed files with 13 additions and 11 deletions
+1 -1
View File
@@ -61,7 +61,7 @@ function buildEntries(input: unknown): BlueprintSelection[] {
const entries = input.map(entry => {
if (!entry || (entry.game !== 't5' && entry.game !== 't6') || typeof entry.mapSlug !== 'string'
|| !zombieMapBySlug(entry.game, entry.mapSlug)) throw new Error('Each selection needs a supported game and map.');
if (!Number.isSafeInteger(entry.count) || entry.count < 1 || entry.count > 20) throw new Error('Choose 1–20 lobbies for each map.');
if (!Number.isSafeInteger(entry.count) || entry.count < 1 || entry.count > 15) throw new Error('Choose 1–15 lobbies for each map.');
const key = `${entry.game}:${entry.mapSlug}`;
if (seen.has(key)) throw new Error('Each game/map can appear once. Increase its lobby count instead.');
seen.add(key);
@@ -16,8 +16,8 @@
let error = $state('');
let total = $derived(rows.reduce((sum, row) => sum + (Number(row.count) || 0), 0));
const duplicate = (row: typeof rows[number]) => rows.some(other => other.key !== row.key && other.game === row.game && other.mapSlug === row.mapSlug);
let valid = $derived(name.trim().length > 0 && rows.length > 0 && total <= 100 && rows.every(row => row.mapSlug && Number.isInteger(row.count) && row.count >= 1 && row.count <= 20 && !duplicate(row)));
let reason = $derived(!name.trim() ? 'Enter a collection name to save.' : total > 100 ? `Reduce the total by ${total - 100} to stay within 100 target lobbies.` : rows.some(duplicate) ? 'Choose each game and map once; increase its lobby count instead.' : !valid ? 'Choose a map and 1–20 target lobbies for every row.' : '');
let valid = $derived(name.trim().length > 0 && rows.length > 0 && total <= 100 && rows.every(row => row.mapSlug && Number.isInteger(row.count) && row.count >= 1 && row.count <= 15 && !duplicate(row)));
let reason = $derived(!name.trim() ? 'Enter a collection name to save.' : total > 100 ? `Reduce the total by ${total - 100} to stay within 100 target lobbies.` : rows.some(duplicate) ? 'Choose each game and map once; increase its lobby count instead.' : !valid ? 'Choose a map and 1–15 target lobbies for every row.' : '');
let canAdd = $derived((['t5', 't6'] as const).some(game => catalogs[game].some(map => !rows.some(row => row.game === game && row.mapSlug === map.slug))));
let wasOpen = false;
let session = 0;
@@ -83,7 +83,7 @@
<span class="number">{String(index + 1).padStart(2, '0')}</span>
<label>Game<select aria-label={`Game ${index + 1}`} bind:value={row.game} disabled={busy} onchange={event => row.mapSlug = catalogs[event.currentTarget.value as ZombieGame][0]?.slug ?? ''}><option value="t5">Black Ops I</option><option value="t6">Black Ops II</option></select></label>
<label>Map<select aria-label={`Map ${index + 1}`} aria-invalid={duplicate(row) || undefined} aria-describedby={duplicate(row) ? `map-error-${row.key}` : undefined} bind:value={row.mapSlug} disabled={busy}>{#each catalogs[row.game] as map}<option value={map.slug} disabled={rows.some(other => other.key !== row.key && other.game === row.game && other.mapSlug === map.slug)}>{map.friendly}</option>{/each}</select>{#if duplicate(row)}<span class="error" id={`map-error-${row.key}`}>Already selected. Choose another map.</span>{/if}</label>
<label>Target lobbies<input aria-label={`Lobbies ${index + 1}`} aria-invalid={!(Number.isInteger(row.count) && row.count >= 1 && row.count <= 20) || undefined} aria-describedby={`count-hint-${row.key}`} type="number" min="1" max="20" step="1" bind:value={row.count} required disabled={busy} /><span id={`count-hint-${row.key}`}>1–20</span></label>
<label>Target lobbies<input aria-label={`Lobbies ${index + 1}`} aria-invalid={!(Number.isInteger(row.count) && row.count >= 1 && row.count <= 15) || undefined} aria-describedby={`count-hint-${row.key}`} type="number" min="1" max="15" step="1" bind:value={row.count} required disabled={busy} /><span id={`count-hint-${row.key}`}>1–15</span></label>
<button type="button" class="remove" aria-label={`Remove map ${index + 1}`} disabled={busy || rows.length === 1} onclick={() => rows = rows.filter(item => item.key !== row.key)}>×</button>
</div>
{/each}