Files
Cozypaw-Hospital/docs/game-design.md
T

442 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` (13). 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 |
| 12 | Proof of Concept | Ein Raum, eine Figur, Drag & Drop |
| 34 | Core Systems | RoomNavigator, SaveManager, Settings |
| 57 | Erdgeschoss | Reception, GiftShop, Restaurant, EmergencyRoom |
| 810 | 1. Obergeschoss | XRay, Pharmacy, Lab, PatientRoom |
| 1113 | 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 014 ✅ abgeschlossen. Sprints 1522 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.*