Files
Wackelpeter/AGENTS.md
T

7.5 KiB
Raw Blame History

AGENTS.md

Git-Regeln

  • Git-Commits nur auf explizite Anforderung des Users. Niemals eigenständig committen.
  • Vor einem Commit: git status, git diff und git log --oneline -10 prüfen; nur beabsichtigte Dateien stagen, keine Secrets.
  • Commit-Message kurz und im Stil des Repos (bisher englisch, imperative).

Build & Verifikation

cd web
npx tsc --noEmit    # Typcheck (Pflicht nach Änderungen)
npm run build       # tsc + vite build → web/dist
npm run dev         # Dev-Server mit HMR
  • Stack: Three.js 0.170 + TypeScript + Vite. Keine weiteren Laufzeit-Dependencies (kein Postprocessing verfügbar — Screen-Effekte als CSS-Overlays).
  • Makefile: make web/make dev/make build (Container-Build via Podman).
  • UI-Sprache: Deutsch (Skill-Namen, Level-up-Karten); Code-Kommentare bisher gemischt, Git-Messages englisch.

Architektur (web/src)

Datei Inhalt
Game.ts Zentrale Klasse: State-Machine, Render-Loop (animate()), Input, UI-Verdrahtung, Spawning, Partikel-/Effekt-Arrays (bloodParticles, traps, gasClouds, slashEffects, projectiles), Hotbar
Player.ts Blob-Spieler: Movement, Sprung, damage()/heal() (Schild-Absorption), Stats (applySkillSystem), Hurt-Feedback (Flash + Blut via game.onPlayerHurt)
Enemy.ts Abstrakte Basis: body (THREE.Group), takeDamage (Sound + XP/Lifesteal via game.onDamageDealt), Fade-Death, Felder knockback, rootTime
Skills.ts Alle Skill-Definitionen (SKILLS-Array: id, name, type, maxLevel, describe) + Level-Wertetabellen (z. B. auraRadius(level)). SkillSystem = XP/Level/Angebote. HOTBAR_SKILLS = aktive Skills in Erwerbs-Reihenfolge
SkillIcons.ts SVG-Icons (64×64, Template-Strings) für die Level-up-Karten
Weapon.ts Basis für aktive Skills: cooldown1-3 + reduceCooldowns()
Sword.ts / Staff.ts Linksklick-Schwert (Bogen-Trefferzone via SwordTrail.ts), Rechtsklick-Feuerball + Teleport
ChainLightning.ts Kettenblitz (Q-Slot)
Trapper.ts Falle (skill1) + Gaswolke (skill2)
Aura.ts / Boomerang.ts Passive Skills, lesen Level live aus game.skillSystem
Trap.ts / GasCloud.ts Platzierbare Effekte; update(dt, game): boolean (false = entfernen), dispose()
Golem.ts / Krake.ts / Minotaurus.ts / SpiderQueen.ts / FrostGiant.ts / Necromancer.ts / Dragon.ts Bosse (Welle 10 / 20 / 30 / 40 / 50 / 60 / 70), registriert in Game.bossWaves ({name, spawn}). Krake: stationär (spawnAtCenter), Tinte (InkBlob.ts/InkPuddle.ts: Slow + DoT), Tentakel-Griff (5 rote Boden-Marken um den Oktopus → Pull via player.pullTime), Sweep (roter Tentakel kreist um den Oktopus, überspringbar), Enrage <30% HP. Spinnenkönigin: stationär, Netze (WebPatch.ts: fesselt via player.rootTime), Spinngeschwister (Mini-Spiders), Säuresalven. Frostriese: Eispanzer (70% Schadensreduktion, armorHp, bricht + Stagger, regeneriert nach Timer), Eisberst-Ring (nacheinander explodierende Marken), Eiszapfen-Regen, Stampf (überspringbar). Erz-Nekromant: schwebend, Teleport mit Glow-Ring-Telegraf + Ankunftssalve, Beschwörungen mit wachsendem Boden-Glow. Drache: fliegt und ist nur im Lande-Zustand verletzbar (takeDamage-Override blockt im Flug), Sturzangriff mit wachsendem Schatten-Telegraf, Feuerspeer (Telegraf-Bogen, überspringbar), Feuerbälle im Flug, Enrage mit Doppel-Sturz. Minotaurus: nur von hinten verletzbar (vorn „BLOCK"), Turn-Lag, Dash → Wand-Stun (Sterne) + Wandrisse, Axt-Schwung (überspringbar), Sprung+Stampf (Stun player.stunTime + FallingRock.ts), Enrage <40% (Doppel-Dash, Kamera-Shake)
InkBlob.ts / InkPuddle.ts / FallingRock.ts Boss-Effekte: Tintenprojektil → Pfütze mit Slow (player.slowTimer) + DoT + game.triggerInkVignette(); fallender Fels mit Schatten-Telegraf
*Enemy*.ts (Spider, Ghost, Frog, Bat, BeeSwarm, Slime, Brute, Crab, Turret) Prozedurale Gegner-Modelle; je update(dt, game)
SoundManager.ts Vorab geladene .ogg-Buffer (playSound(name, volume)); Sounds: damage, spawn, fireball, explosion, teleport, gameover, sword_hit1, spiderwalking
MeshLoader.ts loadGLB(name)web/public/models/*.glb; Texturen in web/public/textures/

Wichtige Konventionen & Fallstricke

  • Blickrichtung = lokale +Z: Three.js Object3D.lookAt richtet +Z zum Ziel aus. Augen/Gesichter von Gegnern und Spieler müssen auf +Z liegen (sonst laufen sie rückwärts — war schon mal ein Bug bei Frosch/Golem/Brute/Slime/Bat/Crab).
  • UI-Overlays: Alle Panels liegen absolut in #ui-overlay (pointer-events: none). Versteckte Panels brauchen drei Dinge: opacity: 0 + pointerEvents: none + visibility: hidden. Achtung: .menu-button { pointer-events: auto } übersteuert pointer-events: none des Parents — ohne visibility: hidden sind unsichtbare Buttons klickbar (verursachte „stille Neustarts" mitten im Spiel). Neues Panel → immer visibility mitsetzen.
  • State-Machine: menu | playing | levelup | gameover + Flag paused (ESC). animate() rendert bei Pause nur UI. showMenu() hat einen Guard (nur aus menu/gameover/pause).
  • Hotbar: Aktive Skills (Kettenblitz, Teleport, Falle, Gaswolke) werden beim Erwerb auf Q, E, 1, 2, 3, 4 verteilt (HOTBAR_KEY_CODES). Dispatch über triggerHotbarSkill(). Cooldown-Labels zeigen Taste + Name (syncSkillVisibility).
  • Skill-Arten: Aktive = Weapon-Subklassen mit Cooldowns (UI-Balken in index.html, Update in updateUI). Passive = Stats in Player.applySkillSystem ODER Game-seitige Objekte (Aura/Boomerang), die in syncSkillVisibility erzeugt/zersstört und im Loop geupdated werden.
  • Effekt-Lifecycle: Neue Effekte mit eigenem update(dt, game)-Rückgabewert (false = aufräumen) und dispose(); in Game.resetWorld() ALLES aufräumen (sonst Geister-Objekte nach „Nochmal spielen").
  • Gegner-Festhalten: enemy.rootTime > 0 friert enemy.update() ein (siehe Game.updateEnemies) — so funktioniert die Falle, ohne jede Gegner-Klasse anzufassen.
  • Boss-Wellen & Boss-Modus: Game.bossWaves (Map: Welle → {name, spawn}) steuert Boss-Wellen (10, 20 …) — Boss-Welle startet erst, wenn die Arena geräumt ist (enemies.every(e => e.dead)); nach Boss-Tod läuft die nächste normale Welle an. Cheat-Code „idkfa" im Hauptmenü blendet den Boss-Modus ein (#boss-mode, Buttons werden aus bossWaves erzeugt) → startBossFight(wave) mit Zufalls-Skill-Boost (grantRandomSkills). spawnAtCenter-Flag lässt stationäre Bosse in der Arena-Mitte spawnen. Effekte, die ein Boss selbst verwaltet (Tinten-Blobs/-Pfützen, Ringe), müssen in onDeath/dispose aufgeräumt werden (Dead-Enemy-Removal ruft dispose NICHT auf).
  • Spieler-Status-Effekte (in Player.ts + Game.updatePlayerMovement): slowTimer/slowFactor (Tinten-Slow, Lila-Tönung), pullTime/pullDir (Tentakel-Griff: Eingabe wird ignoriert, Geschwindigkeit = Pull-Richtung), stunTime (Stampf-Schockwelle: keine Eingaben, gelbe Sterne kreisen um den Kopf) und rootTime (Netz-Fessel: keine Bewegung, Angriffe bleiben möglich). Alles wird in Player.reset() zurückgesetzt.
  • Kamera-Shake: game.triggerCameraShake(duration, amp) → in animate() wird die Kameraposition kurz zufällig versetzt (kein Postprocessing nötig).
  • Kontaktschaden (Gegner berührt Spieler) läuft in Game.ts (Close-Encounter-Loop) mit contactDps * dt; Dornen-Skill ebendort als Gegenschlag.
  • playSound erzeugt pro Aufruf einen AudioBufferSourceNode — bei hochfrequenten Effekten (Aura-Ticks, Bumerang-Hits) ggf. throtteln.