106 lines
4.1 KiB
Python
106 lines
4.1 KiB
Python
# utils/leveling.py
|
|
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
|
|
|
|
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.
|
|
Optionally applies all level-up rewards from current_level+1 .. target_level (idempotent if you recompute stats from level).
|
|
"""
|
|
# ensures we never try to go above the max level in the game
|
|
target_level = max(1, min(target_level, prog.max_level))
|
|
current = entity.level
|
|
|
|
# if not changing levels, just set the xp and move along
|
|
if current == target_level:
|
|
entity.xp = prog.xp_for_level(target_level)
|
|
_recalc(entity)
|
|
|
|
# Set final level + floor XP
|
|
entity.level = target_level
|
|
entity.xp = prog.xp_for_level(target_level)
|
|
|
|
# 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",
|
|
level=target_level,
|
|
per_tier=1,
|
|
primary=entity.profession.primary_stat)
|
|
_add_abilities(entity,spells_list)
|
|
|
|
_recalc(entity)
|
|
|
|
def grant_xp(entity:Entity, amount: int, prog: LevelProgression) -> Tuple[int, int]:
|
|
"""
|
|
Add XP and auto-level if thresholds crossed.
|
|
Returns (old_level, new_level).
|
|
"""
|
|
old_level = entity.level or 1
|
|
entity.xp = entity.xp + int(amount)
|
|
|
|
new_level = prog.level_for_xp(entity.xp)
|
|
|
|
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",
|
|
level=new_level,
|
|
per_tier=1,
|
|
primary=entity.profession.primary_stat)
|
|
_add_abilities(entity,spells_list)
|
|
|
|
if new_level > old_level:
|
|
entity.level = new_level
|
|
_recalc(entity)
|
|
|
|
# --- compute XP to next level ---
|
|
if new_level >= prog.max_level:
|
|
# Maxed out
|
|
entity.xp_to_next_level = 0
|
|
else:
|
|
next_floor = prog.xp_for_level(new_level + 1)
|
|
entity.xp_to_next_level = max(0, next_floor - entity.xp)
|
|
|
|
|
|
return old_level, new_level
|
|
|
|
# ---------- reward + recalc helpers ----------
|
|
|
|
def _add_abilities(entity:Entity, abilities_list:list) -> None:
|
|
for ability in abilities_list:
|
|
entity.abilities.append(ability)
|
|
|
|
def _recalc(entity:Entity) -> None:
|
|
"""
|
|
Recompute derived stats from entity.level + profession.
|
|
Replace with your actual formulas.
|
|
"""
|
|
L = entity.level
|
|
prof = entity.profession
|
|
|
|
# scale attack/defense by per-level gains
|
|
if prof:
|
|
base_pa = entity.profession.physical_attack_per_level or 0
|
|
base_pd = entity.profession.physical_defense_per_level or 0
|
|
base_ma = entity.profession.magic_attack_per_level or 0
|
|
base_md = entity.profession.magic_defense_per_level or 0
|
|
|
|
entity.physical_attack = round(base_pa + prof.physical_attack_per_level * (L - 1),2)
|
|
entity.physical_defense = round(base_pd + prof.physical_defense_per_level * (L - 1),2)
|
|
entity.magic_attack = round(base_ma + prof.magic_attack_per_level * (L - 1),2)
|
|
entity.magic_defense = round(base_md + prof.magic_defense_per_level * (L - 1),2)
|
|
|
|
# HP/MP growth from profession base
|
|
if prof:
|
|
entity.status.max_hp = entity.status.max_hp + int(prof.base_hp) + int((L * prof.base_hp) * 0.5)
|
|
entity.status.max_mp = entity.status.max_mp + int(prof.base_mp) + int((L * prof.base_mp) * 0.5)
|
|
|
|
# set current to max if you keep current_hp/mp
|
|
entity.status.current_hp = entity.status.max_hp
|
|
entity.status.current_mp = entity.status.max_mp
|
|
|