Regenbogenland entfernen — Sterne sind die höchste Stufe

This commit is contained in:
schmop 2026-05-31 16:58:13 +02:00
parent 60a0fe4297
commit 8947a28e59
8 changed files with 17 additions and 40 deletions

View File

@ -9,12 +9,7 @@
--c-rocket-fin: #4a4a6e;
--c-flame-inner: #fff4b3;
--c-flame-outer: #ff8a3d;
--c-rainbow-1: #ff5555;
--c-rainbow-2: #ffa84a;
--c-rainbow-3: #ffe34a;
--c-rainbow-4: #6cdc6c;
--c-rainbow-5: #4ab3ff;
--c-rainbow-6: #b06cff;
--c-star: #ffe34a;
--c-correct: #5fd07a;
--c-text: #1f1f3a;
--c-text-on-dark: #ffffff;

View File

@ -7,7 +7,7 @@
// boost — Raketenschub (Whoosh, niedrig)
// level — neue Stufe (Akkord aufsteigend)
// countdown — letzte 5 Sekunden (Tick pro Sekunde, höher werdend)
// fanfare — Regenbogenland erreicht (kurze Tonfolge)
// fanfare — Sterne erreicht / Runde gewonnen (kurze Tonfolge)
import { get } from 'svelte/store';
import { settings } from '../stores/settings';

View File

@ -4,15 +4,15 @@
import Cloud from '../svg/Cloud.svelte';
import Moon from '../svg/Moon.svelte';
import Star from '../svg/Star.svelte';
import Rainbow from '../svg/Rainbow.svelte';
import { STAGE_THRESHOLDS } from '../../game/stages';
type Props = { correct: number; won: boolean };
let { correct, won }: Props = $props();
// Höhenpositionen 0..1 entlang der Bahn (von unten = 0 nach oben = 1)
const STAGE_Y = [0.05, 0.25, 0.45, 0.65, 0.85, 0.97] as const;
// Höhenpositionen 0..1 entlang der Bahn (von unten = 0 nach oben = 1); Sterne sind oben.
const STAGE_Y = [0.05, 0.25, 0.45, 0.65, 0.85] as const;
const WIN_Y = 0.95; // beim Sieg fliegt die Rakete hoch hinauf zu den Sternen
// Rakete bewegt sich kontinuierlich pro richtiger Antwort: bei den Schwellen-Counts
// sitzt sie genau auf der jeweiligen Stufen-Markierung, dazwischen wird interpoliert.
@ -29,7 +29,7 @@
}
return STAGE_Y[4];
}
const rocketY = $derived(won ? STAGE_Y[5] : yFor(correct));
const rocketY = $derived(won ? WIN_Y : yFor(correct));
</script>
<div class="track" class:won>
@ -48,9 +48,6 @@
<Star size={20} color="#fff" />
<Star size={24} color="#a8e8ff" />
</div>
<div class="marker rainbow" style="bottom: {STAGE_Y[5] * 100}%" class:visible={won}>
<Rainbow size={140} />
</div>
<div class="rocket" style="bottom: calc({rocketY * 100}% - 60px)" aria-hidden="true">
<Rocket size={120} />
@ -86,17 +83,6 @@
}
.marker.stars { display: flex; gap: 6px; left: 60%; }
.rainbow {
left: 50%;
transform: translate(-50%, 50%);
opacity: 0;
transition: opacity 0.6s ease, transform 0.6s ease;
}
.rainbow.visible {
opacity: 1;
transform: translate(-50%, 50%) scale(1.1);
}
.rocket {
position: absolute;
left: 50%;

View File

@ -8,7 +8,6 @@
import Cloud from '../svg/Cloud.svelte';
import Moon from '../svg/Moon.svelte';
import Star from '../svg/Star.svelte';
import Rainbow from '../svg/Rainbow.svelte';
type Props = { target: Target; highlightDate?: string | null };
let { target, highlightDate = null }: Props = $props();
@ -24,7 +23,6 @@
2: { c: Cloud, size: 52 },
3: { c: Moon, size: 40 },
4: { c: Star, size: 36 },
5: { c: Rainbow, size: 56 },
};
</script>

View File

@ -9,9 +9,9 @@
const bestStage = $derived(($progress.perTarget[target]?.top5[0]?.stage ?? 0) as number);
const runs = $derived($progress.perTarget[target]?.runs ?? 0);
// Fortschritt 0..1 → Höhe der Mini-Rakete in der Karte
const progressFraction = $derived(Math.min(bestStage / 5, 1));
const masteredAtRainbow = $derived(bestStage >= 5);
// Fortschritt 0..1 → Höhe der Mini-Rakete in der Karte (Stufe 4 = Sterne = ganz oben)
const progressFraction = $derived(Math.min(bestStage / 4, 1));
const masteredAtStars = $derived(bestStage >= 4);
</script>
<button class="card" type="button" onclick={() => onClick(target)} aria-label={`Zielzahl ${target}`}>
@ -27,7 +27,7 @@
<div class="dot dot-5"></div>
</div>
<div class="footer">
{#if masteredAtRainbow}
{#if masteredAtStars}
<Crown size={28} />
{/if}
<span class="runs" aria-label={`${runs} Runden gespielt`}>
@ -87,7 +87,7 @@
.dot-2 { bottom: 38%; }
.dot-3 { bottom: 58%; }
.dot-4 { bottom: 78%; }
.dot-5 { bottom: 92%; background: var(--c-rainbow-3); width: 6px; height: 6px; }
.dot-5 { bottom: 92%; background: var(--c-star); width: 6px; height: 6px; }
.footer {
display: flex;

View File

@ -37,7 +37,7 @@ describe('progress', () => {
it('limits top5 to five entries', () => {
let p = emptyProgress();
for (let i = 0; i < 10; i++) p = recordRun(p, 6, ((i % 5) + 1) as 1 | 2 | 3 | 4 | 5);
for (let i = 0; i < 10; i++) p = recordRun(p, 6, ((i % 4) + 1) as 1 | 2 | 3 | 4);
expect(p.perTarget[6].top5.length).toBe(5);
expect(p.perTarget[6].runs).toBe(10);
});

View File

@ -1,13 +1,11 @@
// Schwellen aus BRAINSTORM.md: 2/4/7/10 richtige Aufgaben pro Stufenaufstieg.
// Während der Runde zeigt die Rakete Stufen 0..4:
// 0 Boden, 1 Luftballon, 2 Wolken, 3 Mond, 4 Sterne.
// Stufe 5 (Regenbogenland) ist die Win-Celebration — wird nicht durch stageFor zurückgegeben,
// sondern durch isWin() signalisiert und im ResultScreen dargestellt.
// Stufen 0..4: 0 Boden, 1 Luftballon, 2 Wolken, 3 Mond, 4 Sterne.
// Die Sterne sind die höchste Stufe und zugleich der Sieg (isWin).
export const STAGE_THRESHOLDS = [2, 4, 7, 10] as const;
export const TOTAL_TO_WIN = 10;
export type Stage = 0 | 1 | 2 | 3 | 4 | 5;
export type Stage = 0 | 1 | 2 | 3 | 4;
export const STAGE_NAMES: Record<Stage, string> = {
0: 'boden',
@ -15,7 +13,6 @@ export const STAGE_NAMES: Record<Stage, string> = {
2: 'wolken',
3: 'mond',
4: 'sterne',
5: 'regenbogenland',
};
export function stageFor(correct: number): Stage {

View File

@ -10,6 +10,7 @@
import Timer from '../components/game/Timer.svelte';
import BurstFx from '../components/game/BurstFx.svelte';
import IconButton from '../components/shared/IconButton.svelte';
import Star from '../components/svg/Star.svelte';
type Props = { target: Target };
let { target }: Props = $props();
@ -84,7 +85,7 @@
/>
{:else if $game.status === 'won'}
<div class="end-message">
<span class="big">🌈</span>
<span class="big"><Star size={200} /></span>
</div>
{/if}
</main>