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:
fedora_openclaw committed 2026-07-05 18:56:44 +02:00
commit 46116f7f4b
25 files changed
+2594

No files matched your search

+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Generische Kampf-Engine: Stagger, Bullet Time (Focus), Glory Kills, Hacking."""
import random
from doom_matrix.engine.ui import Colors, slow_print, dramatic_pause, bar, menu
from doom_matrix.engine import cheats
from doom_matrix.entities.items import type_multiplier
from doom_matrix.entities.enemy import Enemy
from doom_matrix.systems import hacking
GLORY_HEAL = 25
FOCUS_COST_BULLET_TIME = 25
FOCUS_REGEN_PER_TURN = 8
def _init_status(enemy):
if not hasattr(enemy, "frozen"):
enemy.frozen = False
if not hasattr(enemy, "jammed"):
enemy.jammed = 0
if not hasattr(enemy, "turned"):
enemy.turned = False
def _pick_target(enemies):
alive = [e for e in enemies if e.is_alive]
if len(alive) == 1:
return alive[0]
print(f"\n{Colors.RED}Ziele:{Colors.END}")
for i, e in enumerate(alive):
stagger_tag = f" {Colors.YELLOW}[STAGGERED]{Colors.END}" if e.staggered else ""
print(f" {Colors.GREEN}[{i + 1}]{Colors.END} {e.name} - {bar(e.hp, e.max_hp, Colors.RED)}{stagger_tag}")
choice = input(f"\n{Colors.CYAN}Ziel waehlen: {Colors.END}").strip()
try:
idx = int(choice) - 1
if 0 <= idx < len(alive):
return alive[idx]
except ValueError:
pass
return alive[0]
def _weapon_menu(player):
print(f"\n{Colors.GREEN}Waffen:{Colors.END}")
usable = []
for w in player.weapons:
if w.ammo_type == "melee":
usable.append(w)
tag = "unbegrenzt"
else:
have = player.ammo.get(w.ammo_type, 0)
tag = f"{have} {w.ammo_type}"
if have >= w.ammo_cost:
usable.append(w)
print(f" {Colors.GREEN}[{len(usable) if w in usable else '-'}]{Colors.END} {w.name} ({tag})")
options = [(str(i + 1), w.name) for i, w in enumerate(usable)]
choice = menu(options, "Waffe")
try:
idx = int(choice) - 1
if 0 <= idx < len(usable):
return usable[idx]
except ValueError:
pass
return usable[0] if usable else player.get_weapon("fists")
def _fire_weapon(player, weapon, enemy, rng):
dmg = rng.randint(*weapon.damage_range)
dmg = int(dmg * type_multiplier(weapon.damage_type, enemy.type))
if weapon.ammo_type != "melee":
player.ammo[weapon.ammo_type] = max(0, player.ammo[weapon.ammo_type] - weapon.ammo_cost)
return dmg
def _enemy_attack(player, enemy, rng):
if enemy.frozen:
slow_print(f"\n{Colors.GREEN}{enemy.name} ist eingefroren -- kein Angriff diese Runde!{Colors.END}")
enemy.frozen = False
return
if enemy.turned:
slow_print(f"\n{Colors.GREEN}{enemy.name} greift, verwirrt, ins Leere.{Colors.END}")
enemy.turned = False
return
dmg = rng.randint(*enemy.attack_range)
if enemy.jammed:
dmg = int(dmg * 0.5)
enemy.jammed -= 1
remaining, absorbed = player.take_damage(dmg)
if absorbed:
slow_print(f"\n{Colors.RED}{enemy.name} greift an! Armor absorbiert {absorbed} Schaden.{Colors.END}")
if remaining:
slow_print(f"{Colors.RED}Du erleidest {remaining} Schaden!{Colors.END}")
if not absorbed and not remaining:
slow_print(f"\n{Colors.GREEN}{enemy.name} greift an -- God Mode absorbiert alles!{Colors.END}")
def run_combat(player, enemies, title="KAMPF", rng=None, on_reinforcement=None):
"""
enemies: Liste von Enemy-Objekten.
Gibt True (Sieg) oder False (Tod, sofern kein Auto-Revive) zurueck.
"""
rng = rng or random
for e in enemies:
_init_status(e)
slow_print(f"\n{Colors.BOLD}{Colors.RED}═══ {title} ═══{Colors.END}")
while any(e.is_alive for e in enemies) and player.health > 0:
print()
for e in enemies:
if e.is_alive:
stagger_tag = f" {Colors.YELLOW}[STAGGERED - Glory Kill bereit!]{Colors.END}" if e.staggered else ""
print(f"{Colors.RED}{e.name}:{Colors.END} {bar(e.hp, e.max_hp, Colors.RED)}"
f" | Stagger {bar(e.stagger, e.stagger_max, Colors.YELLOW, 10)}{stagger_tag}")
print(f"{Colors.CYAN}Du:{Colors.END} {bar(player.health, player.max_health, Colors.RED)} | "
f"Armor {player.armor} | Focus {bar(player.focus, player.max_focus, Colors.GREEN, 10)}")
options = [
("1", "Schiessen"),
("2", f"Bullet Time (-{FOCUS_COST_BULLET_TIME} Focus, garantiertes Ausweichen + Krit)"),
("3", "Glory Kill (nur bei gestaggertem Gegner)"),
("4", f"Hack (Skill-Check, Hacking {player.hacking})"),
]
if player.inventory.has("medkit"):
options.append(("5", "Medkit benutzen"))
action = menu(options, "Aktion")
if cheats.try_cheat(action, player):
continue
bullet_time_active = False
if action == "1":
weapon = _weapon_menu(player)
target = _pick_target(enemies)
dmg = _fire_weapon(player, weapon, target, rng)
slow_print(f"\n{Colors.YELLOW}{weapon.name} trifft {target.name}!{Colors.END}")
target.apply_damage(dmg)
slow_print(f"{Colors.RED}-{dmg} HP für {target.name}!{Colors.END}")
if target.staggered:
slow_print(f"{Colors.YELLOW}{Colors.BOLD}{target.name} ist GESTAGGERT! Glory Kill moeglich!{Colors.END}")
elif action == "2":
if player.focus >= FOCUS_COST_BULLET_TIME:
player.focus -= FOCUS_COST_BULLET_TIME
bullet_time_active = True
slow_print(f"\n{Colors.GREEN}Zeit verlangsamt sich. Grüner Code umhüllt dich.{Colors.END}")
weapon = _weapon_menu(player)
target = _pick_target(enemies)
dmg = int(_fire_weapon(player, weapon, target, rng) * 1.5)
slow_print(f"{Colors.YELLOW}Kritischer Treffer in Bullet Time! {weapon.name} zerreisst {target.name}!{Colors.END}")
target.apply_damage(dmg)
slow_print(f"{Colors.RED}-{dmg} HP für {target.name}!{Colors.END}")
else:
slow_print(f"\n{Colors.RED}Nicht genug Focus fuer Bullet Time! Der Versuch kostet dich die Deckung.{Colors.END}")
elif action == "3":
target = next((e for e in enemies if e.is_alive and e.staggered), None)
if target:
slow_print(f"\n{Colors.RED}{Colors.BOLD}GLORY KILL!{Colors.END}")
slow_print(f"{Colors.RED}Du stürmst vor, packst {target.name} und reisst ihn auseinander!{Colors.END}")
slow_print(f"{Colors.RED}Grüne Flüssigkeit spritzt — kein Blut. Code.{Colors.END}")
target.hp = 0
player.heal(GLORY_HEAL)
player.glory_kills += 1
slow_print(f"{Colors.GREEN}+{GLORY_HEAL} HP! Der Tod heilt dich.{Colors.END}")
else:
slow_print(f"\n{Colors.YELLOW}Kein Gegner ist gestaggert! Der Glory Kill schlaegt fehl.{Colors.END}")
target = _pick_target(enemies)
dmg = rng.randint(5, 10)
player.take_damage(dmg)
slow_print(f"{Colors.RED}{target.name} kontert! -{dmg} HP!{Colors.END}")
elif action == "4":
target = _pick_target(enemies)
success = hacking.attempt_hack(player, target.firewall, rng)
if success:
effect = hacking.choose_hack_effect()
hacking.apply_hack_effect(effect, target, player, rng)
else:
slow_print(f"{Colors.RED}Das System hat deinen Hack erkannt! Firewall-Rueckstoss!{Colors.END}")
dmg = rng.randint(10, 20)
player.take_damage(dmg)
slow_print(f"{Colors.RED}-{dmg} HP durch Rueckstoss!{Colors.END}")
if hacking.trace_triggered(player):
slow_print(f"\n{Colors.MAGENTA}{Colors.BOLD}TRACE KOMPLETT! Das System schickt Verstaerkung!{Colors.END}")
if on_reinforcement:
reinforcement = on_reinforcement()
if reinforcement:
_init_status(reinforcement)
enemies.append(reinforcement)
slow_print(f"{Colors.RED}{reinforcement.name} betritt den Kampf!{Colors.END}")
elif action == "5" and player.inventory.has("medkit"):
item = player.inventory.consume("medkit")
player.heal(item.value)
slow_print(f"\n{Colors.GREEN}Medkit benutzt. +{item.value} HP!{Colors.END}")
else:
slow_print(f"{Colors.YELLOW}Ungueltige Eingabe.{Colors.END}")
continue
# Boss-Phasenwechsel pruefen
for e in enemies:
if e.is_alive and e.maybe_advance_phase():
slow_print(f"\n{Colors.MAGENTA}{Colors.BOLD}{e.name} tritt in Phase {e.phase} ein! Er wird gefaehrlicher!{Colors.END}")
e.clear_stagger()
# Fokus regeneriert leicht jede Runde
player.focus = min(player.max_focus, player.focus + FOCUS_REGEN_PER_TURN)
# Gegner greifen an (nicht wenn Bullet Time garantiertes Ausweichen gibt)
for e in enemies:
if e.is_alive and player.health > 0:
if bullet_time_active:
slow_print(f"\n{Colors.GREEN}Du weichst {e.name}s Angriff muehelos aus — Bullet Time!{Colors.END}")
continue
dramatic_pause(0.3)
_enemy_attack(player, e, rng)
if player.health <= 0:
if player.try_auto_revive():
slow_print(f"\n{Colors.MAGENTA}{Colors.BOLD}Die Backup-Pille aktiviert sich! Du wirst neu geladen...{Colors.END}")
slow_print(f"{Colors.GREEN}HP wiederhergestellt auf {player.health}.{Colors.END}")
else:
return False
slow_print(f"\n{Colors.GREEN}{Colors.BOLD}ALLE GEGNER ELIMINIERT!{Colors.END}")
return True