From 65e17caaf940bf1ebb7979ef3c0fc7b19d9a8782 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Thu, 7 May 2026 21:58:34 +0200 Subject: [PATCH] docs: add comprehensive Game Design Document (GDD) covering all systems and sprints 15-22 --- docs/game-design.md | 441 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 docs/game-design.md diff --git a/docs/game-design.md b/docs/game-design.md new file mode 100644 index 0000000..1444d85 --- /dev/null +++ b/docs/game-design.md @@ -0,0 +1,441 @@ +# Cozypaw Hospital — Game Design Document (GDD) + +> **Dieses Dokument ist die einzige Quelle der Wahrheit für das Spiel-Design.** +> Sprint-Specs und Implementierungspläne werden daraus abgeleitet. Änderungen hier zuerst besprechen, dann in Sprint-Specs übernehmen. + +*Zuletzt aktualisiert: 2026-05-07* + +--- + +## Kapitel 1 — Vision & Prinzipien + +### Kern-Idee + +**Genre:** Digitales Puppenhaus / Pretend Play. Sandbox — kein Ziel, kein Scheitern, kein Scoring. + +**Zielgruppe:** Kinder ab 3 Jahren. Primär auf Tablet (Android zuerst, iOS später). + +**Kern-Fantasie:** Ich bin Arzt oder Schwester in einem Tier-Krankenhaus. Ich entscheide, wer wo liegt, wer behandelt wird, wer ein Baby bekommt. Ich erzähle mir meine eigene Geschichte. + +### Nicht-Verhandelbare Prinzipien + +Diese Punkte werden in keinem Sprint, keiner Spec und keinem PR geändert: + +1. **Keine Werbung** — keine Ad-SDKs, kein AdMob, keine Rewarded Ads +2. **Keine Datensammlung** — keine Analytics, kein Tracking, keine Telemetrie +3. **Offline-first** — vollständig ohne Internetverbindung spielbar +4. **Keine In-App-Käufe** — kein Premium-Content, keine Währungen +5. **Keine Achievements / Scoring** — kein Sterne-System, kein Freischalten durch Sammeln +6. **Garten von Anfang an offen** — kein Unlock-Gate +7. **Keine Gift-Mechaniken** — alle Medikamente sind positiv/neutral +8. **COPPA/DSGVO-konform** — Play Store Data Safety: "Keine Daten erhoben" +9. **Kein Text in der Spielwelt** — Icons, Symbole, Farben statt Beschriftungen +10. **Minimum Touch-Target 64dp** — für Kinder-Finger + +### Was wir besser machen als das Original (Yasa Pets Hospital) + +| Original | Cozypaw | +|---|---| +| Werbung zwischen Räumen | ❌ Keine Werbung | +| Firebase Analytics | ❌ Kein Tracking | +| Grüne Giftflaschen machen krank | ❌ Alle Medikamente positiv | +| Garten erst nach Sternen freischaltbar | ✅ Von Anfang an offen | +| Kleine Touch-Targets | ✅ Mind. 64dp | +| Generische Figuren | ✅ Figuren basieren auf Haustieren der Kinder | + +--- + +## Kapitel 2 — Welt & Räume + +### Struktur + +3 Etagen + Gartenbereich. Navigation: +- **Innerhalb einer Etage:** Camera-Pan per `NavigationArrow` +- **Zwischen Etagen:** Aufzug-Button + `RoomNavigator` (Camera-Tween) +- **Garten:** `HomeButton` — wechselt Hospital ↔ Garten + +### Raum-Übersicht + +| Etage | Raum | Scene | Room-Chest Inhalt | Besondere Interaktion | +|---|---|---|---|---| +| EG | Empfang | `Reception.tscn` | Blumensträuße, Nummerntickets | Wartenummer-Automat: Tap → Ticket spawnt | +| EG | Geschenke-Shop | `GiftShop.tscn` | Geschenkkörbe, Kuscheltiere, Grußkarten | — | +| EG | Restaurant | `Restaurant.tscn` | Mahlzeit-Tabletts × 3, Getränke | Tablett auf Tisch stellen | +| EG | Notaufnahme | `EmergencyRoom.tscn` | Trage, Verbandsmaterial | Ambulanz-Türen öffnen → Trage herausziehen | +| 1.OG | Röntgen | `XRay.tscn` | Gips-Set, Röntgenfotos | Figur auf Tisch → Röntgenbild → Gips per Drag anlegen | +| 1.OG | Apotheke | `Pharmacy.tscn` | Medikamentenflaschen × 3, Tüten | Flasche auf Figur → Figur wird gesünder | +| 1.OG | Labor | `Lab.tscn` | Reagenzgläser × 2, Mikroskop-Slides | Slide in Mikroskop → Bild wechselt | +| 1.OG | Patientenzimmer | `PatientRoom.tscn` | Kissen, Infusionsbeutel, TV-Fernbedienung | IV-Bag an Haken + Figur im Bett → SLEEPING | +| 2.OG | Ultraschall | `Ultrasound.tscn` | Gel-Tube, Ultraschall-Sonde | Sonde an PREGNANT → Bildschirm zeigt Fötus | +| 2.OG | Kreißsaal | `DeliveryRoom.tscn` | Baby-Decke, Windeln | DeliveryBed: PREGNANT rein → BABY spawnt | +| 2.OG | Säuglingsstation | `Nursery.tscn` | Fläschchen, Rassel, Schnuller | Wiege schaukeln; Fläschchen an BABY → trinkt | +| Garten | Gartenparty | `GardenParty.tscn` | Tassen, Kuchen, Luftballons | GiftBox: Tap → öffnet; TeaPot: Tap → gießt | + +--- + +## Kapitel 3 — Figurensystem + +### Roster MVP (8 Figuren) + +| ID | Spezies | Rolle | Start-State | +|---|---|---|---| +| `bunny_f` | Häschen | Patientin / Mutter | HEALTHY | +| `bunny_m` | Häschen | Patient / Besucher | HEALTHY | +| `kitten_f` | Kätzchen | Patientin | HEALTHY | +| `kitten_m` | Kätzchen | Patient | HEALTHY | +| `bunny_baby` | Häschen | Baby | BABY | +| `kitten_baby` | Kätzchen | Baby | BABY | +| `fox_doctor` | Fuchs | Arzt | HEALTHY | +| `owl_nurse` | Eule | Krankenschwester | HEALTHY | + +### Zustände + +Bereits in `CharacterData.State` implementiert: + +``` +HEALTHY → SICK → HEALTHY (durch Medikament) +HEALTHY → PREGNANT → (DeliveryBed) → HEALTHY + BABY spawnt +HEALTHY / SICK → SLEEPING (durch IV-Bag oder Bett) +SLEEPING → HEALTHY (nach Interaktion) +HEALTHY → TIRED (kann in Bett gelegt werden) +``` + +### Animations-States (AnimatedSprite2D) + +| State | Trigger | Frames | +|---|---|---| +| `idle` | Default, kein Snap, nicht gehalten | 4 — atmet, blinzelt (loop) | +| `held` | DragDropComponent aktiv | 2 — Arme leicht seitlich | +| `sitting` | Snap auf Stuhl / Hocker / Theke | 2 — aufrecht sitzend | +| `lying` | Snap auf Bett / Tisch / Trage | 2 — flach liegend | +| `happy` | Interaktion erfolgreich abgeschlossen | 4 — hüpft/jubelt (einmalig, dann idle) | +| `sleeping` | State = SLEEPING | 4 — Augen zu, langsamer Atem (loop) | + +### Node-Struktur + +``` +Character (Node2D) class_name Character +├── DragDropComponent ← Drag & Drop (bestehend) +├── SnapReceiver (Node2D) ← NEU: scannt SnapPoints beim Loslassen +├── AnimatedSprite2D ← NEU: ersetzt ColorRect-Placeholder +├── OutfitLayer1 (Sprite2D) ← NEU: Kleidung (Kittel, Patientenhemd) +├── OutfitLayer2 (Sprite2D) ← NEU: Medizinisch (Gips, Verband) +├── OutfitLayer3 (Sprite2D) ← NEU: Accessoire (Stethoskop, Mütze) +├── HandLeft (Node2D) ← NEU: Attachment-Point links +│ └── HeldItem (Node2D) ← aktuell gehaltenes Item (oder leer) +├── HandRight (Node2D) ← NEU: Attachment-Point rechts +│ └── HeldItem (Node2D) +└── CollisionArea (Area2D) ← bestehend +``` + +### Outfit-Regeln + +Ein `OutfitItem` hat `layer: int` (1–3). Per Drag auf eine Figur gezogen → Layer-Sprite wechselt auf das Item-Bild, Item verschwindet aus der Welt. Tap auf aktiven OutfitLayer → Item fällt neben Figur zurück in die Welt. Outfit wird in `CharacterData` als `outfit: Array[String]` (3 Einträge, leer = kein Outfit) gespeichert. + +--- + +## Kapitel 4 — Objekt-Katalog + +### Kategorien + +| Typ | Beschreibung | +|---|---| +| **Möbel / Snap-Host** | Statisch, hat einen oder mehrere SnapPoints für Figuren | +| **Holdable Item** | Kann aufgehoben und in einen Hand-Slot gelegt werden | +| **Outfit Item** | Holdable + per Drag auf Figur anwendbar (belegt Outfit-Layer) | +| **Maschine** | Nimmt Figur oder Item auf → produziert Ausgabe | +| **Spawner** | Tap → neues Item-Exemplar erscheint in der Welt | +| **Applier** | Auf Figur gezogen → ändert State oder spielt Animation | + +### Vollständige Liste (~55 Objekte) + +**EG — Empfang (6)** +- `queue_machine` — Spawner: Tap → `number_ticket` spawnt +- `number_ticket` — Holdable +- `reception_desk` — Snap-Host (Pose: `sitting`) +- `waiting_chair` × 3 — Snap-Host (Pose: `sitting`) + +**EG — Geschenke-Shop (4)** +- `gift_basket` — Holdable +- `plush_toy` — Holdable +- `greeting_card` — Holdable +- `shop_counter` — Snap-Host (Pose: `sitting`) + +**EG — Restaurant (5)** +- `meal_tray_1/2/3` — Holdable (3 Mahlzeit-Varianten) +- `drink_cup` — Holdable +- `dining_table` — Snap-Host (Pose: `sitting`) + +**EG — Notaufnahme (4)** +- `ambulance` — Maschine: Türen öffnen → Trage sichtbar *(bereits impl.)* +- `stretcher` — Holdable + Snap-Host (Pose: `lying`) +- `bandage_roll` — Outfit Item (Layer 2: Verband) +- `er_table` — Snap-Host (Pose: `lying`) + +**1.OG — Röntgen (5)** +- `xray_machine` — Maschine: Figur auf Tisch → Röntgenbild *(bereits impl.)* +- `xray_photo` — Holdable (result item, spawnt nach Röntgen) +- `cast_machine` — Maschine: Figur rein → legt Gips-Outfit an +- `cast_arm` — Outfit Item (Layer 2: Gips) +- `xray_table` — Snap-Host (Pose: `lying`) + +**1.OG — Apotheke (4)** +- `medicine_bottle_red/blue/yellow` — Applier: auf Figur → State = HEALTHY + `happy` +- `medicine_bag` — Holdable +- `pharmacy_counter` — Snap-Host (Pose: `sitting`) + +**1.OG — Labor (4)** +- `microscope` — Maschine: Slide in Slot → Bild wechselt +- `test_tube` × 2 — Holdable +- `microscope_slide` — Holdable → passt in `microscope`-Slot +- `lab_stool` — Snap-Host (Pose: `sitting`) + +**1.OG — Patientenzimmer (6)** +- `patient_bed` × 2 — Snap-Host (Pose: `lying`) *(bereits impl.)* +- `iv_bag` — Holdable + Applier: in IV-Hook gehängt + Figur im Bett → State = SLEEPING +- `iv_hook` — Slot an patient_bed, nimmt `iv_bag` auf +- `tv_remote` — Holdable +- `pillow` — Holdable +- `bedside_table` — Snap-Host (Pose: `sitting`) + +**2.OG — Ultraschall (5)** +- `ultrasound_machine` — Maschine *(bereits impl.)* +- `ultrasound_probe` — Holdable + Applier: auf PREGNANT-Figur auf Tisch → Bildschirm zeigt Fötus +- `gel_tube` — Holdable +- `exam_table` — Snap-Host (Pose: `lying`) +- `ultrasound_screen` — reagiert auf Probe-Nähe (Kind des ultrasound_machine) + +**2.OG — Kreißsaal (4)** +- `delivery_bed` — Maschine: PREGNANT eingerastet → BABY spawnt *(bereits impl.)* +- `baby_blanket` — Holdable +- `diaper` — Outfit Item für BABY (Layer 1) +- `baby_scale` — Snap-Host (Pose: `lying`, nur BABY-Figuren) + +**2.OG — Säuglingsstation (5)** +- `cradle` × 2 — Snap-Host (Pose: `lying`) *(bereits impl.)* +- `baby_bottle` — Holdable + Applier: auf BABY → `happy`-Animation + Trink-SFX +- `rattle` — Holdable +- `pacifier` — Outfit Item für BABY (Layer 3) +- `incubator` — Snap-Host (Pose: `lying`, nur BABY-Figuren) + +**Garten (7)** +- `gift_box` × 3 — Tap → öffnet sich *(bereits impl.)* +- `tea_pot` — Tap → gießt *(bereits impl.)* +- `tea_cup` × 2 — Holdable +- `cake` — Holdable +- `balloon` × 2 — Holdable +- `garden_table` — Snap-Host (Pose: `sitting`) +- `garden_chair` × 2 — Snap-Host (Pose: `sitting`) + +--- + +## Kapitel 5 — Core Systems + +### 5.1 Snap-Point System + +`SnapPoint` ist ein `Node2D` an Möbeln: +```gdscript +class_name SnapPoint extends Node2D +@export var pose: String = "sitting" # "sitting" | "lying" +@export var species_filter: Array[String] = [] # leer = alle; ["BABY"] = nur Babys +var occupant: Character = null +``` + +`SnapReceiver` sitzt auf jeder Figur. Bei `drag_released` scannt er alle `SnapPoint`-Nodes im Radius 80px. Nächster freier Snap (unter Berücksichtigung `species_filter`) gewinnt: Figur teleportiert auf SnapPoint-Position, Animation wechselt zu `pose`. Beim erneuten Aufnehmen (`drag_picked_up`) gibt SnapPoint `occupant` frei → Animation zurück zu `held`. + +### 5.2 Hand-Slot System + +`HandLeft` und `HandRight` sind `Node2D`-Nodes an der Figur. Bei `drag_released` eines Holdable Items prüft das Item ob es sich in Reichweite (~60px) eines leeren Hand-Slots befindet. Wenn ja: Item wird Kind des Hand-Slots, `global_position` relativ zum Slot, reist mit Figur mit. Zweiter Drag vom Hand-Slot löst das Item. Maximum 1 Item pro Hand. + +### 5.3 Outfit Layer System + +Drei `Sprite2D`-Nodes über dem Basis-Sprite der Figur. `OutfitItem` hat: +```gdscript +@export var outfit_layer: int = 1 # 1 | 2 | 3 +@export var outfit_sprite: Texture2D +``` +Per Drag auf Figur → Layer-Sprite zeigt `outfit_sprite`, Item verschwindet aus Welt. Tap auf aktiven Layer → Item fällt neben Figur, Layer-Sprite wird leer. Gespeichert in `CharacterData.outfit: Array[String]` (3 Item-IDs). + +### 5.4 Room Chest System + +Jeder Raum hat einen `RoomChest`-Node: +```gdscript +class_name RoomChest extends Node2D +@export var item_ids: Array[String] = [] +``` +Tap auf Chest → öffnet sich, zeigt Items als Icon-Reihe (tappbar). Tap auf Icon → Item-Instanz spawnt neben Chest. Items die in die Chest-Zone (~100px) zurückgezogen werden despawnen. Gehaltene oder eingerastete Items werden nie automatisch entfernt. Chest-State (welche Items spawnt, wo) wird per SaveManager persistiert. + +### 5.5 Applier-Logik + +Items mit `apply_on_drop: bool = true` triggern `apply_to(character: Character)` wenn auf eine Figur gedroppt. Mapping: + +| Item | Effekt | +|---|---| +| `medicine_bottle_*` | `set_state(HEALTHY)` + `happy`-Animation + `sfx_medicine` | +| `iv_bag` (in IV-Hook, Figur im Bett) | `set_state(SLEEPING)` | +| `baby_bottle` auf BABY | `happy`-Animation + `sfx_baby_coo` | +| `bandage_roll` | Outfit Layer 2: Verband-Sprite | +| `cast_arm` | Outfit Layer 2: Gips-Sprite | +| `diaper` | Outfit Layer 1 für BABY | +| `pacifier` | Outfit Layer 3 für BABY | + +### 5.6 Maschinen-Interaktion + +Maschinen haben einen `receive_slot: SnapPoint`. Zusätzliche Bedingungen lösen Ausgaben aus: + +| Maschine | Bedingung | Ausgabe | +|---|---|---| +| `xray_machine` | Figur auf `xray_table` | Röntgenbild-Animation → `xray_photo` spawnt | +| `ultrasound_machine` | PREGNANT auf `exam_table` + `ultrasound_probe` in HandSlot | Fötus-Bild auf `ultrasound_screen` | +| `microscope` | `microscope_slide` in Slot | Bild-Wechsel auf Screen | +| `queue_machine` | Tap | `number_ticket` spawnt | +| `iv_hook` | `iv_bag` eingehängt + Figur in `patient_bed` | `set_state(SLEEPING)` | +| `delivery_bed` | PREGNANT eingerastet + 2s | BABY spawnt, Mutter → HEALTHY *(bereits impl.)* | +| `cast_machine` | Figur eingerastet | `cast_arm`-Outfit (Layer 2) auf Figur, Figur → HEALTHY | +| `baby_scale` | BABY-Figur eingerastet | `happy`-Animation + `sfx_happy` | +| `ultrasound_machine` | PREGNANT auf `exam_table` + `ultrasound_probe` in HandSlot | Fötus-Bild auf `ultrasound_screen` | + +--- + +## Kapitel 6 — UI & Navigation + +### HUD (immer sichtbar) + +- **Aufzug-Button** — Etagen-Wechsel, 64dp, links oder rechts je nach Raum +- **HomeButton** — Hospital ↔ Garten, 64dp *(bereits impl.)* +- **Settings-Button** — oben rechts, 48dp +- **Kein Score, kein Timer, keine Sterne, kein Text** + +### Navigation + +| System | Status | +|---|---| +| Camera-Pan innerhalb Etage | `NavigationArrow` *(bereits impl.)* | +| Etagen-Wechsel | `ElevatorButton` + `RoomNavigator` *(bereits impl.)* | +| Hospital ↔ Garten | `HomeButton` *(bereits impl.)* | + +### Room Chest UI + +Tap auf Chest → Icon-Reihe erscheint direkt in der Spielwelt über/neben der Chest (kein separater Screen). Tap außerhalb → Chest schließt. Icons haben 64dp Mindestgröße. + +### Settings-Menü + +- Lautstärke Musik (Slider) +- Lautstärke SFX (Slider) +- Savegame zurücksetzen (mit Bestätigung) +- Später: Sprache + +--- + +## Kapitel 7 — Audio Design + +### Musik + +| Bereich | Stil | Loop-Länge | +|---|---|---| +| EG | Fröhlich, Empfangs-Feeling | ~2 min | +| 1.OG | Ruhig, Glockenspiel-Elemente | ~2 min | +| 2.OG | Sanft, Schlaflied-Atmosphäre | ~2 min | +| Garten | Beschwingt, Party-Feeling | ~2 min | + +Cross-Fade bei Etagen-Wechsel (AudioManager bereits impl.). Default-Lautstärke: 60%. + +### SFX-Katalog + +| ID | Trigger | +|---|---| +| `sfx_pickup` | Figur oder Item aufheben | +| `sfx_place` | Figur oder Item ablegen (kein Snap) | +| `sfx_snap` | Figur rastet in SnapPoint ein | +| `sfx_happy` | `happy`-Animation startet | +| `sfx_chest_open` | RoomChest öffnet | +| `sfx_item_spawn` | Item spawnt aus Chest | +| `sfx_medicine` | Medikament auf Figur angewendet | +| `sfx_pour` | TeaPot gießt | +| `sfx_gift_open` | GiftBox öffnet | +| `sfx_cradle_rock` | Wiege schaukelt | +| `sfx_xray_scan` | Röntgengerät aktiv | +| `sfx_ambulance` | Ambulanz-Türen öffnen | +| `sfx_baby_coo` | Baby-Interaktion (Fläschchen, Schaukeln) | +| `sfx_bunny_sniff` | Häschen-Figur aufgehoben | +| `sfx_kitten_meow` | Kätzchen-Figur aufgehoben | +| `sfx_fox_yip` | Fuchs-Figur aufgehoben | +| `sfx_owl_hoot` | Eulen-Figur aufgehoben | + +**Quellen:** freesound.org, opengameart.org (Creative Commons) + +--- + +## Kapitel 8 — Save/Load + +### Format v2 + +Erweiterung des bestehenden `SaveManager` + `GameState`. Migration: v1-Saves werden beim Laden auf v2 hochkonvertiert (fehlende Felder → Defaults). + +```json +{ + "version": 2, + "current_floor": 0, + "characters": { + "bunny_f": { + "position": [640, 360], + "state": "HEALTHY", + "outfit": ["white_coat", "", "stethoscope"], + "held_left": "medicine_bottle_blue", + "held_right": "", + "snap_point": "floor1/xray/xray_table/snap_0" + } + }, + "room_chests": { + "floor0/reception/chest": { + "spawned_items": [ + {"id": "number_ticket", "pos": [200, 300]} + ] + } + } +} +``` + +**Auto-Save:** nach jeder Interaktion (Pick-up, Place, Snap, Outfit-Wechsel, State-Änderung). + +--- + +## Kapitel 9 — Sprint-Plan (Gesamt) + +| Sprint | Titel | Kern-Deliverable | +|---|---|---| +| 0 | Setup | Git, Godot, Android-Export | +| 1–2 | Proof of Concept | Ein Raum, eine Figur, Drag & Drop | +| 3–4 | Core Systems | RoomNavigator, SaveManager, Settings | +| 5–7 | Erdgeschoss | Reception, GiftShop, Restaurant, EmergencyRoom | +| 8–10 | 1. Obergeschoss | XRay, Pharmacy, Lab, PatientRoom | +| 11–13 | 2. Obergeschoss | Ultrasound, DeliveryRoom, Nursery | +| **14** | **Zuhause & Garten** | **GardenParty, HomeButton, GiftBox, TeaPot ✅** | +| **15** | **Character System v2** | **AnimatedSprite2D (6 States), SnapReceiver, HandSlots, OutfitLayers** | +| **16** | **Snap-Point System** | **SnapPoint an allen Möbeln in allen 12 Räumen** | +| **17** | **Hand-Slots + Outfits** | **Item-Holding, OutfitItem-Typ, Apply-Logik, Save-Extension** | +| **18** | **Room Chests + Items** | **RoomChest-Node, alle ~55 Objekte verteilt** | +| **19** | **Maschinen & Applier** | **XRay-Foto, Ultraschall-Fötus, Mikroskop, Wartenummer, IV-Bag, Medizin** | +| **20** | **Sound & Musik** | **Kompletter SFX-Katalog, Musik-Loops** | +| **21** | **Polish** | **Tutorial-Hint, Animations-Feinschliff, Settings-Erweiterung** | +| **22** | **Release** | **Icon, Splash, Play Store, Familien-Testing** | + +Sprints 0–14 ✅ abgeschlossen. Sprints 15–22 stehen aus. + +--- + +## Appendix — Asset-Strategie + +**Empfehlung: Hybrid** +- Kinder malen Helden-Figuren (Häschen, Kätzchen) auf Papier → einscannen → digitalisieren +- Freelancer für Hintergründe und generische Objekte +- Tier-Geräusche: freesound.org (Creative Commons) + +**Asset-Formate:** +- Sprites: PNG mit Transparenz, 2× (für Retina-Displays) +- Animationen: SpriteSheet oder einzelne Frames in `AnimatedSprite2D` +- Audio: OGG für Loops, WAV für kurze SFX + +--- + +*Dieses Dokument wird nur bei Produkt-Entscheidungen aktualisiert, nie bei rein technischen Implementierungsdetails.*