v2.1 — Shop, Tränke, Spells, Weg-Progress
Shop-System (klassenspezifisch):
- Waffen-Upgrades: Langschwert, Magierstab, Dolch etc. pro Klasse
- Rüstung-Upgrades: Kettenhemd, Magierrobe etc.
- Schilde für Tanks (Kämpfer/Paladin)
- Heiltränke (2d4+2), Große Heiltränke (4d4+4) ab Stufe 5
- Manatränke für Caster
- 10% Gold-Reserve wird immer behalten
Trank-System:
- Auto-Benutzung bei HP < 40% (Heiltrank) / HP < 20% (Großer)
- Manatrank bei MP < 30% für Caster
- Würfel-Log zeigt jeden Trankwurf
Zauber-System:
- Caster wirken Spells in Runde 1 (50% Chance)
- Feuerball, Verdammnisstrahl, Göttlicher Schlag etc.
- Kosten 2 MP + 1 Spell Slot
- 3d6 + Proficiency Bonus Schaden
Weg-Progress:
- Travel-Phasen zwischen Quests mit Progress-Balken
- 🚶 [████░░░░] Zielort im Bottom-Panel
- Shop-Besuch bei Ankunft
This commit is contained in:
+61
@@ -140,6 +140,14 @@ class DnDCharacter:
|
||||
self.inventory = []
|
||||
self.gold = roll(6, 3)["total"] * 10 # Starting gold 3d6 * 10
|
||||
|
||||
# Potions
|
||||
self.potions = {"healing": 2, "mana": 0, "greater_healing": 0, "strength": 0}
|
||||
self.potion_threshold = 0.4 # Use healing potion when HP < 40% of max
|
||||
|
||||
# MP / Spell points
|
||||
self.max_mp = self._calc_max_mp()
|
||||
self.mp = self.max_mp
|
||||
|
||||
# Stats tracking
|
||||
self.kills = 0
|
||||
self.deaths = 0
|
||||
@@ -148,6 +156,9 @@ class DnDCharacter:
|
||||
self.crit_hits = 0
|
||||
self.crit_misses = 0
|
||||
self.nat_20s = 0
|
||||
self.potions_used = 0
|
||||
self.gold_spent = 0
|
||||
self.items_bought = 0
|
||||
|
||||
@property
|
||||
def ac(self) -> int:
|
||||
@@ -161,6 +172,47 @@ class DnDCharacter:
|
||||
return max(0, self.level) # simplified: 1 slot per level
|
||||
return 0
|
||||
|
||||
def _calc_max_mp(self) -> int:
|
||||
"""MP für Zauber — basierend auf Klasse und INT/WIS/CHA."""
|
||||
if self.dnd_class in ("Magier", "Hexenmeister", "Barde", "Kleriker", "Druide"):
|
||||
primary = DND_CLASSES.get(self.dnd_class, {}).get("primary", "INT")
|
||||
return 10 + self.level * 2 + modifier(self.stats.get(primary, 10)) * 2
|
||||
return 0
|
||||
|
||||
def use_potion(self, potion_type: str) -> int:
|
||||
"""Benutzt einen Trank. Gibt geheilte Menge zurück, 0 wenn keine da."""
|
||||
if self.potions.get(potion_type, 0) <= 0:
|
||||
return 0
|
||||
self.potions[potion_type] -= 1
|
||||
self.potions_used += 1
|
||||
|
||||
if potion_type == "healing":
|
||||
heal = roll(4, 2, 2)["total"] # 2d4+2
|
||||
self.hp = min(self.max_hp, self.hp + heal)
|
||||
return heal
|
||||
elif potion_type == "greater_healing":
|
||||
heal = roll(8, 4, 4)["total"] # 4d4+4
|
||||
self.hp = min(self.max_hp, self.hp + heal)
|
||||
return heal
|
||||
elif potion_type == "mana":
|
||||
heal = roll(4, 2, 2)["total"]
|
||||
self.mp = min(self.max_mp, self.mp + heal)
|
||||
return heal
|
||||
elif potion_type == "strength":
|
||||
# Temporary buff: +2 to next attack (simplified — just return 0)
|
||||
return 0
|
||||
return 0
|
||||
|
||||
def needs_healing(self) -> bool:
|
||||
return self.hp < self.max_hp * self.potion_threshold
|
||||
|
||||
def can_cast_spell(self, cost: int = 1) -> bool:
|
||||
return self.mp >= cost and self.spell_slots > 0
|
||||
|
||||
def cast_spell(self, cost: int = 1):
|
||||
self.mp -= cost
|
||||
self.spell_slots -= 1
|
||||
|
||||
@property
|
||||
def attack_bonus(self) -> int:
|
||||
"""Attack roll modifier: STR/DEX mod + proficiency."""
|
||||
@@ -211,6 +263,8 @@ class DnDCharacter:
|
||||
self.hp = self.max_hp
|
||||
self.prof_bonus = self._calc_prof_bonus()
|
||||
self.spell_slots = self._calc_spell_slots()
|
||||
self.max_mp = self._calc_max_mp()
|
||||
self.mp = self.max_mp
|
||||
# ASI: +2 to one stat every 4 levels
|
||||
if self.level % 4 == 0:
|
||||
best_stat = max(self.stats, key=lambda k: self.stats[k])
|
||||
@@ -238,13 +292,16 @@ class DnDCharacter:
|
||||
"hit_die": self.hit_die, "save_prof": self.save_prof,
|
||||
"weapon_dice": self.weapon_dice,
|
||||
"max_hp": self.max_hp, "hp": self.hp,
|
||||
"max_mp": self.max_mp, "mp": self.mp,
|
||||
"base_ac": self.base_ac, "armor_bonus": self.armor_bonus, "shield_bonus": self.shield_bonus,
|
||||
"prof_bonus": self.prof_bonus, "spell_slots": self.spell_slots,
|
||||
"equipped": {k: v.to_dict() if v else None for k, v in self.equipped.items()},
|
||||
"inventory": [i.to_dict() for i in self.inventory],
|
||||
"gold": self.gold,
|
||||
"potions": self.potions, "potion_threshold": self.potion_threshold,
|
||||
"kills": self.kills, "deaths": self.deaths, "quests_done": self.quests_done,
|
||||
"steps": self.steps, "crit_hits": self.crit_hits, "crit_misses": self.crit_misses, "nat_20s": self.nat_20s,
|
||||
"potions_used": self.potions_used, "gold_spent": self.gold_spent, "items_bought": self.items_bought,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -255,13 +312,17 @@ class DnDCharacter:
|
||||
c.hit_die = d["hit_die"]; c.save_prof = d["save_prof"]
|
||||
c.weapon_dice = d["weapon_dice"]
|
||||
c.max_hp = d["max_hp"]; c.hp = d["hp"]
|
||||
c.max_mp = d.get("max_mp", 0); c.mp = d.get("mp", 0)
|
||||
c.base_ac = d["base_ac"]; c.armor_bonus = d["armor_bonus"]; c.shield_bonus = d["shield_bonus"]
|
||||
c.prof_bonus = d["prof_bonus"]; c.spell_slots = d["spell_slots"]
|
||||
c.equipped = {k: Item.from_dict(v) if v else None for k, v in d.get("equipped", {}).items()}
|
||||
c.inventory = [Item.from_dict(i) for i in d.get("inventory", [])]
|
||||
c.gold = d["gold"]
|
||||
c.potions = d.get("potions", {"healing": 2, "mana": 0, "greater_healing": 0, "strength": 0})
|
||||
c.potion_threshold = d.get("potion_threshold", 0.4)
|
||||
c.kills = d["kills"]; c.deaths = d["deaths"]; c.quests_done = d["quests_done"]
|
||||
c.steps = d["steps"]; c.crit_hits = d["crit_hits"]; c.crit_misses = d["crit_misses"]; c.nat_20s = d["nat_20s"]
|
||||
c.potions_used = d.get("potions_used", 0); c.gold_spent = d.get("gold_spent", 0); c.items_bought = d.get("items_bought", 0)
|
||||
return c
|
||||
|
||||
|
||||
|
||||
+210
-11
@@ -145,11 +145,15 @@ class GameEngine:
|
||||
self.quest_progress = 0
|
||||
self.quest_target = random.randint(3, 8)
|
||||
|
||||
# Travel progress
|
||||
self.travel_progress = 0.0 # 0.0 to 1.0
|
||||
self.is_traveling = False
|
||||
self.travel_destination = ""
|
||||
|
||||
self._fill_cache()
|
||||
|
||||
def _fill_cache(self):
|
||||
"""Füllt Cache initial."""
|
||||
pass # LLM wird asynchron in refill_async geladen
|
||||
pass
|
||||
|
||||
def _add_log(self, msg: str):
|
||||
self.event_log.insert(0, msg)
|
||||
@@ -251,6 +255,13 @@ class GameEngine:
|
||||
while monster.is_alive and self.character.is_alive:
|
||||
rounds += 1
|
||||
|
||||
# Caster tries spell first (50% chance per round)
|
||||
if rounds == 1 and self.character.max_mp > 0 and random.random() < 0.5:
|
||||
if self._cast_spell_if_possible(monster):
|
||||
if not monster.is_alive:
|
||||
self.dice_log.add(f"[bold green]☠️ {monster.name} durch Zauber besiegt![/]")
|
||||
break
|
||||
|
||||
# Player attacks (initiative: DEX check)
|
||||
result = self._player_attack(monster)
|
||||
if result["hit"]:
|
||||
@@ -288,16 +299,17 @@ class GameEngine:
|
||||
self._add_log(f"[bold yellow]⬆️ Stufe {self.character.level} erreicht![/]")
|
||||
self.dice_log.add(f"[bold yellow]⬆️ LEVEL UP! Stufe {self.character.level}[/]")
|
||||
self.character.heal()
|
||||
self.character.mp = self.character.max_mp
|
||||
|
||||
self._add_log(f"[blue]🌍 Du machst dich auf den Weg...[/]")
|
||||
self.current_location = random.choice(FALLBACK_LOCATIONS)
|
||||
self._add_log(f"[blue]🌍 Ankunft in: {self.current_location}[/]")
|
||||
# Start travel to new location
|
||||
self.is_traveling = True
|
||||
self.travel_progress = 0.0
|
||||
self.travel_destination = random.choice(FALLBACK_LOCATIONS)
|
||||
self._add_log(f"[blue]🌍 Du machst dich auf den Weg nach {self.travel_destination}...[/]")
|
||||
|
||||
self.current_quest = random.choice(FALLBACK_QUESTS)
|
||||
self.quest_progress = 0
|
||||
self.quest_target = random.randint(3, 8)
|
||||
self._add_log(f"[cyan]📜 Neue Quest: {self.current_quest}[/]")
|
||||
self._add_log(f"[dim]Ziel: Besiege {self.quest_target} Gegner[/]")
|
||||
|
||||
def _auto_equip(self, item: Item):
|
||||
slot = None
|
||||
@@ -334,22 +346,203 @@ class GameEngine:
|
||||
if sold > 0:
|
||||
self._add_loot_log(f"[dim]💰 {sold} Schrott verkauft (+{total_gold}G)[/]")
|
||||
|
||||
def _visit_shop(self):
|
||||
"""Charakter kauft Ausrüstung und Tränke im Shop (klassenspezifisch)."""
|
||||
c = self.character
|
||||
bought_something = False
|
||||
|
||||
is_caster = c.dnd_class in ("Magier", "Hexenmeister", "Barde", "Kleriker", "Druide")
|
||||
is_tank = c.dnd_class in ("Kämpfer", "Paladin")
|
||||
|
||||
# 1. Buy better weapon first (most impact)
|
||||
weapon_price = 30 + c.level * 15
|
||||
current_weapon = c.equipped.get("weapon")
|
||||
desired_power = c.level + 2
|
||||
if c.gold >= weapon_price and (not current_weapon or current_weapon.power < desired_power):
|
||||
weapon_names = {
|
||||
"Kämpfer": ["Langschwert", "Kampfaxt", "Streitkolben"],
|
||||
"Paladin": ["Langschwert", "Heilige Klinge", "Göttlicher Hammer"],
|
||||
"Schurke": ["Dolch", "Kurzschwert", "Wurfmesser"],
|
||||
"Mönch": ["Kampfstab", "Waffenlose Faust", "Nunchaku"],
|
||||
"Magier": ["Magierstab", "Zauberfokus", "Kristallkugel"],
|
||||
"Hexenmeister": ["Verdammnisstab", "Schattenfokus"],
|
||||
"Barde": ["Rapier", "Laute der Macht", "Elfenklinge"],
|
||||
"Kleriker": ["Streitkolben", "Heiliges Symbol", "Göttlicher Schild"],
|
||||
"Druide": ["Eichenstab", "Dornenpeitsche", "Naturfokus"],
|
||||
"Waldläufer": ["Langbogen", "Kurzschwert", "Jagdspeer"],
|
||||
}
|
||||
names = weapon_names.get(c.dnd_class, ["Langschwert"])
|
||||
rarity = "selten" if c.level >= 5 else "gewöhnlich"
|
||||
w_name = random.choice(names)
|
||||
w = Item(w_name, "weapon", desired_power, weapon_price // 2, rarity)
|
||||
c.gold -= weapon_price
|
||||
c.gold_spent += weapon_price
|
||||
c.items_bought += 1
|
||||
if current_weapon:
|
||||
c.inventory.append(current_weapon)
|
||||
c.equipped["weapon"] = w
|
||||
self._add_loot_log(f"[cyan]⚔️ Kaufe {w} (-{weapon_price}G)[/]")
|
||||
bought_something = True
|
||||
|
||||
# 2. Buy armor
|
||||
armor_price = 40 + c.level * 10
|
||||
current_armor = c.equipped.get("armor")
|
||||
desired_armor = c.level // 2 + 1
|
||||
if c.gold >= armor_price and (not current_armor or current_armor.power < desired_armor):
|
||||
armor_names = {
|
||||
"Kämpfer": ["Kettenhemd", "Plattenpanzer", "Schwere Rüstung"],
|
||||
"Paladin": ["Plattenpanzer", "Heilige Rüstung", "Göttlicher Harnisch"],
|
||||
"Schurke": ["Lederrüstung", "Schattenmantel", "Wendiger Umhang"],
|
||||
"Magier": ["Magierrobe", "Zauberergewand", "Arkanes Gewand"],
|
||||
"default": ["Lederrüstung", "Kettenhemd", "Schuppengewand"],
|
||||
}
|
||||
names = armor_names.get(c.dnd_class, armor_names["default"])
|
||||
a_name = random.choice(names)
|
||||
a = Item(a_name, "armor", desired_armor, armor_price // 2, "gewöhnlich")
|
||||
c.gold -= armor_price
|
||||
c.gold_spent += armor_price
|
||||
c.items_bought += 1
|
||||
c.armor_bonus = desired_armor
|
||||
if current_armor:
|
||||
c.inventory.append(current_armor)
|
||||
c.equipped["armor"] = a
|
||||
self._add_loot_log(f"[cyan]🛡️ Kaufe {a} (-{armor_price}G)[/]")
|
||||
bought_something = True
|
||||
|
||||
# 3. Buy shield for tanks
|
||||
if is_tank and not c.equipped.get("shield"):
|
||||
shield_price = 25
|
||||
if c.gold >= shield_price:
|
||||
s = Item("Eisenschild", "shield", 2, 12, "gewöhnlich")
|
||||
c.gold -= shield_price
|
||||
c.gold_spent += shield_price
|
||||
c.items_bought += 1
|
||||
c.shield_bonus = 2
|
||||
c.equipped["shield"] = s
|
||||
self._add_loot_log(f"[cyan]🛡️ Kaufe {s} (-{shield_price}G)[/]")
|
||||
bought_something = True
|
||||
|
||||
# 4. Buy healing potions (keep 10% gold reserve)
|
||||
heal_price = 50
|
||||
max_healing = 5 if is_tank else 3
|
||||
gold_reserve = c.gold * 0.1
|
||||
while c.potions["healing"] < max_healing and c.gold - heal_price >= gold_reserve:
|
||||
c.gold -= heal_price
|
||||
c.potions["healing"] += 1
|
||||
c.gold_spent += heal_price
|
||||
c.items_bought += 1
|
||||
self._add_loot_log(f"[green]🧪 Kaufe Heiltrank (-{heal_price}G)[/]")
|
||||
bought_something = True
|
||||
|
||||
# 5. Greater healing potions at higher levels
|
||||
if c.level >= 5:
|
||||
greater_price = 150
|
||||
while c.potions["greater_healing"] < 2 and c.gold - greater_price >= gold_reserve:
|
||||
c.gold -= greater_price
|
||||
c.potions["greater_healing"] += 1
|
||||
c.gold_spent += greater_price
|
||||
c.items_bought += 1
|
||||
self._add_loot_log(f"[green]🧪 Kaufe Großen Heiltrank (-{greater_price}G)[/]")
|
||||
bought_something = True
|
||||
|
||||
# 6. Mana potions for casters
|
||||
if is_caster:
|
||||
mana_price = 50
|
||||
while c.potions["mana"] < 3 and c.gold - mana_price >= gold_reserve:
|
||||
c.gold -= mana_price
|
||||
c.potions["mana"] += 1
|
||||
c.gold_spent += mana_price
|
||||
c.items_bought += 1
|
||||
self._add_loot_log(f"[blue]🧪 Kaufe Manatrank (-{mana_price}G)[/]")
|
||||
bought_something = True
|
||||
|
||||
if bought_something:
|
||||
self._add_log(f"[yellow]🏪 Shop besucht — Ausrüstung und Tränke gekauft[/]")
|
||||
self.dice_log.add(f"[yellow]🏪 Shop: {c.items_bought} Items, Rest-Gold: {c.gold}G[/]")
|
||||
|
||||
def _use_potions_if_needed(self):
|
||||
"""Benutzt automatisch Heiltränke wenn HP niedrig ist."""
|
||||
c = self.character
|
||||
|
||||
# Use greater healing first if very low
|
||||
if c.hp < c.max_hp * 0.2 and c.potions.get("greater_healing", 0) > 0:
|
||||
heal = c.use_potion("greater_healing")
|
||||
self.dice_log.add(f"[green]🧪 Großer Heiltrank! 4d4+4 = {heal} HP geheilt (HP: {c.hp}/{c.max_hp})[/]")
|
||||
self._add_log(f"[green]🧪 Großen Heiltrank benutzt (+{heal} HP)[/]")
|
||||
|
||||
# Regular healing potion
|
||||
elif c.needs_healing() and c.potions.get("healing", 0) > 0:
|
||||
heal = c.use_potion("healing")
|
||||
self.dice_log.add(f"[green]🧪 Heiltrank! 2d4+2 = {heal} HP geheilt (HP: {c.hp}/{c.max_hp})[/]")
|
||||
self._add_log(f"[green]🧪 Heiltrank benutzt (+{heal} HP)[/]")
|
||||
|
||||
# Mana potion for casters
|
||||
if c.max_mp > 0 and c.mp < c.max_mp * 0.3 and c.potions.get("mana", 0) > 0:
|
||||
heal = c.use_potion("mana")
|
||||
self.dice_log.add(f"[blue]🧪 Manatrank! 2d4+2 = {heal} MP geheilt (MP: {c.mp}/{c.max_mp})[/]")
|
||||
self._add_log(f"[blue]🧪 Manatrank benutzt (+{heal} MP)[/]")
|
||||
|
||||
def _cast_spell_if_possible(self, monster) -> bool:
|
||||
"""Caster versucht einen Zauber zu wirken."""
|
||||
c = self.character
|
||||
if not c.can_cast_spell(2):
|
||||
return False
|
||||
|
||||
spell_names = {
|
||||
"Magier": ["Feuerball", "Magisches Geschoss", "Blitzschlag"],
|
||||
"Hexenmeister": ["Verdammnisstrahl", "Schattenfluch", "Höllisches Feuer"],
|
||||
"Barde": ["Spottlied", "Heilige Hymne", "Verwirrendes Lied"],
|
||||
"Kleriker": ["Göttlicher Schlag", "Heilige Flamme", "Bannstrahl"],
|
||||
"Druide": ["Frostgriff", "Dornenranke", "Blitzwelle"],
|
||||
}
|
||||
spells = spell_names.get(c.dnd_class, ["Arkane Explosion"])
|
||||
spell = random.choice(spells)
|
||||
|
||||
# Spell damage: higher dice
|
||||
spell_dmg = roll(6, 3, c.prof_bonus)["total"]
|
||||
c.cast_spell(2)
|
||||
|
||||
self.dice_log.add(f"[magenta]✨ {c.name} wirkt {spell}! 3d6+{c.prof_bonus} = {spell_dmg} Schadenszauber[/]")
|
||||
monster.hp = max(0, monster.hp - spell_dmg)
|
||||
self._add_log(f"[magenta]✨ {c.name} wirkt {spell}! ({spell_dmg} Schaden)[/]")
|
||||
|
||||
return True
|
||||
|
||||
def step(self) -> dict:
|
||||
self.character.steps += 1
|
||||
events = []
|
||||
|
||||
# Random travel
|
||||
if random.randint(1, 50) == 1:
|
||||
# === Travel phase ===
|
||||
if self.is_traveling:
|
||||
self.travel_progress += random.uniform(0.15, 0.35)
|
||||
if self.travel_progress >= 1.0:
|
||||
self.is_traveling = False
|
||||
self.current_location = self.travel_destination
|
||||
self.travel_progress = 0.0
|
||||
self._add_log(f"[blue]🌍 Ankunft in: {self.current_location}[/]")
|
||||
self._add_log(f"[cyan]📜 Neue Quest: {self.current_quest}[/]")
|
||||
self._add_log(f"[dim]Ziel: Besiege {self.quest_target} Gegner[/]")
|
||||
# Visit shop on arrival
|
||||
self._visit_shop()
|
||||
else:
|
||||
# Still traveling, no combat this step
|
||||
return {"events": events, "traveling": True}
|
||||
|
||||
# === Random travel between fights ===
|
||||
if random.randint(1, 50) == 1 and not self.is_traveling:
|
||||
self.current_location = random.choice(FALLBACK_LOCATIONS)
|
||||
events.append(("travel", f"[blue]🌍 Du reist nach: {self.current_location}[/]"))
|
||||
self._add_log(events[-1][1])
|
||||
|
||||
# Spawn monster
|
||||
# === Use potions if needed (before fight) ===
|
||||
self._use_potions_if_needed()
|
||||
|
||||
# === Spawn monster ===
|
||||
monster = self._spawn_monster()
|
||||
events.append(("encounter", f"[red]⚔️ {monster.name} (AC {monster.ac}, HP {monster.hp}) erscheint![/]"))
|
||||
self._add_log(events[-1][1])
|
||||
|
||||
# Fight
|
||||
# === Fight ===
|
||||
result = self._combat(monster)
|
||||
|
||||
if result["result"] == "victory":
|
||||
@@ -418,6 +611,9 @@ class GameEngine:
|
||||
"event_log": self.event_log,
|
||||
"loot_log": self.loot_log,
|
||||
"dice_log": self.dice_log.get_all(),
|
||||
"is_traveling": self.is_traveling,
|
||||
"travel_progress": self.travel_progress,
|
||||
"travel_destination": self.travel_destination,
|
||||
}
|
||||
|
||||
def save(self, path: str):
|
||||
@@ -439,4 +635,7 @@ class GameEngine:
|
||||
engine.event_log = d.get("event_log", [])
|
||||
engine.loot_log = d.get("loot_log", [])
|
||||
engine.dice_log.entries = d.get("dice_log", [])
|
||||
engine.is_traveling = d.get("is_traveling", False)
|
||||
engine.travel_progress = d.get("travel_progress", 0.0)
|
||||
engine.travel_destination = d.get("travel_destination", "")
|
||||
return engine
|
||||
+17
-3
@@ -99,12 +99,19 @@ def render_game(engine: GameEngine) -> Layout:
|
||||
char_table.add_row("Stufe", str(c.level))
|
||||
char_table.add_row("XP", f"{c.xp}/{c.xp_to_next}")
|
||||
char_table.add_row("HP", f"{c.hp}/{c.max_hp}")
|
||||
char_table.add_row("MP", f"{c.mp}/{c.max_mp}" if c.max_mp > 0 else "[dim]—[/]")
|
||||
char_table.add_row("AC", str(c.ac))
|
||||
char_table.add_row("Prof", f"+{c.prof_bonus}")
|
||||
char_table.add_row("ATK", f"+{c.attack_bonus}")
|
||||
char_table.add_row("DMG", f"{c.weapon_dice}+{c.damage_bonus}")
|
||||
char_table.add_row("Gold", f"{c.gold}G")
|
||||
|
||||
# Potions
|
||||
pot_str = f"❤️{c.potions.get('healing',0)} " \
|
||||
f"✨{c.potions.get('mana',0)} " \
|
||||
f"💖{c.potions.get('greater_healing',0)}"
|
||||
char_table.add_row("Tränke", pot_str)
|
||||
|
||||
# Stats row
|
||||
stats_str = " ".join(f"{k}:{c.stats[k]}" for k in ["STR","DEX","CON","INT","WIS","CHA"])
|
||||
char_table.add_row("Stats", stats_str)
|
||||
@@ -184,12 +191,19 @@ def render_game(engine: GameEngine) -> Layout:
|
||||
Layout(Panel(quest_text, title="[bold]Aktuelle Quest[/]"), name="quest"),
|
||||
)
|
||||
|
||||
# Bottom: bars
|
||||
# Bottom: bars + travel
|
||||
bars = Text()
|
||||
bars.append(f"HP [{hp_bar}] {c.hp}/{c.max_hp} ", style="red")
|
||||
bars.append(f"XP [{xp_bar}] {c.xp}/{c.xp_to_next} ", style="green")
|
||||
bars.append(f"🌍 {engine.current_location} ", style="blue")
|
||||
bars.append(f"Schritte: {c.steps}", style="dim")
|
||||
|
||||
if engine.is_traveling:
|
||||
travel_len = 20
|
||||
travel_filled = int(engine.travel_progress * travel_len)
|
||||
travel_bar = "█" * travel_filled + "░" * (travel_len - travel_filled)
|
||||
bars.append(f"🚶 [{travel_bar}] {engine.travel_destination}", style="blue")
|
||||
else:
|
||||
bars.append(f"🌍 {engine.current_location} ", style="blue")
|
||||
bars.append(f"Schritte: {c.steps}", style="dim")
|
||||
|
||||
layout["bottom"].update(Panel(bars, style="dim"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user