Initial commit: DOOM x MATRIX System Failure text adventure
Modulares Python-Textadventure mit Kampfsystem (Stagger, Bullet Time, Glory Kills, Hacking-Skill-Checks), Hub-Raum-Navigation, prozeduralen Encountern, 3 Charakterklassen, mehreren Enden und Story-Elementen (weißer Hase, Orakel, schwarze-Katze-Deja-vu-Glitch).
This commit is contained in:
commit
46116f7f4b
25 files changed
+2594
No files matched your search
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Prozedurale Raum- und Encounter-Generierung (seed-basiert, teilbare Runs)."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
|
||||
from doom_matrix.engine.ui import Colors, slow_print
|
||||
from doom_matrix.entities.enemy import Enemy
|
||||
from doom_matrix.entities.items import Item
|
||||
from doom_matrix.systems.combat import run_combat
|
||||
from doom_matrix.world.rooms import Room
|
||||
|
||||
_ITEMS_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "items.json")
|
||||
_ITEM_REGISTRY = None
|
||||
|
||||
|
||||
def load_item_registry():
|
||||
global _ITEM_REGISTRY
|
||||
if _ITEM_REGISTRY is None:
|
||||
with open(_ITEMS_PATH, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
_ITEM_REGISTRY = {d["id"]: Item.from_dict(d) for d in data}
|
||||
return _ITEM_REGISTRY
|
||||
|
||||
|
||||
THEMES = {
|
||||
"mars": {
|
||||
"flavor": [
|
||||
"Ein verlassenes Labor. Monitore flackern mit UAC-Logos.",
|
||||
"Ein Korridor voller Kratzspuren -- etwas wurde hier durch die Wand geschleift.",
|
||||
"Ein Lueftungsschacht pfeift roten Marsstaub durch den Raum.",
|
||||
"Kisten mit der Aufschrift 'ARGENT ENERGY -- NICHT OEFFNEN' stapeln sich hier.",
|
||||
],
|
||||
"enemies": ["imp", "pinky"],
|
||||
},
|
||||
"matrix": {
|
||||
"flavor": [
|
||||
"Ein Buerokomplex aus reinem Code. Die Decke besteht aus Zahlenkolonnen.",
|
||||
"Ein U-Bahn-Gleis, das nirgendwo hinfuehrt. Der naechste Zug kommt nie.",
|
||||
"Ein Zimmer voller alter Kabel-Telefone -- Fluchtpunkte, die niemand mehr nutzt.",
|
||||
"Ein endloser Flur mit identischen Tueren. Tuer 101 wiederholt sich dreimal.",
|
||||
],
|
||||
"enemies": ["agent_imp", "agent_clone"],
|
||||
},
|
||||
"hell": {
|
||||
"flavor": [
|
||||
"Ein Serverraum, in dem die Racks aus Rippen bestehen.",
|
||||
"Ein See aus geschmolzenem Silizium, der im Rhythmus eines Herzschlags glueht.",
|
||||
"Kabelstraenge wachsen wie Wurzeln aus der Wand, angeschlossen an nichts.",
|
||||
"Ein Altar aus Motherboards, umgeben von erkaltetem Magma.",
|
||||
],
|
||||
"enemies": ["cacodemon", "revenant"],
|
||||
},
|
||||
}
|
||||
|
||||
LOOT_TABLE = ["medkit", "armor_shard", "ammo_pack", "praetor_token", "logfile"]
|
||||
|
||||
|
||||
def _room_id(theme, index):
|
||||
return f"{theme}_gen_{index}"
|
||||
|
||||
|
||||
def _encounter_on_enter(theme, rng):
|
||||
def _on_enter(player, ctx):
|
||||
enemy_id = rng.choice(THEMES[theme]["enemies"])
|
||||
enemy = Enemy.create(enemy_id)
|
||||
slow_print(f"\n{Colors.RED}Ein {enemy.name} versperrt dir den Weg!{Colors.END}")
|
||||
victory = run_combat(player, [enemy], title=f"KAMPF: {enemy.name}", rng=rng)
|
||||
if not victory:
|
||||
return ("leave", "game_over")
|
||||
return None
|
||||
return _on_enter
|
||||
|
||||
|
||||
def _loot_on_enter(theme, rng, item_registry):
|
||||
def _on_enter(player, ctx):
|
||||
if rng.random() < 0.7:
|
||||
item_id = rng.choice(LOOT_TABLE)
|
||||
item = item_registry.get(item_id)
|
||||
if item:
|
||||
player.inventory.add(item)
|
||||
slow_print(f"{Colors.GREEN}Du findest: {item.name}{Colors.END}")
|
||||
else:
|
||||
slow_print(f"{Colors.DIM}Nichts von Wert hier. Nur Staub und Stille.{Colors.END}")
|
||||
return None
|
||||
return _on_enter
|
||||
|
||||
|
||||
def generate_filler_rooms(theme, count, rng, seed_offset=0):
|
||||
"""Erzeugt eine Kette prozeduraler Raeume fuer einen Layer (linear verkettet,
|
||||
zum Einhaengen zwischen festen Story-Raeumen)."""
|
||||
item_registry = load_item_registry()
|
||||
rooms = []
|
||||
for i in range(count):
|
||||
idx = i + seed_offset
|
||||
room_id = _room_id(theme, idx)
|
||||
flavor = rng.choice(THEMES[theme]["flavor"])
|
||||
is_encounter = rng.random() < 0.55
|
||||
on_enter = _encounter_on_enter(theme, rng) if is_encounter else _loot_on_enter(theme, rng, item_registry)
|
||||
room = Room(
|
||||
room_id=room_id,
|
||||
name=f"Generierter Raum {idx + 1}",
|
||||
description=flavor,
|
||||
on_enter=on_enter,
|
||||
)
|
||||
rooms.append(room)
|
||||
return rooms
|
||||
|
||||
|
||||
def chain_rooms(rooms, entry_label="Weiter"):
|
||||
"""Verkettet eine Liste von Raeumen linear ueber 'exits'."""
|
||||
for i, room in enumerate(rooms):
|
||||
if i + 1 < len(rooms):
|
||||
room.exits[entry_label] = rooms[i + 1].room_id
|
||||
return rooms
|
||||
Reference in new issue
Block a user