#!/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