5 plans across 3 waves covering all 17 requirements: - Plan 01 (W1): Word lists, Progress extension, rate limits - Plan 02 (W2): Lesson 3-phase state machine, review/repeat - Plan 03 (W1): Forest visual scene with SVG + grid - Plan 04 (W3): Reward screen, pre-generation, full flow wiring - Plan 05 (W2): Parent area with stats and settings Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.4 KiB
9.4 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03-komplettes-spielerlebnis | 03 | execute | 1 |
|
true |
|
|
Purpose: The growing forest IS the reward system — the child sees their world fill up with each session. Output: Visual forest scene in forest screen with grid, animations, and IndexedDB loading.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @src/forest/scene.ts @src/storage/db.ts @src/types.ts @index.html @src/styles/main.css export function getForestElements(db: IDBDatabase): Promise; export function saveForestElement(db: IDBDatabase, element: ForestElement): Promise;export interface ForestElement { id?: number; levelCompleted: number; imageBlob: Blob; imagePrompt: string; description: string; companionText: string; position: { x: number; y: number }; createdAt: string; }
Task 1: SVG forest background and grid HTML structure index.html, src/styles/main.css index.html, src/styles/main.css, src/forest/scene.ts **Update index.html** `#screen-forest .forest` section (per D-07, D-09):Add a forest scene container ABOVE the level-map but BELOW the greeting:
```html
<div class="forest__scene" id="forest-scene">
<svg class="forest__bg" viewBox="0 0 800 400" xmlns="http://www.w3.org/2000/svg">
<!-- Ground -->
<rect x="0" y="300" width="800" height="100" fill="#C8E6C0" rx="0"/>
<!-- Hills -->
<ellipse cx="200" cy="320" rx="250" ry="80" fill="#A8D8A8"/>
<ellipse cx="650" cy="330" rx="200" ry="60" fill="#B8E0B0"/>
<!-- Trees (background) -->
<g opacity="0.3">
<ellipse cx="80" cy="240" rx="40" ry="60" fill="#7DB87D"/>
<ellipse cx="720" cy="250" rx="35" ry="55" fill="#6DA86D"/>
<ellipse cx="400" cy="230" rx="45" ry="70" fill="#8DC88D"/>
</g>
<!-- Sky gradient -->
<defs>
<linearGradient id="sky-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#E8F4FD"/>
<stop offset="100%" stop-color="#F0F8E8"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="800" height="300" fill="url(#sky-grad)"/>
<!-- Clouds -->
<ellipse cx="150" cy="60" rx="60" ry="25" fill="white" opacity="0.5"/>
<ellipse cx="550" cy="80" rx="70" ry="20" fill="white" opacity="0.4"/>
<!-- Sun -->
<circle cx="700" cy="50" r="30" fill="#F8E8A0" opacity="0.6"/>
</svg>
<div class="forest__grid" id="forest-grid">
<!-- 12 slots for forest elements, filled dynamically -->
</div>
</div>
```
NOTE: The SVG layers must be ordered so sky-rect is FIRST (painted first, at back), then hills/ground on top. Adjust SVG element order: sky rect first, then clouds/sun, then background trees, then hills, then ground.
**Add CSS to `src/styles/main.css`:**
```css
/* Forest Scene */
.forest__scene {
position: relative;
width: 100%;
max-width: 800px;
margin: 0 auto 1.5rem;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.forest__bg {
display: block;
width: 100%;
height: auto;
}
.forest__grid {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, 1fr);
padding: 1rem;
gap: 0.5rem;
}
.forest__element {
display: flex;
align-items: center;
justify-content: center;
}
.forest__element img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: 8px;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.15));
}
/* New element animation (per D-08, FRST-02) */
.forest__element--new {
animation: forest-element-appear 1.2s ease-out forwards;
}
@keyframes forest-element-appear {
0% {
opacity: 0;
transform: scale(0.3);
}
60% {
opacity: 1;
transform: scale(1.05);
}
100% {
opacity: 1;
transform: scale(1);
}
}
```
Add a new exported function `renderForestScene(db: IDBDatabase, newElementId?: number): Promise<void>`:
1. Get `#forest-grid` element
2. Call `getForestElements(db)` to load all persisted elements
3. Clear grid contents
4. For each element (up to 12 — the grid has 12 slots):
- Create a `<div class="forest__element">`
- Create an `<img>` with `src` = `URL.createObjectURL(element.imageBlob)`
- Set `alt` = `element.description`
- Apply random offset per D-07: `style="transform: translate(${randomOffset}px, ${randomOffset}px)"` where offset is +-10-20px random
- If `element.id === newElementId`, add class `forest__element--new` for animation
- Append to grid
5. Store created Object URLs in a module-level array so they can be revoked on next call (prevent memory leak):
```typescript
let activeObjectUrls: string[] = [];
```
At start of function, revoke all old URLs via `URL.revokeObjectURL()`, then clear array.
Update `initForestScreen` to call `renderForestScene(db)` alongside existing `renderLevelMap`.
The `newElementId` parameter will be used by Plan 04 (reward flow) to trigger animation on the just-added element.
<success_criteria>
- Forest screen shows SVG woodland background (sky, hills, trees, clouds)
- 4x3 CSS grid overlays the SVG for element placement
- All stored ForestElements load from IndexedDB and display as images
- New elements animate with fade-in + scale-up (1.2s ease-out)
- Object URLs properly managed (revoked on re-render)
- Level map still visible below/alongside the scene </success_criteria>