# merging.py from __future__ import annotations from typing import Iterable, Dict, List, Tuple from app.models.primitives import Attributes, Resources from app.models.races import RaceSheet from app.utils.hero_catalog import HeroArchetype, SkillDef, SpellDef def _add_attributes(a: Attributes, b: Attributes) -> Attributes: return Attributes( base_str=a.base_str + b.base_str, base_dex=a.base_dex + b.base_dex, base_int=a.base_int + b.base_int, base_wis=a.base_wis + b.base_wis, base_luk=a.base_luk + b.base_luk, base_cha=a.base_cha + b.base_cha, ) def _add_resources(a: Resources, b: Resources) -> Resources: return Resources( maxhp=a.maxhp + b.maxhp, hp=a.hp + b.hp, maxmp=a.maxmp + b.maxmp, mp=a.mp + b.mp, gold=a.gold + b.gold if hasattr(a, "gold") else getattr(b, "gold", 0), ) def _unique_chain(strings: Iterable[str]) -> List[str]: seen = set() out: List[str] = [] for s in strings: if s not in seen: out.append(s) seen.add(s) return out def merge_catalogs( race_skills: Dict[str, "SkillDef"], race_spells: Dict[str, "SpellDef"], class_skills: Dict[str, "SkillDef"], class_spells: Dict[str, "SpellDef"], ) -> Tuple[Dict[str, "SkillDef"], Dict[str, "SpellDef"]]: """ Return merged skill and spell maps. By default, CLASS overrides RACE on conflicts. """ skills = dict(race_skills) skills.update(class_skills) spells = dict(race_spells) spells.update(class_spells) return skills, spells def merge_sheets(race: RaceSheet, cls: HeroArchetype): """ Compute final base Attributes/Resources and starting lists, plus merged catalogs. """ attrs = _add_attributes(race.base_attributes, cls.base_attributes) res = _add_resources(race.starting_resources, cls.starting_resources) starting_skills = _unique_chain([*race.starting_skills, *cls.starting_skills]) starting_spells = _unique_chain([*race.starting_spells, *cls.starting_spells]) skills_catalog, spells_catalog = merge_catalogs( race.skills, race.spells, cls.skills, cls.spells ) return attrs, res, starting_skills, starting_spells, skills_catalog, spells_catalog