#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Player-Klasse: Stats, Klassen, Leveling, Inventar, Story-Flags.""" import json import os from doom_matrix.engine.ui import Colors, bar from doom_matrix.entities.items import Inventory, Weapon _WEAPON_DATA_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "weapons.json") _WEAPON_TEMPLATES = None def _load_weapon_templates(): global _WEAPON_TEMPLATES if _WEAPON_TEMPLATES is None: with open(_WEAPON_DATA_PATH, "r", encoding="utf-8") as f: data = json.load(f) _WEAPON_TEMPLATES = {d["id"]: d for d in data} return _WEAPON_TEMPLATES def make_weapon(weapon_id): templates = _load_weapon_templates() return Weapon.from_dict(templates[weapon_id]) # Charakterklassen: Basiswerte CLASSES = { "slayer": { "name": "Slayer", "description": "Doom-Fokus. Mehr HP und Staerke, schwaecheres Hacking.", "hp": 120, "armor": 20, "focus": 60, "hacking": 2, "strength": 8, "corruption_rate": 1.0, "start_weapon": "super_shotgun", "start_ammo": {"shells": 20}, }, "operator": { "name": "Operator", "description": "Matrix-Fokus. Starkes Hacking und Focus, koerperlich schwaecher.", "hp": 85, "armor": 10, "focus": 100, "hacking": 9, "strength": 3, "corruption_rate": 1.0, "start_weapon": "plasma_rifle", "start_ammo": {"cells": 100}, }, "hybrid": { "name": "Anomalie", "description": "Ausgewogen. Einzigartige Dialogoptionen, aber doppelte Corruption.", "hp": 100, "armor": 15, "focus": 80, "hacking": 5, "strength": 5, "corruption_rate": 2.0, "start_weapon": "super_shotgun", "start_ammo": {"shells": 10, "cells": 40}, }, } class Player: def __init__(self, player_class="slayer"): cls_data = CLASSES[player_class] self.player_class = player_class self.class_name = cls_data["name"] self.name = "The One Slayer" self.max_health = cls_data["hp"] self.health = cls_data["hp"] self.armor = cls_data["armor"] self.max_focus = cls_data["focus"] self.focus = cls_data["focus"] self.hacking = cls_data["hacking"] self.strength = cls_data["strength"] self.corruption_rate = cls_data["corruption_rate"] self.corruption = 0 # 0-100 self.level = 1 self.praetor_tokens = 0 self.weapons = [make_weapon("fists")] start_weapon = make_weapon(cls_data["start_weapon"]) self.weapons.append(start_weapon) self.ammo = {"bullets": 0, "shells": 0, "cells": 0, "bfg": 0} self.ammo.update(cls_data["start_ammo"]) self.inventory = Inventory() self.layer = 3 # Start in Matrix self.location = "construct" self.glory_kills = 0 self.hacks = 0 self.trace_marks = 0 # Story-Flags fuer Easter Eggs / Verzweigungen self.flags = { "met_oracle": False, "saw_rabbit": False, "cat_glitch_count": 0, "cat_glitch_seen": False, "god_mode": False, "cheats_used": [], } def has_weapon(self, weapon_id): return any(w.weapon_id == weapon_id for w in self.weapons) def get_weapon(self, weapon_id): for w in self.weapons: if w.weapon_id == weapon_id: return w return None def add_weapon(self, weapon_id): if not self.has_weapon(weapon_id): self.weapons.append(make_weapon(weapon_id)) def heal(self, amount): self.health = min(self.max_health, self.health + amount) def take_damage(self, amount): """Wendet Schaden an, Armor absorbiert zuerst. Gibt (final_damage, armor_absorbed) zurueck.""" if self.flags.get("god_mode"): return 0, 0 armor_absorb = min(self.armor, amount) self.armor -= armor_absorb remaining = amount - armor_absorb self.health = max(0, self.health - remaining) return remaining, armor_absorb def add_armor(self, amount): self.armor = min(100, self.armor + amount) def gain_corruption(self, amount): self.corruption = min(100, self.corruption + amount * self.corruption_rate) def is_dead(self): return self.health <= 0 def try_auto_revive(self): if self.inventory.has_backup_pill and self.is_dead(): self.inventory.has_backup_pill = False self.health = int(self.max_health * 0.5) return True return False def spend_praetor_token(self, stat): if self.praetor_tokens <= 0: return False self.praetor_tokens -= 1 if stat == "health": self.max_health += 15 self.health = min(self.max_health, self.health + 15) elif stat == "focus": self.max_focus += 15 self.focus = min(self.max_focus, self.focus + 15) elif stat == "hacking": self.hacking += 2 elif stat == "strength": self.strength += 2 return True def _layer_name(self): layers = { 4: f"{Colors.RED}MARS UAC BASE{Colors.END}", 3: f"{Colors.GREEN}THE MATRIX{Colors.END}", 2: f"{Colors.MAGENTA}HELL SERVER{Colors.END}", 1: f"{Colors.YELLOW}THE ARCHITECT'S CORE{Colors.END}", 0: f"{Colors.WHITE}{Colors.BOLD}LAYER 0{Colors.END}", } return layers.get(self.layer, "UNKNOWN") def show_status(self): print(f"\n{Colors.BOLD}{Colors.CYAN}═══ STATUS: {self.class_name} ═══{Colors.END}") print(f"Health: {bar(self.health, self.max_health, Colors.RED)}") print(f"Armor: {bar(self.armor, 100, Colors.YELLOW)}") print(f"Focus: {bar(self.focus, self.max_focus, Colors.GREEN)}") print(f"Corruption: {bar(int(self.corruption), 100, Colors.MAGENTA)}") print(f"Hacking: {self.hacking} Strength: {self.strength} Level: {self.level}") print(f"Layer: {self._layer_name()}") print(f"Weapons: {', '.join(w.name for w in self.weapons)}") if any(self.ammo.values()): print(f"Ammo: {', '.join([f'{k}={v}' for k, v in self.ammo.items() if v > 0])}") inv_lines = self.inventory.list_display() if inv_lines: print(f"Inventar: {' | '.join(inv_lines)}") if self.praetor_tokens: print(f"{Colors.YELLOW}Praetor-Token verfuegbar: {self.praetor_tokens}{Colors.END}") print(f"{Colors.CYAN}══════════════════════════════{Colors.END}\n") def to_dict(self): return { "player_class": self.player_class, "health": self.health, "max_health": self.max_health, "armor": self.armor, "focus": self.focus, "max_focus": self.max_focus, "hacking": self.hacking, "strength": self.strength, "corruption": self.corruption, "level": self.level, "praetor_tokens": self.praetor_tokens, "weapon_ids": [w.weapon_id for w in self.weapons], "ammo": self.ammo, "layer": self.layer, "location": self.location, "glory_kills": self.glory_kills, "hacks": self.hacks, "trace_marks": self.trace_marks, "flags": self.flags, "keycards": list(self.inventory.keycards), "crucible_fragments": self.inventory.crucible_fragments, "has_backup_pill": self.inventory.has_backup_pill, } @classmethod def from_dict(cls, d): p = cls(d.get("player_class", "slayer")) p.health = d["health"] p.max_health = d["max_health"] p.armor = d["armor"] p.focus = d["focus"] p.max_focus = d["max_focus"] p.hacking = d["hacking"] p.strength = d["strength"] p.corruption = d["corruption"] p.level = d["level"] p.praetor_tokens = d["praetor_tokens"] p.weapons = [make_weapon(wid) for wid in d["weapon_ids"]] p.ammo = d["ammo"] p.layer = d["layer"] p.location = d["location"] p.glory_kills = d["glory_kills"] p.hacks = d["hacks"] p.trace_marks = d["trace_marks"] p.flags = d["flags"] p.inventory.keycards = set(d.get("keycards", [])) p.inventory.crucible_fragments = d.get("crucible_fragments", 0) p.inventory.has_backup_pill = d.get("has_backup_pill", False) return p