finished out skills per level, added skill trees to professions templates

This commit is contained in:
2025-11-02 19:08:36 -06:00
parent fd572076e0
commit 31aa0000cc
16 changed files with 361 additions and 26 deletions

View File

@@ -37,7 +37,7 @@ def _name(rng: random.Random, theme: Dict) -> str:
def _damage_power(tier: int, rng: random.Random) -> float:
# Linear-with-jitter curve—easy to reason about and scale
base = 50 + 7 * tier
base = 10 * tier
jitter = 1.0 + rng.uniform(-0.08, 0.08)
return round(base * jitter, 2)

View File

@@ -11,27 +11,23 @@ from app.game.systems.leveling import set_level
dice = Dice()
# tuning knobs
level_growth = 1.25
progression = DEFAULT_LEVEL_PROGRESSION
def build_char(name:str, origin_story:str, race_id:str, profession_id:str, fame:int=0, level:int=1) -> Entity:
def build_char(name:str, origin_story:str, race_id:str, profession_id:str,ability_pathway:str,level:int=1) -> Entity:
races = RaceRepository()
professions = ProfessionRepository()
progression = DEFAULT_LEVEL_PROGRESSION
race = races.get(race_id)
profession = professions.get(profession_id)
profession.ability_paths
e = Entity(
uuid = str(uuid.uuid4()),
name = name,
origin_story = origin_story,
fame = fame,
race =race,
ability_pathway=ability_pathway,
profession = profession
)

View File

@@ -33,6 +33,7 @@ class Entity:
origin_story:str = ""
race: Race = field(default_factory=Race)
profession: Profession = field(default_factory=Profession)
ability_pathway: str = ""
level: int = 0
xp: int = 0

View File

@@ -16,6 +16,7 @@ class Profession:
physical_defense_per_level: float
magic_attack_per_level: float
magic_defense_per_level: float
ability_paths: list[str] = field(default_factory=list)
tags: list[str] = field(default_factory=list) # e.g., {"playable"}, {"leader","elite"}
enemy: Optional[EnemyProfile] = None # ⬅ optional enemy-only tuning
@@ -88,6 +89,7 @@ class Profession:
physical_defense_per_level=as_float(data["physical_defense_per_level"], "physical_defense_per_level"),
magic_attack_per_level=as_float(data["magic_attack_per_level"], "magic_attack_per_level"),
magic_defense_per_level=as_float(data["magic_defense_per_level"], "magic_defense_per_level"),
ability_paths=(data.get("ability_paths",[])),
tags=tags,
enemy=enemy,
)

View File

@@ -1,9 +1,14 @@
# utils/leveling.py
import math
import random
from typing import Dict, Any, Callable, Optional, List, Tuple
from app.game.generators.level_progression import LevelProgression
from app.game.models.entities import Entity
from app.game.generators.abilities_factory import newly_unlocked_abilities
GLOBAL_CHANCE_PER_LEVEL = 0.3
def set_level(entity:Entity, target_level: int, prog: LevelProgression) -> None:
"""
Snap an entity to a *specific* level. Sets XP to that level's floor.
@@ -25,12 +30,15 @@ def set_level(entity:Entity, target_level: int, prog: LevelProgression) -> None:
# set next level xp as xp needed for next level
entity.xp_to_next_level = prog.xp_for_level(target_level + 1)
spells_list = newly_unlocked_abilities(class_name=entity.profession.name,
path="Hellknight",
# entity get's a random number of spells based on the number of levels gained
skills_per_level = calculate_skills_gained(current,target_level)
skills = newly_unlocked_abilities(class_name=entity.profession.name,
path=entity.ability_pathway,
level=target_level,
per_tier=1,
per_tier=skills_per_level,
primary=entity.profession.primary_stat)
_add_abilities(entity,spells_list)
_add_abilities(entity,skills)
_recalc(entity)
@@ -46,12 +54,12 @@ def grant_xp(entity:Entity, amount: int, prog: LevelProgression) -> Tuple[int, i
if new_level > old_level:
for L in range(old_level + 1, new_level + 1):
spells_list = newly_unlocked_abilities(class_name=entity.profession.name,
path="Hellknight",
skills = newly_unlocked_abilities(class_name=entity.profession.name,
path=entity.ability_pathway,
level=new_level,
per_tier=1,
primary=entity.profession.primary_stat)
_add_abilities(entity,spells_list)
_add_abilities(entity,skills)
if new_level > old_level:
entity.level = new_level
@@ -70,6 +78,30 @@ def grant_xp(entity:Entity, amount: int, prog: LevelProgression) -> Tuple[int, i
# ---------- reward + recalc helpers ----------
def calculate_skills_gained(current_level: int, target_level: int, chance_per_level: float = GLOBAL_CHANCE_PER_LEVEL) -> int:
"""
Returns the number of new skills a player might gain
when leveling from current_level to target_level.
chance_per_level: probability (01) of gaining a skill per level.
Guarantees at least 2 skills for small level gains.
"""
levels_gained = max(0, target_level - current_level)
if levels_gained == 0:
return 0
# Simulate random gain per level
gained = sum(1 for _ in range(levels_gained) if random.random() < chance_per_level)
# Guarantee at least 2 skills if the player barely leveled
if levels_gained < 3:
gained = max(gained, 2)
# Smooth scaling: for huge jumps, don't explode linearly
# (cap roughly at 40% of total levels gained)
cap = math.ceil(levels_gained * 0.4)
return min(gained, cap)
def _add_abilities(entity:Entity, abilities_list:list) -> None:
for ability in abilities_list:
entity.abilities.append(ability)

View File

@@ -10,32 +10,38 @@ Frostbinder:
nouns: ["Spike", "Lance", "Shard", "Burst", "Ray"]
of_things: ["Frost", "Winter", "Stillness", "Silence", "Cold"]
Bloodborn:
Khaosfire:
elements: ["curse", "shadow"]
adjectives: ["Tainted", "Cursed", "Corrupted", "Polluted", "Toxic", "Deadly"]
nouns: ["Poison", "Infection", "Wound", "Bane", "Plague", "Ravage", "Scourge"]
of_things: ["Infectious", "Contagion", "Disease", "Fungus", "Spore", "Seeds"]
Assassin:
Exsanguin:
elements: ["wind", "shadow"]
adjectives: ["Stealthy", "Deadly", "Ruthless", "Silent", "Lethal", "Fearsome"]
nouns: ["Dagger", "Knife", "Poison", "Bolt", "Arrows", "Scimitar", "Twinblade"]
of_things: ["Hunter", "Stalker", "Predator", "Ghost", "Shadow", "Silhouette"]
Cleric:
Sanctifier:
elements: ["fire", "light"]
adjectives: ["Holy", "Divine", "Pure", "Blessed", "Sacred", "Radiant"]
nouns: ["Flame", "Lightning", "Bolt", "Sword", "Shield", "Vocation", "Call"]
of_things: ["Healing", "Protection", "Blessing", "Salvation", "Redemption", "Mercy"]
Hexist:
Necromantia:
elements: ["curse", "shadow"]
adjectives: ["Dark", "Malefic", "Sinister", "Maledicta", "Witchlike", "Pestilential"]
nouns: ["Spell", "Hex", "Curse", "Influence", "Taint", "Bane", "Malice"]
of_things: ["Poison", "Deceit", "Demonic", "Abomination", "Evil", "Sinister"]
Ranger:
Huntsman:
elements: ["air", "wind"]
adjectives: ["Wild", "Free", "Wanderer", "Survivor", "Huntress", "Skilled"]
nouns: ["Arrows", "Rifle", "Bow", "Arrowhead", "Shotgun", "Crossbow", "Pistol"]
of_things: ["Wolves", "Forest", "Wilderness", "Hunter", "Tracking", "Ambush"]
Kratosphere:
elements: ["stone", "earth"]
adjectives: ["Rocky", "Solid", "Unyielding", "Resilient", "Firm"]
nouns: ["Shield", "Spike", "Breach", "Barrier", "Fortress"]
of_things: ["Stonework", "Rockfall", "Stonefall", "Earthquake", "Bedrock"]

View File

@@ -8,4 +8,5 @@ physical_attack_per_level: 1.1
physical_defense_per_level: 1.1
magic_attack_per_level: 1.7
magic_defense_per_level: 1.6
ability_paths: ['Frostbinder']
tags: [playable, mage]

View File

@@ -8,4 +8,5 @@ physical_attack_per_level: 1.8
physical_defense_per_level: 1.4
magic_attack_per_level: 1.2
magic_defense_per_level: 1.1
ability_paths: ['Exsanguin']
tags: [playable, flex]

View File

@@ -8,4 +8,5 @@ physical_attack_per_level: 1.8
physical_defense_per_level: 1.4
magic_attack_per_level: 1.2
magic_defense_per_level: 1.1
ability_paths: ['Khaosfire']
tags: [playable, martial]

View File

@@ -8,4 +8,5 @@ physical_attack_per_level: 1.1
physical_defense_per_level: 1.1
magic_attack_per_level: 1.7
magic_defense_per_level: 1.6
ability_paths: ['Sanctifier']
tags: [playable, mage]

View File

@@ -8,4 +8,5 @@ physical_attack_per_level: 1.8
physical_defense_per_level: 1.4
magic_attack_per_level: 1.2
magic_defense_per_level: 1.1
ability_paths: ['Kratosphere']
tags: [playable, martial]

View File

@@ -8,4 +8,5 @@ physical_attack_per_level: 1.1
physical_defense_per_level: 1.1
magic_attack_per_level: 1.7
magic_defense_per_level: 1.6
ability_paths: ['Necromantia']
tags: [playable, mage]

View File

@@ -8,4 +8,5 @@ physical_attack_per_level: 1.8
physical_defense_per_level: 1.4
magic_attack_per_level: 1.2
magic_defense_per_level: 1.1
ability_paths: ['Huntsman']
tags: [playable, flex]

View File

@@ -8,4 +8,5 @@ physical_attack_per_level: 1.1
physical_defense_per_level: 1.1
magic_attack_per_level: 1.7
magic_defense_per_level: 1.6
ability_paths: ['Hellknight']
tags: [playable, mage]