Initial scaffold: Godot 4.3 scenes, systems, and placeholders.

Wire all twelve levels with billboard prefabs, autoload systems (inventory, achievements, mock shop, DE/EN/BY), and satire audio locks so the Title→End flow is playable with placeholder assets.
This commit is contained in:
copilot 2026-09-14 18:36:02 +02:00
commit 938cc4bc82
114 changed files with 4819 additions and 0 deletions

29
.gitignore vendored Normal file
View file

@ -0,0 +1,29 @@
# Godot 4
.godot/
.import/
export.cfg
export_presets.cfg
*.translation
# Build / OS
*.apk
*.aab
*.ipa
*.app
*.dmg
*.exe
*.x86_64
*.x86
*.arm64
*.arm32
*.wasm
*.pck
.DS_Store
Thumbs.db
*~
# Editor / agent
.cursor/
*.swp
*.tmp
/tmp/

65
README.md Normal file
View file

@ -0,0 +1,65 @@
# Explosion der Spiele
Pay2Win – um reiche Leute anzulocken. Absichtlich chaotisches Meta-Comedy-Action-Adventure in Godot 4.3+ mit 2.5D-Billboard-Sprites.
**Forgejo:** https://forgejo.fsociety00.cc/copilot/Explosion_der_Spiele
## Start
1. Godot **4.3+** installieren (oder `~/.local/bin/godot` aus dem Scaffold-Setup)
2. Projektordner in Godot öffnen
3. F5 / Play — startet `scenes/title_screen.tscn`
### Steuerung
| Aktion | Taste |
|--------|--------|
| Bewegen | WASD / Pfeile |
| Interagieren | E / Leertaste |
| Angriff | Linksklick / J |
| Pause (Shop/Inventar/Settings) | Esc |
| Cutscene skippen | Enter |
Im Inventar (Pause): Item doppelklicken/aktivieren — **Milch** skippt zum Endboss.
## Scaffold-Inhalt
- 12 Szenen verdrahtet (Title → Intro → Village → … → Final → End + Death)
- Autoloads: `GameState`, `Inventory`, `Achievements`, `Settings`, `Locale` (DE/EN/BY), `Shop` (Mock), `AudioDirector`, `UIRoot`
- Billboard-Prefab (`PlaneMesh` + `billboard_mode`)
- Pay-to-Win-Shop ohne echte Zahlungen (`1€ = 1 Dresslerpunkt` Mock-Button)
- Musik-Lautstärke absichtlich gesperrt (bleibt laut)
- Burger-Cham-Event alle 120s auf dem Title Screen
- Save unter `user://save.json`
## Regenerieren von Platzhaltern
```bash
python3 scripts/tools/generate_scaffold.py
```
## Asset-TODOs (noch ersetzen)
### Grafik
- Titelbild: verpixeltes Mario + Sonic + Burger-Regen + verstörter Mensch
- Burger-Cham-Bild (70-jähriger Mann mit Burger)
- Intro Tisch/Konsole vor/nach Explosion
- Dorf-Häuser & verstörte Dorfbewohner
- Schuhmacher Reif, Eisbär + Schlangenmähdrescher, Schlange, Kuh-Masseur
- Glitzos, dunkelgrauer Magier, bayerischer Farmer, Urhahn, Nutztiere
- Mond: Käseverkäufer, Katapulte, Mondschwert, Fake Bowser
- Dresslermeister (Riesenapfel), Hongkong/Glitschkönig, Quirxels
- Todesbildschirm / UI-Icons (Zahn, Milch, Banane, Wartchen, …)
### Audio
- Laute Startmusik mit Gesang („Hey, das hier ist so eine laute Musik extra…“)
- Sprecher Intro / Hongkong / Dorf / Bayerisch „Schulz“ / EN / DE-Snippets
- Burger-Cham, Explosion, Kämpfe, Massage, Käsekatapult, …
Aktuell liegen nur generierte Beep-WAVs unter `assets/audio/placeholders/`.
## Hinweis
Keine echten Zahlungen, keine lizenzierten Charakter-Assets — alles Satire/Platzhalter.

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://nhwevpdjup3n"
path="res://.godot/imported/music_title.wav-57b4e64507bbc8925e954e4e57e5851e.sample"
[deps]
source_file="res://assets/audio/placeholders/music_title.wav"
dest_files=["res://.godot/imported/music_title.wav-57b4e64507bbc8925e954e4e57e5851e.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://c2npy2yl10fnk"
path="res://.godot/imported/sfx_burger.wav-db0aa5a35f09b19628bcc5adb31f2f05.sample"
[deps]
source_file="res://assets/audio/placeholders/sfx_burger.wav"
dest_files=["res://.godot/imported/sfx_burger.wav-db0aa5a35f09b19628bcc5adb31f2f05.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://co6y6u2bhy6h0"
path="res://.godot/imported/sfx_explosion.wav-77982b105b7a7f0e48a5d77c7259ba4b.sample"
[deps]
source_file="res://assets/audio/placeholders/sfx_explosion.wav"
dest_files=["res://.godot/imported/sfx_explosion.wav-77982b105b7a7f0e48a5d77c7259ba4b.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cbh0wcso2lbc0"
path="res://.godot/imported/sfx_hit.wav-d268f78f266f112d5872bff13bfabd54.sample"
[deps]
source_file="res://assets/audio/placeholders/sfx_hit.wav"
dest_files=["res://.godot/imported/sfx_hit.wav-d268f78f266f112d5872bff13bfabd54.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cim5abw2jutxg"
path="res://.godot/imported/voice_by.wav-4a06e6c3862106da5c3ffb005bb73594.sample"
[deps]
source_file="res://assets/audio/placeholders/voice_by.wav"
dest_files=["res://.godot/imported/voice_by.wav-4a06e6c3862106da5c3ffb005bb73594.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dn0idcqha2dt3"
path="res://.godot/imported/voice_de.wav-527db9c39a6a9179700a48588b5966b4.sample"
[deps]
source_file="res://assets/audio/placeholders/voice_de.wav"
dest_files=["res://.godot/imported/voice_de.wav-527db9c39a6a9179700a48588b5966b4.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dbgd6utbqexdl"
path="res://.godot/imported/voice_en.wav-8421c4eefb2d6ff186d4f1843721b23e.sample"
[deps]
source_file="res://assets/audio/placeholders/voice_en.wav"
dest_files=["res://.godot/imported/voice_en.wav-8421c4eefb2d6ff186d4f1843721b23e.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b37uov28lxb2u"
path="res://.godot/imported/apple.png-dde7f9d0b34ab9bfe7973a6edb4257ce.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/apple.png"
dest_files=["res://.godot/imported/apple.png-dde7f9d0b34ab9bfe7973a6edb4257ce.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dh5i54nbbbj6r"
path="res://.godot/imported/bear.png-02b280fa351cad00e3ac7c302cee163d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/bear.png"
dest_files=["res://.godot/imported/bear.png-02b280fa351cad00e3ac7c302cee163d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://baruvthy65fu1"
path="res://.godot/imported/bowser.png-c84ab30a63fce643c365f696605d01d2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/bowser.png"
dest_files=["res://.godot/imported/bowser.png-c84ab30a63fce643c365f696605d01d2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://uyn85tw36akw"
path="res://.godot/imported/burger.png-3df913a40d4a086c06294c0a44a2fa1e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/burger.png"
dest_files=["res://.godot/imported/burger.png-3df913a40d4a086c06294c0a44a2fa1e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bluonhwj4bue2"
path="res://.godot/imported/cheese.png-91a03b4c9a1a30165eb93db45ccb7f2c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/cheese.png"
dest_files=["res://.godot/imported/cheese.png-91a03b4c9a1a30165eb93db45ccb7f2c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bxbfjo6md4v2u"
path="res://.godot/imported/cow.png-5af0a175ec8cb018d78c655e4e9d50ab.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/cow.png"
dest_files=["res://.godot/imported/cow.png-5af0a175ec8cb018d78c655e4e9d50ab.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://v2ptpx03awel"
path="res://.godot/imported/disturbed.png-fdb7e57dbd8f4fd176e77131b1f9ae80.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/disturbed.png"
dest_files=["res://.godot/imported/disturbed.png-fdb7e57dbd8f4fd176e77131b1f9ae80.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bjo2jmg30dwlb"
path="res://.godot/imported/enemy.png-2bc00d5222c6ad20d9fbc774f89ed042.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/enemy.png"
dest_files=["res://.godot/imported/enemy.png-2bc00d5222c6ad20d9fbc774f89ed042.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cnhxctf1imwsb"
path="res://.godot/imported/farmer.png-35160a98f4d1ab8cce31fcf559333381.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/farmer.png"
dest_files=["res://.godot/imported/farmer.png-35160a98f4d1ab8cce31fcf559333381.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cadkrhb8eu8jq"
path="res://.godot/imported/glitzo.png-e4c6cb631c224e6a8ab47dcff37802b0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/glitzo.png"
dest_files=["res://.godot/imported/glitzo.png-e4c6cb631c224e6a8ab47dcff37802b0.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bwloqkk48cr1o"
path="res://.godot/imported/hongkong.png-1b0e65f7ca46d69b86624e44d93f5178.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/hongkong.png"
dest_files=["res://.godot/imported/hongkong.png-1b0e65f7ca46d69b86624e44d93f5178.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b0ia7b1sjbxj2"
path="res://.godot/imported/house.png-f12f204fc595e6df46c249db8b7216a0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/house.png"
dest_files=["res://.godot/imported/house.png-f12f204fc595e6df46c249db8b7216a0.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://crnb86h5j70mg"
path="res://.godot/imported/icon.png-8434f827a87e33b3fd72236835b6f111.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/icon.png"
dest_files=["res://.godot/imported/icon.png-8434f827a87e33b3fd72236835b6f111.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://fiud1rqkrsif"
path="res://.godot/imported/mage.png-ee7edbc9dcd55175c02ebedf34804aa8.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/mage.png"
dest_files=["res://.godot/imported/mage.png-ee7edbc9dcd55175c02ebedf34804aa8.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c80pynhwy8fsk"
path="res://.godot/imported/mario.png-973ed4c47ea1f2908c76904a3e7156bf.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/mario.png"
dest_files=["res://.godot/imported/mario.png-973ed4c47ea1f2908c76904a3e7156bf.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bf73lxk3qoj3x"
path="res://.godot/imported/npc.png-c9f72d8cc600833078acd2b5de3e225c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/npc.png"
dest_files=["res://.godot/imported/npc.png-c9f72d8cc600833078acd2b5de3e225c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://maq6a015enut"
path="res://.godot/imported/player.png-2b290dc7723bf72b037d787d1b30075d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/player.png"
dest_files=["res://.godot/imported/player.png-2b290dc7723bf72b037d787d1b30075d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://u1hyriw1ibin"
path="res://.godot/imported/quirxel.png-fcf13717d2eb4da0c9997e65d430ccd1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/quirxel.png"
dest_files=["res://.godot/imported/quirxel.png-fcf13717d2eb4da0c9997e65d430ccd1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://daxjrieeu120x"
path="res://.godot/imported/rooster.png-9a1e1d11af73a69166984766ee65babc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/rooster.png"
dest_files=["res://.godot/imported/rooster.png-9a1e1d11af73a69166984766ee65babc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://i5qx8kyrhbxb"
path="res://.godot/imported/shoe.png-29f3e83b74b5ec37f2dbe3ba2ab6702e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/shoe.png"
dest_files=["res://.godot/imported/shoe.png-29f3e83b74b5ec37f2dbe3ba2ab6702e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ccl8j0eef53by"
path="res://.godot/imported/skull.png-8a0473e644f92d1307336c1250a35ad4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/skull.png"
dest_files=["res://.godot/imported/skull.png-8a0473e644f92d1307336c1250a35ad4.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://1ajf0vlua0uu"
path="res://.godot/imported/snake.png-b61c1accf854736987940264d77b0800.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/snake.png"
dest_files=["res://.godot/imported/snake.png-b61c1accf854736987940264d77b0800.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://db7yx7878cuor"
path="res://.godot/imported/sonic.png-76fdb2575572fa769f6910819c2f27ea.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/placeholders/sonic.png"
dest_files=["res://.godot/imported/sonic.png-76fdb2575572fa769f6910819c2f27ea.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

72
data/achievements.json Normal file
View file

@ -0,0 +1,72 @@
[
{
"id": "died_100",
"title_key": "ach_died_100_title",
"desc_key": "ach_died_100_desc"
},
{
"id": "tiramisu_tonne",
"title_key": "ach_tiramisu_title",
"desc_key": "ach_tiramisu_desc"
},
{
"id": "snake_massage",
"title_key": "ach_snake_massage_title",
"desc_key": "ach_snake_massage_desc"
},
{
"id": "milk_skip",
"title_key": "ach_milk_skip_title",
"desc_key": "ach_milk_skip_desc"
},
{
"id": "first_glitzo",
"title_key": "ach_first_glitzo_title",
"desc_key": "ach_first_glitzo_desc"
},
{
"id": "moon_cheese",
"title_key": "ach_moon_cheese_title",
"desc_key": "ach_moon_cheese_desc"
},
{
"id": "eaten_to_death",
"title_key": "ach_eaten_title",
"desc_key": "ach_eaten_desc"
},
{
"id": "played_hour",
"title_key": "ach_hour_title",
"desc_key": "ach_hour_desc"
},
{
"id": "cool_sunglasses",
"title_key": "ach_sunglasses_title",
"desc_key": "ach_sunglasses_desc"
},
{
"id": "dressler_apples",
"title_key": "ach_dressler_title",
"desc_key": "ach_dressler_desc"
},
{
"id": "beat_bowser",
"title_key": "ach_bowser_title",
"desc_key": "ach_bowser_desc"
},
{
"id": "beat_hongkong",
"title_key": "ach_hongkong_title",
"desc_key": "ach_hongkong_desc"
},
{
"id": "tooth_millionaire",
"title_key": "ach_tooth_title",
"desc_key": "ach_tooth_desc"
},
{
"id": "all_burgers",
"title_key": "ach_all_title",
"desc_key": "ach_all_desc"
}
]

13
data/items.json Normal file
View file

@ -0,0 +1,13 @@
[
{"id": "tooth", "name_key": "item_tooth", "stackable": true},
{"id": "snake", "name_key": "item_snake", "stackable": false},
{"id": "milk", "name_key": "item_milk", "stackable": true, "usable": true},
{"id": "banana", "name_key": "item_banana", "stackable": true},
{"id": "wartchen", "name_key": "item_wartchen", "stackable": true},
{"id": "sunglasses", "name_key": "item_sunglasses", "stackable": false},
{"id": "death_ticket", "name_key": "item_death_ticket", "stackable": true, "usable": true},
{"id": "moon_sword", "name_key": "item_moon_sword", "stackable": false},
{"id": "chicken", "name_key": "item_chicken", "stackable": true},
{"id": "cheese", "name_key": "item_cheese", "stackable": true},
{"id": "tiramisu", "name_key": "item_tiramisu", "stackable": true, "usable": true}
]

88
data/locales/by.json Normal file
View file

@ -0,0 +1,88 @@
{
"menu_start": "Losgeh!",
"menu_continue": "Weidermacha",
"menu_settings": "Einstellunga",
"menu_shop": "Pay-to-Win-Gschäft",
"menu_inventory": "Rucksack",
"menu_language": "Sproch",
"menu_sfx": "SFX-Lautstärkn",
"menu_music": "Musikk (bleibt laut)",
"menu_quit": "Aufhören",
"menu_resume": "Zruck",
"menu_buy_point": "1€ = 1 Dresslerpunkt (Mock)",
"menu_points": "Dresslerpunkte: %s",
"title_subtitle": "Pay2Win – damit de Reichen kommen",
"title_bubble": "Oi mei, des wird so verrückt. Hongkong is a weiser Mo.",
"death_title": "DU BIST DAHI",
"death_continue": "No amoi sterbn?",
"interact_hint": "[E] Redn",
"exit_hint": "Level verlassn",
"lang_voice_de": "Am deutschen Wesen soll die Welt genesen",
"lang_voice_en": "I drink my garden in the cinema",
"lang_voice_by": "Schulz",
"dialog_reif": "Wennst no ned reif bist, muaß i da an Schuah schenken!",
"dialog_snake": "I bin ganz verspannt, oi tut des weh",
"dialog_bear": "Hongkong hot den Rasenmäher zu Schlangen gmochd",
"dialog_hongkong_npc": "Hongkong, Hongkong is er!",
"dialog_cow": "Massage: Schlang / Spieler / Quadrat-Emoji",
"dialog_farmer": "Servus! De Schweine brauchn Quests und der Urhahn pickt.",
"dialog_mage": "Rechts gehts zum Hof. Links is Fake-Open-World.",
"dialog_cheese": "Der Mond is aus Kas. Willst a Sonnenbrille?",
"dialog_bowser": "I halt a Hendl gfangn! (Fake Bowser)",
"dialog_hongkong_boss": "I kimm wieder!",
"dialog_bear_end": "Guad dassd unsere Welt grettet host. Aber Zahnarzt kostet immer no.",
"dialog_dressler": "Dresslauf die Eins!",
"dialog_intro_1": "Du sitzt an deim Tisch…",
"dialog_intro_2": "Du muasst es schaffn, sonst kommt der Hongkong",
"death_shoe": "Schuah gschenkt kriagt",
"death_mower": "Mähdrescher-Biss",
"death_rooster": "Vom Urhahn totpickt",
"death_quirxel": "Vom Quirxel gquadratet",
"death_default": "Z'Tode gstorbn",
"item_tooth": "Zahn",
"item_snake": "Schlang",
"item_milk": "Selbergmelkte Milch",
"item_banana": "Banane",
"item_wartchen": "Wartchen",
"item_sunglasses": "Coole Sonnenbrille",
"item_death_ticket": "Ticket: Z'Tode gessen",
"item_moon_sword": "Mondschwert",
"item_chicken": "Hendl",
"item_cheese": "Verschimmelta Kas",
"item_tiramisu": "Tiramisu (kg)",
"shop_sunglasses": "Sonnenbrille (5 Punkte)",
"shop_ticket": "Z'Tode-gessen-Ticket (3 Punkte)",
"shop_milk": "Milch-Skip (10 Punkte)",
"shop_tiramisu": "1kg Tiramisu (1 Punkt)",
"toast_achievement": "Achievement: %s",
"build_house": "Haus baua",
"ach_died_100_title": "Z'Tode gstorbn",
"ach_died_100_desc": "100x sterbn",
"ach_tiramisu_title": "Jetzt is Abnehma ogfragt",
"ach_tiramisu_desc": "1 Tonne Tiramisu essn",
"ach_snake_massage_title": "Schlangenmassage",
"ach_snake_massage_desc": "Schlang zum Masseur bringa",
"ach_milk_skip_title": "Selbergmelkte Milch",
"ach_milk_skip_desc": "Milch trinkn und zum Endboss skippen",
"ach_first_glitzo_title": "So pixelig dass i scho a Pixel war",
"ach_first_glitzo_desc": "Ersten Glitzo tötn",
"ach_moon_cheese_title": "Jetzt woaß i dass der Mond aus Kas is",
"ach_moon_cheese_desc": "Mond-Level betretn",
"ach_eaten_title": "Z'Tode gessen",
"ach_eaten_desc": "Z'Tode-gessen-Ticket essn",
"ach_hour_title": "Überlegt",
"ach_hour_desc": "1 Stund spuin",
"ach_sunglasses_title": "Coole Sonnenbrille",
"ach_sunglasses_desc": "Sonnenbrille am Mond kaafn",
"ach_dressler_title": "De Äpfel san heit guat dresselt worn",
"ach_dressler_desc": "Dresslermeister bsiegn",
"ach_bowser_title": "Du host einfach den Bowser bsiegt",
"ach_bowser_desc": "Bowser bsiegn",
"ach_hongkong_title": "Du host des endgültig geglitched",
"ach_hongkong_desc": "Hongkong bsiegn",
"ach_tooth_title": "Du bist jetzt Zahnionär",
"ach_tooth_desc": "Zähne beim Zahnarzt klauen",
"ach_all_title": "Burger, Burger, Burger. Heit ess i alle Quests",
"ach_all_desc": "Alle Achievements freischaltn",
"burger_cham": "Burger! Beste Qualität von der Explosion der Spiele"
}

88
data/locales/de.json Normal file
View file

@ -0,0 +1,88 @@
{
"menu_start": "Starten",
"menu_continue": "Weiter",
"menu_settings": "Einstellungen",
"menu_shop": "Pay-to-Win-Shop",
"menu_inventory": "Inventar",
"menu_language": "Sprache",
"menu_sfx": "SFX-Lautstärke",
"menu_music": "Musik (gesperrt laut)",
"menu_quit": "Beenden",
"menu_resume": "Zurück",
"menu_buy_point": "1€ = 1 Dresslerpunkt (Mock)",
"menu_points": "Dresslerpunkte: %s",
"title_subtitle": "Pay2Win – um reiche Leute anzulocken",
"title_bubble": "Oh mein Gott, es wird so verrückt. Hongkong ist ein weiser Mann.",
"death_title": "DU BIST GESTORBEN",
"death_continue": "Nochmal sterben wollen?",
"interact_hint": "[E] Interagieren",
"exit_hint": "Level verlassen",
"lang_voice_de": "Am deutschen Wesen soll die Welt genesen",
"lang_voice_en": "I drink my garden in the cinema",
"lang_voice_by": "Schulz",
"dialog_reif": "Wenn du noch nicht reif bist, muss ich dir einen Schuh schenken!",
"dialog_snake": "Ich bin sehr verspannt, oh, mir tut alles so weh",
"dialog_bear": "Hongkong hat den Rasenmäher zu Schlangen verwandelt",
"dialog_hongkong_npc": "Hongkong, Hongkong ist er!",
"dialog_cow": "Massageoptionen: Schlange / Spieler / Quadrat-Emoji",
"dialog_farmer": "Servus! Die Schweine brauchen Quests und der Urhahn pickt.",
"dialog_mage": "Der Weg nach rechts führt zum Bauernhof. Links ist Fake-Open-World.",
"dialog_cheese": "Der Mond ist aus Käse. Willst du eine Sonnenbrille?",
"dialog_bowser": "Ich halte ein Huhn gefangen! (Fake Bowser)",
"dialog_hongkong_boss": "Ich werde wiederkommen!",
"dialog_bear_end": "Gut, dass du unsere Welt gerettet hast. Aber die Zahnarztkosten sind immer noch teuer.",
"dialog_dressler": "Dresslauf die Eins!",
"dialog_intro_1": "Du sitzt an deinem Tisch…",
"dialog_intro_2": "Du musst es schaffen, sonst kommt der Hongkong",
"death_shoe": "Schuh geschenkt",
"death_mower": "Mähdrescher-Biss",
"death_rooster": "Vom Urhahn totgepickt",
"death_quirxel": "Von einem Quirxel gequadratet",
"death_default": "Zu Tode gestorben",
"item_tooth": "Zahn",
"item_snake": "Schlange",
"item_milk": "Selbst gemelkte Milch",
"item_banana": "Banane",
"item_wartchen": "Wartchen",
"item_sunglasses": "Coole Sonnenbrille",
"item_death_ticket": "Ticket: Zu Tode gegessen",
"item_moon_sword": "Mondschwert",
"item_chicken": "Huhn",
"item_cheese": "Verschimmelter Käse",
"item_tiramisu": "Tiramisu (kg)",
"shop_sunglasses": "Sonnenbrille (5 Punkte)",
"shop_ticket": "Zu-Tode-gegessen-Ticket (3 Punkte)",
"shop_milk": "Milch-Skip (10 Punkte)",
"shop_tiramisu": "1kg Tiramisu (1 Punkt)",
"toast_achievement": "Achievement: %s",
"build_house": "Haus bauen",
"ach_died_100_title": "Zu Tode gestorben",
"ach_died_100_desc": "100x sterben",
"ach_tiramisu_title": "Jetzt ist Abnehmen angesagt",
"ach_tiramisu_desc": "1 Tonne Tiramisu essen",
"ach_snake_massage_title": "Schlangenmassage",
"ach_snake_massage_desc": "Schlange zum Masseur bringen",
"ach_milk_skip_title": "Selbst gemelkte Milch",
"ach_milk_skip_desc": "Milch trinken und zum Endboss skippen",
"ach_first_glitzo_title": "Das ist ja so pixelig, dass ich schon ein Pixel würde",
"ach_first_glitzo_desc": "Ersten Glitzo töten",
"ach_moon_cheese_title": "Jetzt weiß ich, dass der Mond aus Käse ist",
"ach_moon_cheese_desc": "Mond-Level betreten",
"ach_eaten_title": "Zu Tode gegessen",
"ach_eaten_desc": "Zu-Tode-gegessen-Ticket essen",
"ach_hour_title": "Überlegt",
"ach_hour_desc": "1 Stunde spielen",
"ach_sunglasses_title": "Coole Sonnenbrille",
"ach_sunglasses_desc": "Sonnenbrille auf dem Mond kaufen",
"ach_dressler_title": "Die Äpfel wurden heute echt gut gedresselt",
"ach_dressler_desc": "Dresslermeister besiegen",
"ach_bowser_title": "Du hast einfach den Bowser besiegt",
"ach_bowser_desc": "Bowser besiegen",
"ach_hongkong_title": "Du hast das endgültig geglitched",
"ach_hongkong_desc": "Hongkong besiegen",
"ach_tooth_title": "Du bist jetzt Zahnionär",
"ach_tooth_desc": "Zähne beim Zahnarzt klauen",
"ach_all_title": "Burger, Burger, Burger. Heute ess ich alle Quests",
"ach_all_desc": "Alle Achievements freischalten",
"burger_cham": "Burger! Beste Qualität von der Explosion der Spiele"
}

88
data/locales/en.json Normal file
View file

@ -0,0 +1,88 @@
{
"menu_start": "Start",
"menu_continue": "Continue",
"menu_settings": "Settings",
"menu_shop": "Pay-to-Win Shop",
"menu_inventory": "Inventory",
"menu_language": "Language",
"menu_sfx": "SFX Volume",
"menu_music": "Music (locked loud)",
"menu_quit": "Quit",
"menu_resume": "Resume",
"menu_buy_point": "1€ = 1 Dressler Point (Mock)",
"menu_points": "Dressler Points: %s",
"title_subtitle": "Pay2Win – to attract rich people",
"title_bubble": "Oh my god, it will get so crazy. Hongkong is a wise man.",
"death_title": "YOU DIED",
"death_continue": "Want to die again?",
"interact_hint": "[E] Interact",
"exit_hint": "Leave level",
"lang_voice_de": "Am deutschen Wesen soll die Welt genesen",
"lang_voice_en": "I drink my garden in the cinema",
"lang_voice_by": "Schulz",
"dialog_reif": "If you are not ripe yet, I must gift you a shoe!",
"dialog_snake": "I am very tense, oh everything hurts",
"dialog_bear": "Hongkong turned the lawnmower into snakes",
"dialog_hongkong_npc": "Hongkong, Hongkong is he!",
"dialog_cow": "Massage options: Snake / Player / Square Emoji",
"dialog_farmer": "Howdy! Pigs need quests and the Ur-rooster pecks.",
"dialog_mage": "Right path leads to the farm. Left is fake open world.",
"dialog_cheese": "The moon is made of cheese. Want sunglasses?",
"dialog_bowser": "I am holding a chicken hostage! (Fake Bowser)",
"dialog_hongkong_boss": "I will return!",
"dialog_bear_end": "Good that you saved our world. But dental costs are still expensive.",
"dialog_dressler": "Dress run the one!",
"dialog_intro_1": "You sit at your desk…",
"dialog_intro_2": "You must succeed, or Hongkong comes",
"death_shoe": "Gifted a shoe",
"death_mower": "Combine-harvester bite",
"death_rooster": "Pecked to death by Ur-rooster",
"death_quirxel": "Squared by a Quirxel",
"death_default": "Died to death",
"item_tooth": "Tooth",
"item_snake": "Snake",
"item_milk": "Self-reported milk",
"item_banana": "Banana",
"item_wartchen": "Wartchen",
"item_sunglasses": "Cool sunglasses",
"item_death_ticket": "Ticket: Eaten to death",
"item_moon_sword": "Moon sword",
"item_chicken": "Chicken",
"item_cheese": "Moldy cheese",
"item_tiramisu": "Tiramisu (kg)",
"shop_sunglasses": "Sunglasses (5 points)",
"shop_ticket": "Eaten-to-death ticket (3 points)",
"shop_milk": "Milk skip (10 points)",
"shop_tiramisu": "1kg tiramisu (1 point)",
"toast_achievement": "Achievement: %s",
"build_house": "Build house",
"ach_died_100_title": "Died to death",
"ach_died_100_desc": "Die 100 times",
"ach_tiramisu_title": "Time to lose weight",
"ach_tiramisu_desc": "Eat 1 ton of tiramisu",
"ach_snake_massage_title": "Snake massage",
"ach_snake_massage_desc": "Bring the snake to the masseur",
"ach_milk_skip_title": "Self-reported milk",
"ach_milk_skip_desc": "Drink milk and skip to final boss",
"ach_first_glitzo_title": "So pixelly I might become a pixel",
"ach_first_glitzo_desc": "Kill the first Glitzo",
"ach_moon_cheese_title": "Now I know the moon is cheese",
"ach_moon_cheese_desc": "Enter the moon level",
"ach_eaten_title": "Eaten to death",
"ach_eaten_desc": "Eat the eaten-to-death ticket",
"ach_hour_title": "Thoughtful",
"ach_hour_desc": "Play for 1 hour",
"ach_sunglasses_title": "Cool sunglasses",
"ach_sunglasses_desc": "Buy sunglasses on the moon",
"ach_dressler_title": "The apples were dressed well today",
"ach_dressler_desc": "Defeat Dresslermeister",
"ach_bowser_title": "You simply beat Bowser",
"ach_bowser_desc": "Defeat Bowser",
"ach_hongkong_title": "You finally glitched it",
"ach_hongkong_desc": "Defeat Hongkong",
"ach_tooth_title": "You are now a toothionaire",
"ach_tooth_desc": "Steal teeth at the dentist",
"ach_all_title": "Burger, Burger, Burger. Today I eat all quests",
"ach_all_desc": "Unlock all achievements",
"burger_cham": "Burger! Best quality from Explosion der Spiele"
}

94
project.godot Normal file
View file

@ -0,0 +1,94 @@
; Engine configuration file.
; Explosion der Spiele — Pay2Win Chaos Scaffold (Godot 4.3+)
config_version=5
[application]
config/name="Explosion der Spiele"
config/description="Pay2Win – um reiche Leute anzulocken. Absichtlich chaotisches Meta-Comedy-Spiel."
config/version="0.1.0-scaffold"
run/main_scene="res://scenes/title_screen.tscn"
config/features=PackedStringArray("4.3", "Forward Plus")
config/icon="res://assets/sprites/placeholders/icon.png"
[autoload]
Settings="*res://scripts/autoload/settings.gd"
Locale="*res://scripts/autoload/locale.gd"
Inventory="*res://scripts/autoload/inventory.gd"
Achievements="*res://scripts/autoload/achievements.gd"
Shop="*res://scripts/autoload/shop.gd"
AudioDirector="*res://scripts/autoload/audio_director.gd"
GameState="*res://scripts/autoload/game_state.gd"
UIRoot="*res://scenes/ui/ui_root.tscn"
[display]
window/size/viewport_width=1280
window/size/viewport_height=720
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
[input]
move_left={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
move_right={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
move_up={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
move_down={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
interact={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}
attack={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":74,"key_label":0,"unicode":106,"location":0,"echo":false,"script":null)
]
}
pause_menu={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
skip_cutscene={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
[layer_names]
3d_physics/layer_1="world"
3d_physics/layer_2="player"
3d_physics/layer_3="enemy"
3d_physics/layer_4="interact"
3d_physics/layer_5="hazard"
3d_physics/layer_6="exit"
[rendering]
textures/canvas_textures/default_texture_filter=0
anti_aliasing/quality/msaa_3d=0

73
scenes/death_screen.tscn Normal file
View file

@ -0,0 +1,73 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/levels/death_screen.gd" id="1"]
[node name="DeathScreen" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1")
[node name="Bg" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
color = Color(0.7, 0.1, 0.5, 1)
[node name="Title" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -300.0
offset_top = 120.0
offset_right = 300.0
offset_bottom = 180.0
theme_override_font_sizes/font_size = 36
horizontal_alignment = 1
text = "DU BIST GESTORBEN"
[node name="Cause" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -300.0
offset_top = 200.0
offset_right = 300.0
offset_bottom = 250.0
horizontal_alignment = 1
text = "..."
[node name="Skull" type="ColorRect" parent="."]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -40.0
offset_top = -40.0
offset_right = 40.0
offset_bottom = 40.0
color = Color(1, 1, 1, 1)
[node name="ContinueButton" type="Button" parent="."]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -160.0
offset_top = -120.0
offset_right = 160.0
offset_bottom = -70.0
text = "Continue"
[connection signal="pressed" from="ContinueButton" to="." method="_on_continue_pressed"]

58
scenes/end_screen.tscn Normal file
View file

@ -0,0 +1,58 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/end_screen.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.4, 0.7, 0.45, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="end_screen" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="BearEnd" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0.1, 0)
dialog_key = "dialog_bear_end"
texture_path = "res://assets/sprites/placeholders/bear.png"
[node name="HouseSpot" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0.1, -2)
dialog_key = "build_house"
texture_path = "res://assets/sprites/placeholders/house.png"
quest_flag = "house_built"

75
scenes/farm.tscn Normal file
View file

@ -0,0 +1,75 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/farm.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.45, 0.35, 0.2, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="farm" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="Farmer" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 0.1, 0)
dialog_key = "dialog_farmer"
texture_path = "res://assets/sprites/placeholders/farmer.png"
give_item_id = "chicken"
[node name="Urhahn" parent="." instance=ExtResource("5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0.1, -2)
max_hp = 6
texture_path = "res://assets/sprites/placeholders/rooster.png"
defeat_flag = "urhahn_defeated"
[node name="RoosterHazard" parent="." instance=ExtResource("6")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0.1, -4)
death_cause_key = "death_rooster"
[node name="Dressler" parent="." instance=ExtResource("5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4, 0.1, -4)
max_hp = 5
texture_path = "res://assets/sprites/placeholders/apple.png"
defeat_flag = "dressler_defeated"
achievement_on_defeat = "dressler_apples"
[node name="Exit" parent="." instance=ExtResource("3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -12)
require_flag = "urhahn_defeated"

57
scenes/farm_path.tscn Normal file
View file

@ -0,0 +1,57 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/farm_path.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.55, 0.5, 0.3, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="farm_path" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="Mage" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -3)
dialog_key = "dialog_mage"
texture_path = "res://assets/sprites/placeholders/mage.png"
quest_flag = "mage_talked"
[node name="Exit" parent="." instance=ExtResource("3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4, 0.1, -10)
require_flag = "mage_talked"

62
scenes/final_boss.tscn Normal file
View file

@ -0,0 +1,62 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/final_boss.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.15, 0.05, 0.2, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="final_boss" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="Hongkong" parent="." instance=ExtResource("5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -3)
max_hp = 12
texture_path = "res://assets/sprites/placeholders/hongkong.png"
defeat_flag = "hongkong_defeated"
achievement_on_defeat = "beat_hongkong"
[node name="QuirxelHazard" parent="." instance=ExtResource("6")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0.1, -2)
death_cause_key = "death_quirxel"
[node name="Exit" parent="." instance=ExtResource("3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -12)
require_flag = "hongkong_defeated"

View file

@ -0,0 +1,73 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/levels/intro_cutscene.gd" id="1"]
[node name="IntroCutscene" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1")
[node name="Bg" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
color = Color(0.12, 0.1, 0.08, 1)
[node name="Desk" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -200.0
offset_top = -40.0
offset_right = 200.0
offset_bottom = 80.0
color = Color(0.45, 0.28, 0.12, 1)
[node name="Console" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -60.0
offset_top = -100.0
offset_right = 60.0
offset_bottom = -40.0
color = Color(0.2, 0.2, 0.25, 1)
[node name="Label" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -400.0
offset_top = 40.0
offset_right = 400.0
offset_bottom = 120.0
horizontal_alignment = 1
autowrap_mode = 3
text = "..."
[node name="SkipButton" type="Button" parent="."]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -80.0
offset_top = -80.0
offset_right = 80.0
offset_bottom = -40.0
text = "Skip / Weiter"
[connection signal="pressed" from="SkipButton" to="." method="_on_skip_pressed"]

View file

@ -0,0 +1,59 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/massage_studio.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.7, 0.55, 0.5, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="massage_studio" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="CowMasseur" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -2)
dialog_key = "dialog_cow"
texture_path = "res://assets/sprites/placeholders/cow.png"
require_item_id = "snake"
consume_required = true
quest_flag = "snake_massaged"
achievement_id = "snake_massage"
[node name="Exit" parent="." instance=ExtResource("3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -10)

65
scenes/meadow.tscn Normal file
View file

@ -0,0 +1,65 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/meadow.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.4, 0.75, 0.3, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="meadow" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="Glitzo" parent="." instance=ExtResource("5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0.1, -1)
max_hp = 4
texture_path = "res://assets/sprites/placeholders/glitzo.png"
achievement_on_defeat = "first_glitzo"
[node name="Bear" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0.1, -3)
dialog_key = "dialog_bear"
texture_path = "res://assets/sprites/placeholders/bear.png"
[node name="MowerHazard" parent="." instance=ExtResource("6")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -5)
death_cause_key = "death_mower"
[node name="Exit" parent="." instance=ExtResource("3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -12)

70
scenes/moon.tscn Normal file
View file

@ -0,0 +1,70 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/moon.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.75, 0.75, 0.7, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="moon" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="CheeseSeller" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 0.1, -1)
dialog_key = "dialog_cheese"
texture_path = "res://assets/sprites/placeholders/cheese.png"
give_item_id = "cheese"
[node name="SwordStone" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0.1, -1)
dialog_key = "dialog_cheese"
texture_path = "res://assets/sprites/placeholders/apple.png"
give_item_id = "moon_sword"
[node name="FakeBowser" parent="." instance=ExtResource("5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -4)
max_hp = 8
texture_path = "res://assets/sprites/placeholders/bowser.png"
defeat_flag = "bowser_defeated"
achievement_on_defeat = "beat_bowser"
[node name="Exit" parent="." instance=ExtResource("3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -12)
require_flag = "bowser_defeated"

View file

@ -0,0 +1,8 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/billboard/billboard_sprite.gd" id="1"]
[node name="BillboardSprite" type="Node3D"]
script = ExtResource("1")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]

11
scenes/prefabs/enemy.tscn Normal file
View file

@ -0,0 +1,11 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Script" path="res://scripts/combat/hittable.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/billboard_sprite.tscn" id="2"]
[node name="Enemy" type="Node3D"]
script = ExtResource("1")
max_hp = 4
[node name="BillboardSprite" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)

View file

@ -0,0 +1,15 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Script" path="res://scripts/combat/exit_trigger.gd" id="1"]
[sub_resource type="BoxShape3D" id="BoxShape3D_1"]
size = Vector3(2.5, 2, 2.5)
[node name="ExitTrigger" type="Area3D"]
collision_layer = 0
collision_mask = 2
script = ExtResource("1")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("BoxShape3D_1")

View file

@ -0,0 +1,15 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Script" path="res://scripts/combat/hazard_zone.gd" id="1"]
[sub_resource type="BoxShape3D" id="BoxShape3D_1"]
size = Vector3(2.0, 2, 2.0)
[node name="HazardZone" type="Area3D"]
collision_layer = 0
collision_mask = 2
script = ExtResource("1")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("BoxShape3D_1")

10
scenes/prefabs/npc.tscn Normal file
View file

@ -0,0 +1,10 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Script" path="res://scripts/combat/interactable_npc.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/billboard_sprite.tscn" id="2"]
[node name="NPC" type="Node3D"]
script = ExtResource("1")
[node name="BillboardSprite" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)

View file

@ -0,0 +1,20 @@
[gd_scene load_steps=4 format=3]
[ext_resource type="Script" path="res://scripts/player/player_controller.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/billboard_sprite.tscn" id="2"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1"]
radius = 0.35
height = 1.0
[node name="Player" type="CharacterBody3D"]
collision_layer = 2
collision_mask = 1
script = ExtResource("1")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
shape = SubResource("CapsuleShape3D_1")
[node name="BillboardSprite" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)

57
scenes/shoe_boss.tscn Normal file
View file

@ -0,0 +1,57 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/shoe_boss.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.25, 0.2, 0.15, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="shoe_boss" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="Reif" parent="." instance=ExtResource("5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -2)
max_hp = 5
texture_path = "res://assets/sprites/placeholders/shoe.png"
defeat_flag = "reif_defeated"
[node name="Exit" parent="." instance=ExtResource("3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -12)
require_flag = "reif_defeated"

84
scenes/title_screen.tscn Normal file
View file

@ -0,0 +1,84 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/levels/title_screen.gd" id="1"]
[node name="TitleScreen" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1")
[node name="Bg" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.08, 0.05, 0.12, 1)
[node name="Rain" type="Node2D" parent="."]
[node name="Title" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -360.0
offset_top = 80.0
offset_right = 360.0
offset_bottom = 140.0
grow_horizontal = 2
theme_override_font_sizes/font_size = 40
horizontal_alignment = 1
text = "Explosion der Spiele"
[node name="Subtitle" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -360.0
offset_top = 140.0
offset_right = 360.0
offset_bottom = 180.0
grow_horizontal = 2
horizontal_alignment = 1
text = "Pay2Win"
[node name="Bubble" type="Label" parent="."]
layout_mode = 0
offset_left = 40.0
offset_top = 500.0
offset_right = 420.0
offset_bottom = 620.0
autowrap_mode = 3
text = "..."
[node name="Disturbed" type="ColorRect" parent="."]
layout_mode = 0
offset_left = 40.0
offset_top = 620.0
offset_right = 120.0
offset_bottom = 700.0
color = Color(0.7, 0.7, 0.7, 1)
[node name="StartButton" type="Button" parent="."]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -120.0
offset_top = -120.0
offset_right = 120.0
offset_bottom = -70.0
grow_horizontal = 2
grow_vertical = 0
text = "Starten"
[connection signal="pressed" from="StartButton" to="." method="_on_start_pressed"]

197
scenes/ui/ui_root.tscn Normal file
View file

@ -0,0 +1,197 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/ui/ui_root.gd" id="1"]
[node name="UIRoot" type="CanvasLayer"]
process_mode = 3
layer = 100
script = ExtResource("1")
[node name="HUD" type="Control" parent="."]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="HpLabel" type="Label" parent="HUD"]
layout_mode = 0
offset_left = 16.0
offset_top = 12.0
offset_right = 200.0
offset_bottom = 40.0
text = "HP 5/5"
[node name="ToastLabel" type="Label" parent="."]
visible = false
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -280.0
offset_top = 48.0
offset_right = 280.0
offset_bottom = 88.0
grow_horizontal = 2
horizontal_alignment = 1
text = "Achievement"
[node name="DialogPanel" type="PanelContainer" parent="."]
visible = false
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -420.0
offset_top = -140.0
offset_right = 420.0
offset_bottom = -24.0
grow_horizontal = 2
grow_vertical = 0
[node name="Margin" type="MarginContainer" parent="DialogPanel"]
layout_mode = 2
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 16
theme_override_constants/margin_bottom = 12
[node name="DialogLabel" type="Label" parent="DialogPanel/Margin"]
layout_mode = 2
autowrap_mode = 3
text = "..."
[node name="BurgerOverlay" type="ColorRect" parent="."]
visible = false
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.05, 0.02, 0.02, 0.85)
[node name="BurgerLabel" type="Label" parent="BurgerOverlay"]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -320.0
offset_top = -40.0
offset_right = 320.0
offset_bottom = 40.0
grow_horizontal = 2
grow_vertical = 2
horizontal_alignment = 1
vertical_alignment = 1
text = "Burger!"
[node name="PausePanel" type="PanelContainer" parent="."]
process_mode = 3
visible = false
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -280.0
offset_top = -300.0
offset_right = 280.0
offset_bottom = 300.0
grow_horizontal = 2
grow_vertical = 2
[node name="Margin" type="MarginContainer" parent="PausePanel"]
layout_mode = 2
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 16
theme_override_constants/margin_bottom = 12
[node name="VBox" type="VBoxContainer" parent="PausePanel/Margin"]
layout_mode = 2
theme_override_constants/separation = 6
[node name="TitleLabel" type="Label" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Einstellungen"
[node name="ResumeButton" type="Button" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Zurück"
[node name="PointsLabel" type="Label" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Dresslerpunkte: 0"
[node name="ShopTitle" type="Label" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Shop"
[node name="BuyPointButton" type="Button" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "1€"
[node name="BuySunglassesButton" type="Button" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Sunglasses"
[node name="BuyTicketButton" type="Button" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Ticket"
[node name="BuyMilkButton" type="Button" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Milk"
[node name="BuyTiramisuButton" type="Button" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Tiramisu"
[node name="SfxLabel" type="Label" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "SFX"
[node name="SfxSlider" type="HSlider" parent="PausePanel/Margin/VBox"]
layout_mode = 2
max_value = 1.0
step = 0.05
value = 1.0
[node name="MusicLabel" type="Label" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Music locked"
[node name="MusicSlider" type="HSlider" parent="PausePanel/Margin/VBox"]
layout_mode = 2
editable = false
max_value = 1.0
value = 1.0
[node name="LangLabel" type="Label" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Language"
[node name="LangOption" type="OptionButton" parent="PausePanel/Margin/VBox"]
layout_mode = 2
[node name="InventoryLabel" type="Label" parent="PausePanel/Margin/VBox"]
layout_mode = 2
text = "Inventar"
[node name="InventoryList" type="ItemList" parent="PausePanel/Margin/VBox"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
[connection signal="pressed" from="PausePanel/Margin/VBox/ResumeButton" to="." method="_on_resume_pressed"]
[connection signal="pressed" from="PausePanel/Margin/VBox/BuyPointButton" to="." method="_on_buy_point"]
[connection signal="pressed" from="PausePanel/Margin/VBox/BuySunglassesButton" to="." method="_on_buy_sunglasses"]
[connection signal="pressed" from="PausePanel/Margin/VBox/BuyTicketButton" to="." method="_on_buy_ticket"]
[connection signal="pressed" from="PausePanel/Margin/VBox/BuyMilkButton" to="." method="_on_buy_milk"]
[connection signal="pressed" from="PausePanel/Margin/VBox/BuyTiramisuButton" to="." method="_on_buy_tiramisu"]
[connection signal="value_changed" from="PausePanel/Margin/VBox/SfxSlider" to="." method="_on_sfx_changed"]
[connection signal="value_changed" from="PausePanel/Margin/VBox/MusicSlider" to="." method="_on_music_changed"]
[connection signal="item_selected" from="PausePanel/Margin/VBox/LangOption" to="." method="_on_lang_selected"]
[connection signal="item_activated" from="PausePanel/Margin/VBox/InventoryList" to="." method="_on_inventory_activated"]

70
scenes/village.tscn Normal file
View file

@ -0,0 +1,70 @@
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://scripts/levels/village.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/player.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/exit_trigger.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/npc.tscn" id="4"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemy.tscn" id="5"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazard.tscn" id="6"]
[sub_resource type="BoxMesh" id="BoxMesh_ground"]
size = Vector3(40, 0.2, 40)
[sub_resource type="StandardMaterial3D" id="Mat_ground"]
albedo_color = Color(0.35, 0.7, 0.35, 1)
shading_mode = 0
[sub_resource type="BoxShape3D" id="BoxShape_ground"]
size = Vector3(40, 0.2, 40)
[node name="village" type="Node3D"]
script = ExtResource("1")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866, -0.354, 0.354, 0, 0.707, 0.707, -0.5, -0.612, 0.612, 0, 8, 0)
shadow_enabled = false
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707, 0.707, 0, -0.707, 0.707, 0, 12, 12)
current = true
fov = 50.0
[node name="Ground" type="StaticBody3D" parent="."]
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
mesh = SubResource("BoxMesh_ground")
surface_material_override/0 = SubResource("Mat_ground")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
shape = SubResource("BoxShape_ground")
[node name="Player" parent="." instance=ExtResource("2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 4)
[node name="NPC_HongkongFan" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 0.1, 0)
dialog_key = "dialog_hongkong_npc"
texture_path = "res://assets/sprites/placeholders/npc.png"
give_item_id = "snake"
[node name="House" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0.1, -2)
dialog_key = "dialog_hongkong_npc"
texture_path = "res://assets/sprites/placeholders/house.png"
[node name="Dentist" parent="." instance=ExtResource("4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0.1, 2)
dialog_key = "dialog_hongkong_npc"
texture_path = "res://assets/sprites/placeholders/npc.png"
give_item_id = "tooth"
quest_flag = "teeth_stolen"
achievement_id = "tooth_millionaire"
[node name="Exit" parent="." instance=ExtResource("3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, -10)

View file

@ -0,0 +1,57 @@
extends Node
## Achievement unlock + persistence.
signal unlocked(achievement_id: String, title: String)
var _defs: Array = []
var _unlocked: Dictionary = {} # id -> true
func _ready() -> void:
_load_defs()
func _load_defs() -> void:
var file := FileAccess.open("res://data/achievements.json", FileAccess.READ)
if file == null:
push_error("Missing achievements.json")
return
var parsed: Variant = JSON.parse_string(file.get_as_text())
if typeof(parsed) == TYPE_ARRAY:
_defs = parsed
func unlock(achievement_id: String) -> void:
if _unlocked.get(achievement_id, false):
return
_unlocked[achievement_id] = true
var title := achievement_id
for entry in _defs:
if str(entry.get("id", "")) == achievement_id:
title = Locale.tr_key(str(entry.get("title_key", achievement_id)))
break
unlocked.emit(achievement_id, title)
_check_all()
GameState.save_game()
func is_unlocked(achievement_id: String) -> bool:
return bool(_unlocked.get(achievement_id, false))
func get_defs() -> Array:
return _defs
func _check_all() -> void:
if _unlocked.get("all_burgers", false):
return
for entry in _defs:
var id := str(entry.get("id", ""))
if id == "all_burgers":
continue
if not _unlocked.get(id, false):
return
_unlocked["all_burgers"] = true
var title := Locale.tr_key("ach_all_title")
unlocked.emit("all_burgers", title)
func serialize() -> Dictionary:
return {"unlocked": _unlocked.duplicate()}
func deserialize(data: Dictionary) -> void:
_unlocked = data.get("unlocked", {}).duplicate()

View file

@ -0,0 +1,91 @@
extends Node
## Music / SFX / Voice + Burger-Cham every 120s on title/loading.
signal burger_cham_triggered
const BURGER_INTERVAL := 120.0
# TODO: Replace placeholder streams with real takes (see README asset list).
const MUSIC_TITLE := "res://assets/audio/placeholders/music_title.wav"
const SFX_EXPLOSION := "res://assets/audio/placeholders/sfx_explosion.wav"
const SFX_HIT := "res://assets/audio/placeholders/sfx_hit.wav"
const SFX_BURGER := "res://assets/audio/placeholders/sfx_burger.wav"
const VOICE_DE := "res://assets/audio/placeholders/voice_de.wav"
const VOICE_EN := "res://assets/audio/placeholders/voice_en.wav"
const VOICE_BY := "res://assets/audio/placeholders/voice_by.wav"
var _music: AudioStreamPlayer
var _sfx: AudioStreamPlayer
var _voice: AudioStreamPlayer
var _burger_timer: float = 0.0
var burger_cham_enabled: bool = true
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
_music = AudioStreamPlayer.new()
_music.bus = "Music"
_music.volume_db = 6.0 # intentionally loud
add_child(_music)
_sfx = AudioStreamPlayer.new()
_sfx.bus = "SFX"
add_child(_sfx)
_voice = AudioStreamPlayer.new()
_voice.bus = "Voice"
add_child(_voice)
func _process(delta: float) -> void:
if not burger_cham_enabled:
return
if GameState.current_scene_id != "title_screen":
return
_burger_timer += delta
if _burger_timer >= BURGER_INTERVAL:
_burger_timer = 0.0
trigger_burger_cham()
func play_title_music() -> void:
var stream := _load_stream(MUSIC_TITLE)
if stream == null:
return
_music.stream = stream
_music.volume_db = 6.0
if not _music.playing:
_music.play()
func stop_music() -> void:
_music.stop()
func play_sfx(path: String) -> void:
var stream := _load_stream(path)
if stream == null:
return
_sfx.stream = stream
_sfx.play()
func play_explosion() -> void:
play_sfx(SFX_EXPLOSION)
func play_hit() -> void:
play_sfx(SFX_HIT)
func play_language_voice(lang: String) -> void:
var path := VOICE_DE
match lang:
"en":
path = VOICE_EN
"by":
path = VOICE_BY
var stream := _load_stream(path)
if stream == null:
return
_voice.stream = stream
_voice.play()
func trigger_burger_cham() -> void:
play_sfx(SFX_BURGER)
burger_cham_triggered.emit()
func _load_stream(path: String) -> AudioStream:
if ResourceLoader.exists(path):
return load(path)
return null

View file

@ -0,0 +1,161 @@
extends Node
## Global game flow, HP, death, playtime, save/load.
## TODO: Expand save schema when real progression exists.
signal hp_changed(hp: int, max_hp: int)
signal died(cause_key: String)
signal scene_changed(scene_id: String)
const SAVE_PATH := "user://save.json"
const SCENE_ORDER: Array[String] = [
"title_screen",
"intro_cutscene",
"village",
"shoe_boss",
"meadow",
"massage_studio",
"farm_path",
"farm",
"moon",
"final_boss",
"end_screen",
]
const SCENE_PATHS := {
"title_screen": "res://scenes/title_screen.tscn",
"intro_cutscene": "res://scenes/intro_cutscene.tscn",
"village": "res://scenes/village.tscn",
"shoe_boss": "res://scenes/shoe_boss.tscn",
"meadow": "res://scenes/meadow.tscn",
"massage_studio": "res://scenes/massage_studio.tscn",
"farm_path": "res://scenes/farm_path.tscn",
"farm": "res://scenes/farm.tscn",
"moon": "res://scenes/moon.tscn",
"final_boss": "res://scenes/final_boss.tscn",
"end_screen": "res://scenes/end_screen.tscn",
"death_screen": "res://scenes/death_screen.tscn",
}
var current_scene_id: String = "title_screen"
var max_hp: int = 5
var hp: int = 5
var death_count: int = 0
var last_death_cause_key: String = "death_default"
var playtime_seconds: float = 0.0
var house_built: bool = false
var flags: Dictionary = {}
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
load_game()
func _process(delta: float) -> void:
if get_tree().paused:
return
if current_scene_id in ["title_screen", "death_screen"]:
return
playtime_seconds += delta
if playtime_seconds >= 3600.0:
Achievements.unlock("played_hour")
func goto_scene(scene_id: String) -> void:
if not SCENE_PATHS.has(scene_id):
push_error("Unknown scene: %s" % scene_id)
return
current_scene_id = scene_id
scene_changed.emit(scene_id)
if scene_id == "moon":
Achievements.unlock("moon_cheese")
save_game()
get_tree().change_scene_to_file(SCENE_PATHS[scene_id])
func goto_next() -> void:
var idx := SCENE_ORDER.find(current_scene_id)
if idx < 0:
goto_scene("village")
return
if idx >= SCENE_ORDER.size() - 1:
goto_scene("end_screen")
return
goto_scene(SCENE_ORDER[idx + 1])
func skip_to_final_boss() -> void:
Achievements.unlock("milk_skip")
flags["milk_skip_used"] = true
goto_scene("final_boss")
func start_new_game() -> void:
hp = max_hp
flags.clear()
house_built = false
goto_scene("intro_cutscene")
func heal_full() -> void:
hp = max_hp
hp_changed.emit(hp, max_hp)
func take_damage(amount: int = 1, cause_key: String = "death_default") -> void:
hp = max(0, hp - amount)
hp_changed.emit(hp, max_hp)
if hp <= 0:
die(cause_key)
func die(cause_key: String = "death_default") -> void:
last_death_cause_key = cause_key
death_count += 1
died.emit(cause_key)
if death_count >= 100:
Achievements.unlock("died_100")
save_game()
goto_scene("death_screen")
func set_flag(key: String, value: Variant = true) -> void:
flags[key] = value
save_game()
func get_flag(key: String, default_value: Variant = false) -> Variant:
return flags.get(key, default_value)
func save_game() -> void:
var data := {
"hp": hp,
"max_hp": max_hp,
"death_count": death_count,
"last_death_cause_key": last_death_cause_key,
"playtime_seconds": playtime_seconds,
"current_scene_id": current_scene_id,
"house_built": house_built,
"flags": flags,
"inventory": Inventory.serialize(),
"achievements": Achievements.serialize(),
"shop": Shop.serialize(),
"settings": Settings.serialize(),
}
var file := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if file == null:
push_warning("Could not write save file")
return
file.store_string(JSON.stringify(data, "\t"))
func load_game() -> void:
if not FileAccess.file_exists(SAVE_PATH):
return
var file := FileAccess.open(SAVE_PATH, FileAccess.READ)
if file == null:
return
var parsed: Variant = JSON.parse_string(file.get_as_text())
if typeof(parsed) != TYPE_DICTIONARY:
return
var data: Dictionary = parsed
hp = int(data.get("hp", max_hp))
max_hp = int(data.get("max_hp", max_hp))
death_count = int(data.get("death_count", 0))
last_death_cause_key = str(data.get("last_death_cause_key", "death_default"))
playtime_seconds = float(data.get("playtime_seconds", 0.0))
current_scene_id = str(data.get("current_scene_id", "title_screen"))
house_built = bool(data.get("house_built", false))
flags = data.get("flags", {})
Inventory.deserialize(data.get("inventory", {}))
Achievements.deserialize(data.get("achievements", {}))
Shop.deserialize(data.get("shop", {}))
Settings.deserialize(data.get("settings", {}))

View file

@ -0,0 +1,84 @@
extends Node
## Array-based inventory with item IDs from data/items.json.
signal inventory_changed
signal item_used(item_id: String)
var _items: Dictionary = {} # id -> count
var _defs: Dictionary = {}
func _ready() -> void:
_load_defs()
func _load_defs() -> void:
var file := FileAccess.open("res://data/items.json", FileAccess.READ)
if file == null:
push_error("Missing items.json")
return
var parsed: Variant = JSON.parse_string(file.get_as_text())
if typeof(parsed) != TYPE_ARRAY:
return
for entry in parsed:
if typeof(entry) == TYPE_DICTIONARY and entry.has("id"):
_defs[str(entry["id"])] = entry
func add_item(item_id: String, amount: int = 1) -> void:
if amount <= 0:
return
_items[item_id] = int(_items.get(item_id, 0)) + amount
inventory_changed.emit()
GameState.save_game()
func remove_item(item_id: String, amount: int = 1) -> bool:
var have := int(_items.get(item_id, 0))
if have < amount:
return false
have -= amount
if have <= 0:
_items.erase(item_id)
else:
_items[item_id] = have
inventory_changed.emit()
GameState.save_game()
return true
func has_item(item_id: String, amount: int = 1) -> bool:
return int(_items.get(item_id, 0)) >= amount
func get_count(item_id: String) -> int:
return int(_items.get(item_id, 0))
func get_all() -> Dictionary:
return _items.duplicate()
func use_item(item_id: String) -> bool:
if not has_item(item_id):
return false
match item_id:
"milk":
remove_item("milk", 1)
item_used.emit(item_id)
GameState.skip_to_final_boss()
return true
"death_ticket":
remove_item("death_ticket", 1)
Achievements.unlock("eaten_to_death")
item_used.emit(item_id)
return true
"tiramisu":
remove_item("tiramisu", 1)
var eaten := int(GameState.get_flag("tiramisu_kg", 0)) + 1
GameState.set_flag("tiramisu_kg", eaten)
if eaten >= 1000:
Achievements.unlock("tiramisu_tonne")
item_used.emit(item_id)
return true
_:
return false
func serialize() -> Dictionary:
return {"items": _items.duplicate()}
func deserialize(data: Dictionary) -> void:
_items = data.get("items", {}).duplicate()
inventory_changed.emit()

View file

@ -0,0 +1,35 @@
extends Node
## Multilingual strings DE/EN/BY with DE fallback.
signal locale_changed
var _strings: Dictionary = {}
func _ready() -> void:
reload()
func reload() -> void:
_strings = _load_lang("de")
var lang := Settings.language if Settings else "de"
if lang != "de":
var overlay := _load_lang(lang)
for key in overlay.keys():
_strings[key] = overlay[key]
locale_changed.emit()
func _load_lang(lang: String) -> Dictionary:
var path := "res://data/locales/%s.json" % lang
var file := FileAccess.open(path, FileAccess.READ)
if file == null:
push_warning("Missing locale: %s" % path)
return {}
var parsed: Variant = JSON.parse_string(file.get_as_text())
if typeof(parsed) == TYPE_DICTIONARY:
return parsed
return {}
func tr_key(key: String, args: Array = []) -> String:
var text := str(_strings.get(key, key))
if args.is_empty():
return text
return text % args

View file

@ -0,0 +1,79 @@
extends Node
## Persistent settings. Music cannot be turned down (satire).
signal settings_changed
const MUSIC_BUS := "Music"
const SFX_BUS := "SFX"
const VOICE_BUS := "Voice"
var language: String = "de"
var sfx_volume: float = 1.0
## Intentionally locked loud — slider is UI-disabled.
var music_volume: float = 1.0
func _ready() -> void:
_ensure_buses()
apply_audio()
# Locale reloads after both Settings + Locale exist (Locale._ready / deserialize).
func _ensure_buses() -> void:
_ensure_bus(MUSIC_BUS)
_ensure_bus(SFX_BUS)
_ensure_bus(VOICE_BUS)
func _ensure_bus(bus_name: String) -> void:
if AudioServer.get_bus_index(bus_name) != -1:
return
AudioServer.add_bus()
var idx := AudioServer.bus_count - 1
AudioServer.set_bus_name(idx, bus_name)
AudioServer.set_bus_send(idx, "Master")
func set_language(lang: String) -> void:
if lang not in ["de", "en", "by"]:
return
language = lang
Locale.reload()
AudioDirector.play_language_voice(lang)
settings_changed.emit()
GameState.save_game()
func set_sfx_volume(value: float) -> void:
sfx_volume = clampf(value, 0.0, 1.0)
apply_audio()
settings_changed.emit()
GameState.save_game()
func set_music_volume(_value: float) -> void:
## Satire lock: music stays maxed.
music_volume = 1.0
apply_audio()
settings_changed.emit()
func apply_audio() -> void:
_set_bus_linear(MUSIC_BUS, music_volume)
_set_bus_linear(SFX_BUS, sfx_volume)
_set_bus_linear(VOICE_BUS, sfx_volume)
func _set_bus_linear(bus_name: String, linear: float) -> void:
var idx := AudioServer.get_bus_index(bus_name)
if idx == -1:
return
var db := -80.0 if linear <= 0.0001 else 20.0 * log(linear) / log(10.0)
AudioServer.set_bus_volume_db(idx, db)
func serialize() -> Dictionary:
return {
"language": language,
"sfx_volume": sfx_volume,
"music_volume": 1.0,
}
func deserialize(data: Dictionary) -> void:
language = str(data.get("language", "de"))
sfx_volume = float(data.get("sfx_volume", 1.0))
music_volume = 1.0
Locale.reload()
apply_audio()
settings_changed.emit()

50
scripts/autoload/shop.gd Normal file
View file

@ -0,0 +1,50 @@
extends Node
## Mock Pay-to-Win shop. 1€ = 1 Dresslerpunkt (no real payments).
signal points_changed(points: int)
signal purchase_made(item_id: String)
var dressler_points: int = 0
const CATALOG := [
{"id": "buy_point", "cost": 0, "mock_euro": true},
{"id": "sunglasses", "cost": 5, "gives": "sunglasses"},
{"id": "death_ticket", "cost": 3, "gives": "death_ticket"},
{"id": "milk", "cost": 10, "gives": "milk"},
{"id": "tiramisu", "cost": 1, "gives": "tiramisu"},
]
func buy_mock_euro_point() -> void:
## Satire: pretend the user paid 1€.
dressler_points += 1
points_changed.emit(dressler_points)
GameState.save_game()
func purchase(entry_id: String) -> bool:
if entry_id == "buy_point":
buy_mock_euro_point()
return true
for entry in CATALOG:
if str(entry["id"]) != entry_id:
continue
var cost := int(entry.get("cost", 0))
if dressler_points < cost:
return false
dressler_points -= cost
var gives := str(entry.get("gives", ""))
if gives != "":
Inventory.add_item(gives, 1)
if gives == "sunglasses":
Achievements.unlock("cool_sunglasses")
points_changed.emit(dressler_points)
purchase_made.emit(entry_id)
GameState.save_game()
return true
return false
func serialize() -> Dictionary:
return {"dressler_points": dressler_points}
func deserialize(data: Dictionary) -> void:
dressler_points = int(data.get("dressler_points", 0))
points_changed.emit(dressler_points)

View file

@ -0,0 +1,37 @@
extends Node3D
class_name BillboardSprite
## 2D sprite on a 3D plane that always faces the camera (Mario-tree style).
## TODO: Swap placeholder textures for real pixel art.
@export var texture: Texture2D
@export var pixel_size: float = 0.05
@export var modulate_color: Color = Color.WHITE
@onready var _mesh_instance: MeshInstance3D = $MeshInstance3D
func _ready() -> void:
_apply()
func set_texture(tex: Texture2D) -> void:
texture = tex
_apply()
func _apply() -> void:
if _mesh_instance == null:
return
var plane := PlaneMesh.new()
plane.size = Vector2(1.0, 1.0)
_mesh_instance.mesh = plane
var mat := StandardMaterial3D.new()
mat.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR
mat.alpha_scissor_threshold = 0.1
mat.albedo_color = modulate_color
if texture != null:
mat.albedo_texture = texture
var w := float(texture.get_width()) * pixel_size
var h := float(texture.get_height()) * pixel_size
plane.size = Vector2(w, h)
_mesh_instance.material_override = mat

View file

@ -0,0 +1,25 @@
extends Area3D
class_name ExitTrigger
## Walk into this volume to advance or jump to a scene.
@export var next_scene_id: String = ""
@export var use_goto_next: bool = true
@export var require_flag: String = ""
func _ready() -> void:
body_entered.connect(_on_body_entered)
collision_layer = 0
collision_mask = 2 # player layer
func _on_body_entered(body: Node3D) -> void:
if not body.is_in_group("player"):
return
if require_flag != "" and not GameState.get_flag(require_flag, false):
var ui := get_node_or_null("/root/UIRoot")
if ui and ui.has_method("show_dialog"):
ui.show_dialog(Locale.tr_key("exit_hint") + " (?)")
return
if use_goto_next and next_scene_id == "":
GameState.goto_next()
elif next_scene_id != "":
GameState.goto_scene(next_scene_id)

View file

@ -0,0 +1,24 @@
extends Area3D
class_name HazardZone
## Damages the player on enter.
@export var damage: int = 1
@export var death_cause_key: String = "death_default"
@export var cooldown: float = 1.0
var _ready_to_hurt: bool = true
func _ready() -> void:
body_entered.connect(_on_body_entered)
collision_layer = 0
collision_mask = 2
func _on_body_entered(body: Node3D) -> void:
if not _ready_to_hurt:
return
if not body.is_in_group("player"):
return
_ready_to_hurt = false
GameState.take_damage(damage, death_cause_key)
await get_tree().create_timer(cooldown).timeout
_ready_to_hurt = true

View file

@ -0,0 +1,43 @@
extends Node3D
class_name HittableEntity
## Generic enemy / boss stub with hit points.
signal defeated
signal hit(remaining_hp: int)
@export var max_hp: int = 4
@export var texture_path: String = "res://assets/sprites/placeholders/enemy.png"
@export var defeat_flag: String = ""
@export var achievement_on_defeat: String = ""
var hp: int = 4
var _alive: bool = true
@onready var _billboard: BillboardSprite = $BillboardSprite
func _ready() -> void:
hp = max_hp
add_to_group("enemy")
add_to_group("interactable")
if _billboard:
var tex: Texture2D = load(texture_path)
if tex:
_billboard.set_texture(tex)
func take_hit(amount: int = 1) -> void:
if not _alive:
return
hp = max(0, hp - amount)
hit.emit(hp)
AudioDirector.play_hit()
if hp <= 0:
_alive = false
if defeat_flag != "":
GameState.set_flag(defeat_flag, true)
if achievement_on_defeat != "":
Achievements.unlock(achievement_on_defeat)
defeated.emit()
queue_free()
func on_interact(_player: Node) -> void:
take_hit(1)

View file

@ -0,0 +1,43 @@
extends Node3D
class_name InteractableNPC
## Billboard NPC with dialog key and optional quest hook.
signal interacted(npc: InteractableNPC)
@export var dialog_key: String = "dialog_hongkong_npc"
@export var texture_path: String = "res://assets/sprites/placeholders/npc.png"
@export var give_item_id: String = ""
@export var require_item_id: String = ""
@export var consume_required: bool = false
@export var quest_flag: String = ""
@export var achievement_id: String = ""
@export var on_success_goto: String = ""
@onready var _billboard: BillboardSprite = $BillboardSprite
func _ready() -> void:
add_to_group("interactable")
if _billboard:
var tex: Texture2D = load(texture_path)
if tex:
_billboard.set_texture(tex)
func on_interact(_player: Node) -> void:
interacted.emit(self)
var ui := get_node_or_null("/root/UIRoot")
if ui and ui.has_method("show_dialog"):
ui.show_dialog(Locale.tr_key(dialog_key))
if require_item_id != "" and not Inventory.has_item(require_item_id):
return
if require_item_id != "" and consume_required:
Inventory.remove_item(require_item_id, 1)
if give_item_id != "":
Inventory.add_item(give_item_id, 1)
if quest_flag != "":
GameState.set_flag(quest_flag, true)
if quest_flag == "house_built":
GameState.house_built = true
if achievement_id != "":
Achievements.unlock(achievement_id)
if on_success_goto != "":
GameState.goto_scene(on_success_goto)

View file

@ -0,0 +1,36 @@
extends Control
## Blinky death screen with cause text + skull every 10s.
## TODO: Final flashy death art.
@onready var title: Label = $Title
@onready var cause: Label = $Cause
@onready var cont: Button = $ContinueButton
@onready var skull: ColorRect = $Skull
var _blink: float = 0.0
var _skull_timer: float = 0.0
func _ready() -> void:
GameState.current_scene_id = "death_screen"
_refresh()
Locale.locale_changed.connect(_refresh)
skull.visible = false
func _refresh() -> void:
title.text = Locale.tr_key("death_title")
cause.text = Locale.tr_key(GameState.last_death_cause_key)
cont.text = Locale.tr_key("death_continue")
func _process(delta: float) -> void:
_blink += delta
modulate = Color(1, 0.4 + 0.6 * abs(sin(_blink * 6.0)), 0.4 + 0.6 * abs(sin(_blink * 4.0)))
_skull_timer += delta
if _skull_timer >= 10.0:
_skull_timer = 0.0
skull.visible = true
await get_tree().create_timer(1.0).timeout
skull.visible = false
func _on_continue_pressed() -> void:
GameState.heal_full()
GameState.goto_scene("title_screen")

Some files were not shown because too many files have changed in this diff Show more