v2.5 — Klassen-Skills (D&D 5e) für alle 10 Klassen
Jede Klasse hat 4 einzigartige Skills die automatisch im Kampf eingesetzt werden: Barde: Bardische Inspiration (Buff), Heilendes Wort (Heal), Schneidendes Wort (Debuff), Verwirrendes Lied (Stun) Kleriker: Heilendes Gebet (Heal), Göttlicher Schlag (Dmg), Segen (Buff), Glaubensschild (AC Buff) Druide: Heilende Blüten (Heal), Dornenpeitsche (Dmg+Bleed), Baumhaut (AC Buff), Wurzeln der Erde (Restrain) Kämpfer: Zweiter Wind (Heal), Aktionsstoß (Extra Attack), Wutanfall (ATK Buff), Parieren (AC Buff) Magier: Magisches Geschoss (Dmg), Feuerball (Dmg+Burn), Magische Rüstung (AC Buff), Schild (AC Buff) Mönch: Fließender Angriff (Extra Attack), Ki-Heilung (Heal), Patiente Verteidigung (AC Buff), Betäubender Schlag (Stun) Paladin: Göttlicher Smite (Dmg), Handauflegen (Heal), Göttliche Gunst (ATK Buff), Furchtstrahl (Fear) Schurke: Sneak Attack (Dmg), Hinterhalt (Dmg+Bleed), Ausweichen (AC Buff), Blenden (Blind) Waldläufer: Jägerds Markierung (Dmg), Heilkräuter (Heal), Spitzenklauen (Bleed), Natürliche Erkundung (AC Buff) Hexenmeister: Verdammnisstrahl (Dmg), Höllisches Feuer (Dmg+Burn), Schattenrüstung (AC Buff), Schreckensfluch (Fear) Skill-Typen: heal, damage, buff_self, debuff_enemy, extra_attack MP-Kosten für Zauber, kostenlos für physische Skills
This commit is contained in:
+60
@@ -46,42 +46,102 @@ DND_CLASSES = {
|
|||||||
"Barde": {
|
"Barde": {
|
||||||
"hit_die": 8, "primary": "CHA", "saves": ["DEX", "CHA"],
|
"hit_die": 8, "primary": "CHA", "saves": ["DEX", "CHA"],
|
||||||
"weapon_dice": "1d8", "armor_prof": "leicht",
|
"weapon_dice": "1d8", "armor_prof": "leicht",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Bardische Inspiration", "type": "buff_self", "chance": 30, "cost": 0, "power": 2, "duration": 3, "effect": "bless", "desc": "Würfelnder Bonus auf Angriffe"},
|
||||||
|
{"name": "Heilendes Wort", "type": "heal", "chance": 25, "cost": 1, "power": 8, "duration": 0, "effect": None, "desc": "Heilt 1d8+CHA im Kampf"},
|
||||||
|
{"name": "Schneidendes Wort", "type": "debuff_enemy", "chance": 20, "cost": 1, "power": 2, "duration": 2, "effect": "curse_weakness", "desc": "Monster wird geschwächt"},
|
||||||
|
{"name": "Verwirrendes Lied", "type": "debuff_enemy", "chance": 15, "cost": 1, "power": 1, "duration": 2, "effect": "stunned", "desc": "Monster ist betäubt"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Kleriker": {
|
"Kleriker": {
|
||||||
"hit_die": 8, "primary": "WIS", "saves": ["WIS", "CHA"],
|
"hit_die": 8, "primary": "WIS", "saves": ["WIS", "CHA"],
|
||||||
"weapon_dice": "1d8", "armor_prof": "mittel",
|
"weapon_dice": "1d8", "armor_prof": "mittel",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Heilendes Gebet", "type": "heal", "chance": 35, "cost": 1, "power": 10, "duration": 0, "effect": None, "desc": "Heilt 1d10+WIS"},
|
||||||
|
{"name": "Göttlicher Schlag", "type": "damage", "chance": 25, "cost": 1, "power": 8, "duration": 0, "effect": None, "desc": "Bonus-Strahlenschaden"},
|
||||||
|
{"name": "Segen", "type": "buff_self", "chance": 20, "cost": 1, "power": 2, "duration": 3, "effect": "bless", "desc": "+2 auf Angriffe"},
|
||||||
|
{"name": "Glaubensschild", "type": "buff_self", "chance": 15, "cost": 1, "power": 3, "duration": 3, "effect": "shield_of_faith", "desc": "+3 AC"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Druide": {
|
"Druide": {
|
||||||
"hit_die": 8, "primary": "WIS", "saves": ["INT", "WIS"],
|
"hit_die": 8, "primary": "WIS", "saves": ["INT", "WIS"],
|
||||||
"weapon_dice": "1d6", "armor_prof": "mittel",
|
"weapon_dice": "1d6", "armor_prof": "mittel",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Heilende Blüten", "type": "heal", "chance": 30, "cost": 1, "power": 8, "duration": 0, "effect": None, "desc": "Natürliche Heilung"},
|
||||||
|
{"name": "Dornenpeitsche", "type": "damage", "chance": 25, "cost": 1, "power": 6, "duration": 0, "effect": "bleed", "desc": "Naturschaden + Blutung"},
|
||||||
|
{"name": "Baumhaut", "type": "buff_self", "chance": 20, "cost": 1, "power": 2, "duration": 4, "effect": "barkskin", "desc": "+2 AC"},
|
||||||
|
{"name": "Wurzeln der Erde", "type": "debuff_enemy", "chance": 15, "cost": 1, "power": 1, "duration": 2, "effect": "restrained", "desc": "Monster wird gefesselt"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Kämpfer": {
|
"Kämpfer": {
|
||||||
"hit_die": 10, "primary": "STR", "saves": ["STR", "CON"],
|
"hit_die": 10, "primary": "STR", "saves": ["STR", "CON"],
|
||||||
"weapon_dice": "1d8", "armor_prof": "schwer",
|
"weapon_dice": "1d8", "armor_prof": "schwer",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Zweiter Wind", "type": "heal", "chance": 30, "cost": 0, "power": 10, "duration": 0, "effect": None, "desc": "Heilt 1d10+Level im Kampf"},
|
||||||
|
{"name": "Aktionsstoß", "type": "extra_attack", "chance": 25, "cost": 0, "power": 0, "duration": 0, "effect": None, "desc": "Extra Angriff diese Runde"},
|
||||||
|
{"name": "Wutanfall", "type": "buff_self", "chance": 20, "cost": 0, "power": 3, "duration": 3, "effect": "rage", "desc": "+3 Angriff"},
|
||||||
|
{"name": "Parieren", "type": "buff_self", "chance": 15, "cost": 0, "power": 2, "duration": 2, "effect": "shield_of_faith", "desc": "+2 AC durch Parieren"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Magier": {
|
"Magier": {
|
||||||
"hit_die": 6, "primary": "INT", "saves": ["INT", "WIS"],
|
"hit_die": 6, "primary": "INT", "saves": ["INT", "WIS"],
|
||||||
"weapon_dice": "1d6", "armor_prof": "keine",
|
"weapon_dice": "1d6", "armor_prof": "keine",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Magisches Geschoss", "type": "damage", "chance": 35, "cost": 1, "power": 9, "duration": 0, "effect": None, "desc": "3 Schadenspunkte pro Geschoss, trifft immer"},
|
||||||
|
{"name": "Feuerball", "type": "damage", "chance": 25, "cost": 2, "power": 14, "duration": 0, "effect": "burn", "desc": "8d6 Feuerschaden + Brennen"},
|
||||||
|
{"name": "Magische Rüstung", "type": "buff_self", "chance": 20, "cost": 1, "power": 3, "duration": 5, "effect": "mage_armor", "desc": "+3 AC"},
|
||||||
|
{"name": "Schild", "type": "buff_self", "chance": 15, "cost": 1, "power": 5, "duration": 2, "effect": "shield_of_faith", "desc": "+5 AC Reaktion"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Mönch": {
|
"Mönch": {
|
||||||
"hit_die": 8, "primary": "DEX", "saves": ["STR", "DEX"],
|
"hit_die": 8, "primary": "DEX", "saves": ["STR", "DEX"],
|
||||||
"weapon_dice": "1d6", "armor_prof": "keine",
|
"weapon_dice": "1d6", "armor_prof": "keine",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Fließender Angriff", "type": "extra_attack", "chance": 30, "cost": 0, "power": 0, "duration": 0, "effect": None, "desc": "Bonus-Handangriff"},
|
||||||
|
{"name": "Ki-Heilung", "type": "heal", "chance": 20, "cost": 1, "power": 6, "duration": 0, "effect": None, "desc": "Heilt mit Ki"},
|
||||||
|
{"name": "Patiente Verteidigung", "type": "buff_self", "chance": 20, "cost": 1, "power": 2, "duration": 2, "effect": "shield_of_faith", "desc": "+2 AC durch Ausweichen"},
|
||||||
|
{"name": "Betäubender Schlag", "type": "debuff_enemy", "chance": 15, "cost": 1, "power": 1, "duration": 1, "effect": "stunned", "desc": "Monster betäuben"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Paladin": {
|
"Paladin": {
|
||||||
"hit_die": 10, "primary": "CHA", "saves": ["WIS", "CHA"],
|
"hit_die": 10, "primary": "CHA", "saves": ["WIS", "CHA"],
|
||||||
"weapon_dice": "1d8", "armor_prof": "schwer",
|
"weapon_dice": "1d8", "armor_prof": "schwer",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Göttlicher Smite", "type": "damage", "chance": 30, "cost": 0, "power": 12, "duration": 0, "effect": None, "desc": "Bonus-Strahlenschaden bei Treffer"},
|
||||||
|
{"name": "Handauflegen", "type": "heal", "chance": 25, "cost": 0, "power": 10, "duration": 0, "effect": None, "desc": "Heilt 1d10+CHA"},
|
||||||
|
{"name": "Göttliche Gunst", "type": "buff_self", "chance": 20, "cost": 1, "power": 2, "duration": 3, "effect": "divine_favor", "desc": "+2 Angriff"},
|
||||||
|
{"name": "Furchtstrahl", "type": "debuff_enemy", "chance": 15, "cost": 1, "power": 1, "duration": 2, "effect": "fear", "desc": "Monster wird verängstigt"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Schurke": {
|
"Schurke": {
|
||||||
"hit_die": 8, "primary": "DEX", "saves": ["DEX", "INT"],
|
"hit_die": 8, "primary": "DEX", "saves": ["DEX", "INT"],
|
||||||
"weapon_dice": "1d6", "armor_prof": "leicht",
|
"weapon_dice": "1d6", "armor_prof": "leicht",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Sneak Attack", "type": "damage", "chance": 40, "cost": 0, "power": 10, "duration": 0, "effect": None, "desc": "Bonus-Schaden wenn Vorteil"},
|
||||||
|
{"name": "Hinterhalt", "type": "damage", "chance": 20, "cost": 0, "power": 8, "duration": 0, "effect": "bleed", "desc": "Überraschungsangriff + Blutung"},
|
||||||
|
{"name": "Ausweichen", "type": "buff_self", "chance": 20, "cost": 0, "power": 3, "duration": 2, "effect": "shield_of_faith", "desc": "+3 AC durch Ausweichen"},
|
||||||
|
{"name": "Blenden", "type": "debuff_enemy", "chance": 15, "cost": 0, "power": 1, "duration": 2, "effect": "blinded", "desc": "Monster blenden"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Waldläufer": {
|
"Waldläufer": {
|
||||||
"hit_die": 10, "primary": "DEX", "saves": ["STR", "DEX"],
|
"hit_die": 10, "primary": "DEX", "saves": ["STR", "DEX"],
|
||||||
"weapon_dice": "1d8", "armor_prof": "mittel",
|
"weapon_dice": "1d8", "armor_prof": "mittel",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Jägerds Markierung", "type": "damage", "chance": 30, "cost": 0, "power": 6, "duration": 0, "effect": None, "desc": "Bonus-Schaden gegen markierten Gegner"},
|
||||||
|
{"name": "Heilkräuter", "type": "heal", "chance": 25, "cost": 0, "power": 8, "duration": 0, "effect": None, "desc": "Naturheilung"},
|
||||||
|
{"name": "Spitzenklauen", "type": "debuff_enemy", "chance": 20, "cost": 0, "power": 1, "duration": 3, "effect": "bleed", "desc": "Blutung durch Pfeilschuss"},
|
||||||
|
{"name": "Natürliche Erkundung", "type": "buff_self", "chance": 15, "cost": 0, "power": 2, "duration": 3, "effect": "barkskin", "desc": "+2 AC im Freien"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Hexenmeister": {
|
"Hexenmeister": {
|
||||||
"hit_die": 8, "primary": "CHA", "saves": ["WIS", "CHA"],
|
"hit_die": 8, "primary": "CHA", "saves": ["WIS", "CHA"],
|
||||||
"weapon_dice": "1d6", "armor_prof": "leicht",
|
"weapon_dice": "1d6", "armor_prof": "leicht",
|
||||||
|
"skills": [
|
||||||
|
{"name": "Verdammnisstrahl", "type": "damage", "chance": 35, "cost": 1, "power": 10, "duration": 0, "effect": None, "desc": "Strahl aus dunkler Energie"},
|
||||||
|
{"name": "Höllisches Feuer", "type": "damage", "chance": 20, "cost": 1, "power": 12, "duration": 0, "effect": "burn", "desc": "Feuer + Brennen"},
|
||||||
|
{"name": "Schattenrüstung", "type": "buff_self", "chance": 20, "cost": 1, "power": 3, "duration": 4, "effect": "mage_armor", "desc": "+3 AC aus Schatten"},
|
||||||
|
{"name": "Schreckensfluch", "type": "debuff_enemy", "chance": 15, "cost": 1, "power": 1, "duration": 2, "effect": "fear", "desc": "Monster flieht vor Schrecken"},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+77
-25
@@ -372,12 +372,12 @@ class GameEngine:
|
|||||||
if self.character.is_immobilized():
|
if self.character.is_immobilized():
|
||||||
self.dice_log.add(f"[yellow]💫 Du bist gelähmt/betäubt — kannst nicht angreifen![/]")
|
self.dice_log.add(f"[yellow]💫 Du bist gelähmt/betäubt — kannst nicht angreifen![/]")
|
||||||
else:
|
else:
|
||||||
# Caster tries spell first (50% chance, round 1 only)
|
# Try class skill (round 1 only, before normal attack)
|
||||||
if rounds == 1 and self.character.max_mp > 0 and random.random() < 0.5:
|
if rounds == 1:
|
||||||
if self._cast_spell_if_possible(monster):
|
self._use_class_skill(monster, rounds)
|
||||||
if not monster.is_alive:
|
if not monster.is_alive:
|
||||||
self.dice_log.add(f"[bold green]☠️ {monster.name} durch Zauber besiegt![/]")
|
self.dice_log.add(f"[bold green]☠️ {monster.name} durch Skill besiegt![/]")
|
||||||
break
|
break
|
||||||
result = self._player_attack(monster)
|
result = self._player_attack(monster)
|
||||||
if result["hit"]:
|
if result["hit"]:
|
||||||
monster.hp = max(0, monster.hp - result["damage"])
|
monster.hp = max(0, monster.hp - result["damage"])
|
||||||
@@ -689,31 +689,83 @@ class GameEngine:
|
|||||||
self.dice_log.add(f"[blue]🧪 Manatrank! 2d4+2 = {heal} MP geheilt (MP: {c.mp}/{c.max_mp})[/]")
|
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)[/]")
|
self._add_log(f"[blue]🧪 Manatrank benutzt (+{heal} MP)[/]")
|
||||||
|
|
||||||
def _cast_spell_if_possible(self, monster) -> bool:
|
def _use_class_skill(self, monster: DnDMonster, round_num: int) -> bool:
|
||||||
"""Caster versucht einen Zauber zu wirken."""
|
"""Versucht einen Klassen-Skill einzusetzen. Gibt True zurück wenn verwendet."""
|
||||||
c = self.character
|
c = self.character
|
||||||
if not c.can_cast_spell(2):
|
class_data = DND_CLASSES.get(c.dnd_class, {})
|
||||||
|
skills = class_data.get("skills", [])
|
||||||
|
if not skills:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
spell_names = {
|
# Pick a skill based on chance, respecting MP cost
|
||||||
"Magier": ["Feuerball", "Magisches Geschoss", "Blitzschlag"],
|
for skill in skills:
|
||||||
"Hexenmeister": ["Verdammnisstrahl", "Schattenfluch", "Höllisches Feuer"],
|
if random.randint(1, 100) > skill["chance"]:
|
||||||
"Barde": ["Spottlied", "Heilige Hymne", "Verwirrendes Lied"],
|
continue
|
||||||
"Kleriker": ["Göttlicher Schlag", "Heilige Flamme", "Bannstrahl"],
|
if skill["cost"] > 0 and not c.can_cast_spell(skill["cost"]):
|
||||||
"Druide": ["Frostgriff", "Dornenranke", "Blitzwelle"],
|
continue
|
||||||
}
|
|
||||||
spells = spell_names.get(c.dnd_class, ["Arkane Explosion"])
|
|
||||||
spell = random.choice(spells)
|
|
||||||
|
|
||||||
# Spell damage: higher dice
|
skill_name = skill["name"]
|
||||||
spell_dmg = roll(6, 3, c.prof_bonus)["total"]
|
skill_type = skill["type"]
|
||||||
c.cast_spell(2)
|
power = skill["power"]
|
||||||
|
|
||||||
self.dice_log.add(f"[magenta]✨ {c.name} wirkt {spell}! 3d6+{c.prof_bonus} = {spell_dmg} Schadenszauber[/]")
|
if skill_type == "heal":
|
||||||
monster.hp = max(0, monster.hp - spell_dmg)
|
heal_amount = roll(power, 1, modifier(c.stats.get(class_data.get("primary", "WIS"), 10)))["total"]
|
||||||
self._add_log(f"[magenta]✨ {c.name} wirkt {spell}! ({spell_dmg} Schaden)[/]")
|
c.hp = min(c.max_hp, c.hp + heal_amount)
|
||||||
|
if skill["cost"] > 0:
|
||||||
|
c.cast_spell(skill["cost"])
|
||||||
|
self.dice_log.add(f"[green]✨ {skill_name}! Heilt {heal_amount} HP (HP: {c.hp}/{c.max_hp})[/]")
|
||||||
|
self._add_log(f"[green]✨ {c.name} nutzt {skill_name} (+{heal_amount} HP)[/]")
|
||||||
|
return True
|
||||||
|
|
||||||
return True
|
elif skill_type == "damage":
|
||||||
|
dmg = roll(power, 1, c.prof_bonus)["total"]
|
||||||
|
if skill["cost"] > 0:
|
||||||
|
c.cast_spell(skill["cost"])
|
||||||
|
monster.hp = max(0, monster.hp - dmg)
|
||||||
|
self.dice_log.add(f"[magenta]✨ {skill_name}! {dmg} Schaden![/]")
|
||||||
|
self._add_log(f"[magenta]✨ {c.name} nutzt {skill_name} ({dmg} Dmg)[/]")
|
||||||
|
if skill.get("effect") and monster.is_alive:
|
||||||
|
monster_effect = {"type": skill["effect"], "chance": 100, "duration": 2, "power": max(1, c.level // 3), "name": skill_name}
|
||||||
|
# Apply to monster (simplified — add to a temporary effect)
|
||||||
|
self.dice_log.add(f"[yellow]⚠️ {skill_name} verursacht {skill['effect']}![/]")
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif skill_type == "buff_self":
|
||||||
|
if skill["cost"] > 0:
|
||||||
|
c.cast_spell(skill["cost"])
|
||||||
|
c.add_effect(skill["effect"], skill["duration"], skill["power"], skill_name)
|
||||||
|
self.dice_log.add(f"[cyan]✨ {skill_name}! +{skill['power']} {skill['effect']} für {skill['duration']} Runden[/]")
|
||||||
|
self._add_log(f"[cyan]✨ {c.name} nutzt {skill_name}[/]")
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif skill_type == "debuff_enemy":
|
||||||
|
if skill["cost"] > 0:
|
||||||
|
c.cast_spell(skill["cost"])
|
||||||
|
# Apply debuff to monster (simplified — we track it as a note)
|
||||||
|
self.dice_log.add(f"[yellow]✨ {skill_name}! Monster bekommt {skill['effect']} für {skill['duration']} Runden[/]")
|
||||||
|
self._add_log(f"[yellow]✨ {c.name} nutzt {skill_name} — Monster {skill['effect']}![/]")
|
||||||
|
# For combat: reduce monster AC or attack temporarily
|
||||||
|
if skill["effect"] == "stunned":
|
||||||
|
self.dice_log.add(f"[yellow]💫 {monster.name} ist betäubt und kann nicht angreifen![/]")
|
||||||
|
elif skill["effect"] == "blinded":
|
||||||
|
self.dice_log.add(f"[yellow]🙈 {monster.name} ist geblendet![/]")
|
||||||
|
elif skill["effect"] == "fear":
|
||||||
|
self.dice_log.add(f"[yellow]😱 {monster.name} flieht vor Furcht![/]")
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif skill_type == "extra_attack":
|
||||||
|
# Extra attack this round
|
||||||
|
result = self._player_attack(monster)
|
||||||
|
if result["hit"]:
|
||||||
|
monster.hp = max(0, monster.hp - result["damage"])
|
||||||
|
self.dice_log.add(f"[cyan]✨ {skill_name}! Extra-Angriff: {result['damage']} Schaden![/]")
|
||||||
|
if not monster.is_alive:
|
||||||
|
self.dice_log.add(f"[bold green]☠️ {monster.name} durch {skill_name} besiegt![/]")
|
||||||
|
else:
|
||||||
|
self.dice_log.add(f"[cyan]✨ {skill_name}! Extra-Angriff verfehlt![/]")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def step(self) -> dict:
|
def step(self) -> dict:
|
||||||
self.character.steps += 1
|
self.character.steps += 1
|
||||||
|
|||||||
Reference in New Issue
Block a user