diff --git a/faction_turns/faction_utils.py b/faction_turns/faction_utils.py index 5ae33d1..790da7d 100644 --- a/faction_turns/faction_utils.py +++ b/faction_turns/faction_utils.py @@ -439,13 +439,13 @@ FACTION_DEFAULTS: dict = { "xp": 0, "state": "active", "locations": [], - "current_goal": "", "faction_tags": [], "tags_listen": [], "dw_category": "", - "impulse": "", "enemies": [], # явные враги (имена файлов фракций) "allies": [], # явные союзники (имена файлов фракций) + "global_goals": [], + "wwn_goal": None, } @@ -499,6 +499,29 @@ def _clean_assets(fm: dict) -> list[dict]: result.append(a) return result +def clean_global_goals(raw: list) -> list: + """ + Очищает список глобальных целей. + Каждый элемент должен быть словарем с 'description' (str) и 'tactics' (list[str]). + Удаляет дубли по description (оставляет первый). + """ + if not raw: + return [] + cleaned = [] + seen = set() + for item in raw: + if not isinstance(item, dict): + continue + desc = item.get("description", "").strip() + if not desc or desc in seen: + continue + seen.add(desc) + tactics = _clean_list_field(item.get("tactics")) + cleaned.append({ + "description": desc, + "tactics": tactics + }) + return cleaned def normalize_faction_frontmatter(fm: dict) -> dict: """ @@ -520,14 +543,34 @@ def normalize_faction_frontmatter(fm: dict) -> dict: (list, dict)) else default # Списочные поля (нарративные) — чистим от пустых заглушек - for list_field in ("locations", "motivations", "headquarters", "leaders", - "enemies"): + for list_field in ("locations", "headquarters", "leaders", "enemies"): fm[list_field] = _clean_list_field(fm.get(list_field)) # tags_listen / faction_tags тоже чистим fm["faction_tags"] = _clean_list_field(fm.get("faction_tags")) fm["tags_listen"] = _clean_list_field(fm.get("tags_listen")) + # Обработка global_goals + if "global_goals" in fm: + fm["global_goals"] = clean_global_goals(fm.get("global_goals")) + else: + fm["global_goals"] = [] + + # Обработка wwn_goal (если есть) + wwn = fm.get("wwn_goal") + if wwn is not None: + # Если это словарь, но без обязательных полей — приводим к None + if not isinstance(wwn, dict) or not wwn.get("type"): + fm["wwn_goal"] = None + else: + # Гарантируем наличие ключей + wwn.setdefault("status", "active") + wwn.setdefault("progress", 0) + wwn.setdefault("condition", {}) + # xp_reward можно вычислить позже, здесь не трогаем + fm["wwn_goal"] = wwn + # Если wwn_goal = None — оставляем как есть + # Активы fm["assets"] = _clean_assets(fm) diff --git a/faction_turns/propose_faction_turn.py b/faction_turns/propose_faction_turn.py index 9c4871d..396462a 100644 --- a/faction_turns/propose_faction_turn.py +++ b/faction_turns/propose_faction_turn.py @@ -446,10 +446,41 @@ def _format_faction_section( # Нарративные теги для LLM dw_cat = fm.get("dw_category", "") - impulse = fm.get("impulse", "") + global_goals = fm.get("global_goals", []) + wwn_goal = fm.get("wwn_goal", {}) + narrative_str = "" - if dw_cat or impulse: - narrative_str = f"\n**Нарратив:** dw_category={dw_cat} | impulse=*\"{impulse}\"*" + if dw_cat: + narrative_str += f"\n**Нарратив:** dw_category={dw_cat}" + + # Глобальные цели + if global_goals: + narrative_str += "\n**Глобальные цели:**" + for goal in global_goals: + desc = goal.get("description", "") + tactics = goal.get("tactics", []) + narrative_str += f"\n - **{desc}**" + if tactics: + narrative_str += "\n Тактики: " + ", ".join(tactics) + + # WWN-цель + if wwn_goal and wwn_goal.get("status") == "active": + goal_type = wwn_goal.get("type", "Неизвестная") + progress = wwn_goal.get("progress", 0) + condition = wwn_goal.get("condition", {}) + # Пытаемся показать читаемый прогресс + if condition.get("kind") == "accumulate_damage": + target = condition.get("target", "?") + progress_str = f"{progress}/{target} урона" + elif condition.get("kind") == "create_boi": + target_loc = condition.get("target_location", "?") + progress_str = f"{progress}/1 (локация {target_loc})" + else: + progress_str = f"{progress} (условие: {condition})" + narrative_str += f"\n**WWN-цель:** {goal_type} — прогресс {progress_str}" + else: + narrative_str += "\n**WWN-цель:** нет активной" + # Faction tags WWN tags = fm.get("faction_tags", []) @@ -535,6 +566,7 @@ def _format_faction_section( return ( f"### {idx}. `{name}` | Initiative: {initiative} | {state_label.upper()}\n" f"**Файл:** `{name}.md`\n" # Добавьте эту строку + f"**Для resolve:** faction_file: \"{name}\"" f"**Статы:** {status_str}" f"{tags_str}" f"{narrative_str}" diff --git a/faction_turns/resolve_faction_turn.py b/faction_turns/resolve_faction_turn.py index 49c174a..3af0267 100644 --- a/faction_turns/resolve_faction_turn.py +++ b/faction_turns/resolve_faction_turn.py @@ -162,6 +162,7 @@ class FactionTurnResolveRequest(TypedDict, total=False): factions_folder: str # default "Фракции" events_folder: str # default "События" actions: list[FactionAction] # list[FactionAction] + new_goals: dict[str, dict] # --------------------------------------------------------------------------- @@ -924,6 +925,64 @@ def _resolve_upgrade_attribute(action: dict, faction_fm: dict) -> Optional[dict] "new_hp_max": new_hp_max, } +# --------------------------------------------------------------------------- +# Автоматическое обновление прогресса WWN-целей +# --------------------------------------------------------------------------- + +def update_wwn_goal_progress(fm: dict, action: dict, result: dict) -> None: + """ + Обновляет прогресс активной wwn_goal на основе выполненного действия. + Вызывается после каждого действия. + """ + wwn = fm.get("wwn_goal") + if not wwn or wwn.get("status") != "active": + return + + goal_type = wwn.get("type") + condition = wwn.get("condition", {}) + progress = wwn.get("progress", 0) + + if goal_type == "Blood the Enemy": + # При успешной атаке добавляем нанесённый урон + if action.get("action_type") == "Attack" and result.get("attacker_wins"): + damage = result.get("damage_dealt", 0) + wwn["progress"] = progress + damage + + elif goal_type == "Expand Influence": + # При создании BoI + if action.get("action_type") == "Expand Influence" and result.get("boi_created"): + wwn["progress"] = 1 + + # Другие типы целей можно добавить позже + # Для целей с таймером (Peaceable Kingdom) нужна отдельная проверка в конце хода + +def check_goal_completion(fm: dict) -> bool: + """ + Проверяет, достигла ли активная цель порога. + Возвращает True, если цель выполнена (тогда начисляет XP и меняет статус). + """ + wwn = fm.get("wwn_goal") + if not wwn or wwn.get("status") != "active": + return False + + goal_type = wwn.get("type") + condition = wwn.get("condition", {}) + progress = wwn.get("progress", 0) + + completed = False + if goal_type == "Blood the Enemy": + target = condition.get("target", 0) + completed = progress >= target + elif goal_type == "Expand Influence": + completed = progress >= 1 + # Другие типы + + if completed: + wwn["status"] = "completed" + xp_gain = wwn.get("xp_reward", 0) + fm["xp"] = fm.get("xp", 0) + xp_gain + return True + return False # --------------------------------------------------------------------------- # Главная функция @@ -1086,6 +1145,47 @@ def resolve_faction_turn( fm["last_action"] = f"{action_type}: {action.get('attacking_asset') or action.get('asset_name') or ''}" action_results.append(result) + # Обновляем прогресс цели после каждого действия + update_wwn_goal_progress(fm, action, result) + + # Проверяем завершение целей после всех действий + for ff, (fm, body, path) in all_factions_data.items(): + if check_goal_completion(fm): + # Можно добавить лог о выполнении + pass + + # Шаг 2.5: Обработка new_goals (полная замена) + new_goals = req.get("new_goals", {}) + for faction_file, goal_update in new_goals.items(): + if faction_file not in all_factions_data: + continue + fm, body, path = all_factions_data[faction_file] + + # 1. Обновление global_goals (если поле присутствует) + if "global_goals" in goal_update: + new_list = goal_update["global_goals"] + # Используем clean_global_goals из faction_utils + from faction_utils import clean_global_goals + fm["global_goals"] = clean_global_goals(new_list) + + # 2. Обновление wwn_goal (если поле присутствует) + if "wwn_goal" in goal_update: + new_goal = goal_update["wwn_goal"] + if new_goal is None: + fm["wwn_goal"] = None + else: + # Создаём новый объект цели + goal_type = new_goal.get("type") + if goal_type: + from wwn_mechanics import get_xp_reward_for_goal + fm["wwn_goal"] = { + "type": goal_type, + "xp_reward": get_xp_reward_for_goal(goal_type, fm), + "status": "active", + "condition": new_goal.get("condition", {}), + "progress": 0 + } + # если тип отсутствует, ничего не делаем (оставляем старую цель) # Шаг 3: Записываем все изменённые файлы фракций в vault write_errors: list[str] = [] diff --git a/faction_turns/wwn_mechanics.py b/faction_turns/wwn_mechanics.py index 1eecdc6..04a2720 100644 --- a/faction_turns/wwn_mechanics.py +++ b/faction_turns/wwn_mechanics.py @@ -449,6 +449,43 @@ def format_asset_line(asset: dict, is_base: bool = False) -> str: ) +# --------------------------------------------------------------------------- +# WWN Goals — XP rewards +# --------------------------------------------------------------------------- + +WWN_GOAL_XP_REWARD = { + "Blood the Enemy": 2, + "Eliminate Target": 1, + "Expand Influence": 1, + "Peaceable Kingdom": 1, + "Wealth of Kingdoms": 2, + # Для динамических целей используем функции +} + +def get_xp_reward_for_goal(goal_type: str, faction_fm: dict = None) -> int: + """ + Возвращает XP-награду за выполнение цели. + Для целей с динамической наградой (Destroy the Foe, Root Out the Enemy и т.п.) + вычисляет по формулам. + """ + if goal_type in ("Destroy the Foe", "Root Out the Enemy", "Sphere Dominance", "Invincible Valor"): + if faction_fm is None: + return 0 + # Пример для Destroy the Foe: 2 + среднее атрибутов (округление вверх?) + if goal_type == "Destroy the Foe": + avg = (faction_fm.get("force", 0) + faction_fm.get("wealth", 0) + faction_fm.get("cunning", 0)) / 3 + return 2 + round(avg) + elif goal_type == "Root Out the Enemy": + avg = (faction_fm.get("force", 0) + faction_fm.get("wealth", 0) + faction_fm.get("cunning", 0)) / 3 + return max(1, round(avg / 2)) + elif goal_type == "Sphere Dominance": + # За каждые 2 уничтоженных - 1 XP, но базово 1? + return 1 # Упрощённо + elif goal_type == "Invincible Valor": + return 2 + return WWN_GOAL_XP_REWARD.get(goal_type, 0) + + # --------------------------------------------------------------------------- # Тест # --------------------------------------------------------------------------- diff --git a/generate_npc/generate_npc.py b/generate_npc/generate_npc.py index ade6383..8959ffd 100644 --- a/generate_npc/generate_npc.py +++ b/generate_npc/generate_npc.py @@ -38,6 +38,58 @@ from psycho_types import get_psychosocial_type_and_archetype from bio_tables import generate_bio +# ─── symspellpy: индекс регионов ────────────────────────────────────────────── +from symspellpy import SymSpell, Verbosity +from regions import ( + REGIONAL_OVERWRITES, PLANAR_OVERWRITES, + WORLD_INDEX, REGION_INDEX, _clean, +) + +_SYM = SymSpell(max_dictionary_edit_distance=2, prefix_length=7) +for _cleaned_alias in {a for (_scope, a) in REGION_INDEX}: + _SYM.create_dictionary_entry(_cleaned_alias, 1) + + +def _resolve_region(world_param: str | None, region_param: str | None) -> dict | None: + """ + Возвращает словарь chances из региона/плана либо None (глобальные шансы). + world_param, region_param — сырые строки от нейросети (англ.). + """ + if not world_param: + return None + + world_clean = _clean(world_param) + resolved_world = WORLD_INDEX.get(world_clean) + + # Опечатка в имени мира — пытаемся исправить + if resolved_world is None: + best = _SYM.lookup(world_clean, Verbosity.CLOSEST, max_edit_distance=2) + if best: + resolved_world = WORLD_INDEX.get(best[0].term) + if resolved_world is None: + return None + + # План: сам мир и есть набор + if resolved_world in PLANAR_OVERWRITES: + return PLANAR_OVERWRITES[resolved_world]["chances"] + + # Прайм-мир: нужен регион + if not region_param: + return None + scope = resolved_world + reg_clean = _clean(region_param) + + region_key = REGION_INDEX.get((scope, reg_clean)) + if region_key is None: + best = _SYM.lookup(reg_clean, Verbosity.CLOSEST, max_edit_distance=2) + if best: + region_key = REGION_INDEX.get((scope, best[0].term)) + if region_key is None: + return None + + return REGIONAL_OVERWRITES[scope][region_key]["chances"] + + ALIGNMENTS = ["LG", "LN", "LE", "NG", "TN", "NE", "CG", "CN", "CE"] AGE_ORDER = list(AGES.keys()) # ["Youth", "Adult", "MiddleAged", "Old", "Ancient"] @@ -95,6 +147,30 @@ def _filtered_race_chances(allowed_races: list[str]) -> dict[str, float]: return _normalize_chances(pool) +def _region_race_chances(region_chances: dict, allowed_races: list[str]) -> dict[str, float]: + """ + Строит нормированный пул рас на основе региональной перезаписи. + Расы, не указанные в регионе, имеют шанс 0 (не попадают в пул). + allowed_races (если задан) дополнительно сужает пул. + """ + pool = {r: d["chance"] for r, d in region_chances.items() if r in RACES} + if allowed_races: + pool = {r: c for r, c in pool.items() if r in allowed_races} + return _normalize_chances(pool) + + +def _region_subrace_chances(region_chances: dict, race_name: str) -> dict[str, float] | None: + """ + Возвращает нормированный пул подрас из региона для конкретной расы, + либо None — тогда используются стандартные подрасы из RACES. + """ + entry = region_chances.get(race_name, {}) + subs = entry.get("subraces") + if not subs: + return None + return _normalize_chances(dict(subs)) + + def _filtered_age_chances(min_age: str | None, max_age: str | None) -> dict[str, float]: """ Возвращает нормированный пул возрастных категорий @@ -133,6 +209,7 @@ def _generate_single_npc( commoner_chance: float, lang_chances: dict[str, float], gender_param: str = "Any", + region_chances: dict | None = None, ) -> str: """Генерирует одного NPC и возвращает строку описания.""" @@ -148,7 +225,15 @@ def _generate_single_npc( race_name = _weighted_pick(race_chances) race_data = RACES[race_name] subrace_name = None - if "subraces" in race_data and race_data["subraces"]: + + # Приоритет: региональные подрасы, иначе стандартные из RACES + region_subs = ( + _region_subrace_chances(region_chances, race_name) + if region_chances else None + ) + if region_subs: + subrace_name = _weighted_pick(region_subs) + elif "subraces" in race_data and race_data["subraces"]: sub_pool = _normalize_chances( {s: d["chance"] for s, d in race_data["subraces"].items()} ) @@ -342,6 +427,29 @@ class HenchmanConstraint(TypedDict, total=False): count: Количество NPC для генерации. По умолчанию 1. level: Уровень NPC. По умолчанию 1. + + world: Космологический уровень сцены (англ.). Прайм-мир + ("Toril", "Krynn", "Oerth") ЛИБО план существования ("feywild", + "shadowfell", "lower_planes_le/ne/ce", + "upper_planes_lg/ng/cg", "planes_of_chaos", + "plane_of_law"). Пусто → глобальные шансы RACES. + + region: Регион ВНУТРИ прайм-мира (обычное англ. название, + код нормализует через symspellpy). + Определение СТРОГО ПО ПРИОРИТЕТУ: + 0. Никаких артиклей нигде применять нельзя, пример, nelanther_isles, а не the_nelanther_isles + 1. Если world — план, region опускается. + 2. Подземелье/пещера/канализация → "Underdark". + 3. Крупный город → название города + (Waterdeep, Baldur's Gate, Neverwinter, + Suzail, Athkatla, Calimport, Menzoberranzan). + 4. Дикая местность → историческая область по + ближайшему городу/хребту/лесу + (Western Heartlands, Silver Marches, Cormyr, + Amn, Tethyr, Thay, Chult и т.д.). + 5. Море/побережье → ближайшая страна или + уникальная акватория (Sea of Fallen Stars). + 6. Не определить → пусто (глобальные шансы). """ gender: str # "M", "F", "Any" — по умолчанию "Any" allowed_races: list[str] @@ -352,6 +460,8 @@ class HenchmanConstraint(TypedDict, total=False): allowed_languages: list[str] count: int level: int + world: str + region: str def generate_npc(debug_description: str, constraints: list[HenchmanConstraint]) -> str: @@ -383,10 +493,18 @@ def generate_npc(debug_description: str, constraints: list[HenchmanConstraint]) level = int(constraint.get("level", 1)) gender_param = constraint.get("gender", "Any") + # Региональная перезапись: разрешаем регион/план + world_param = constraint.get("world", None) + region_param = constraint.get("region", None) + region_chances = _resolve_region(world_param, region_param) + # Нормируем пулы — это ключевая логика: - # Если из 10 доступных рас разрешены только 2, их относительные - # шансы нормируются к 100% и рандом идёт только в этом подпространстве. - race_chances = _filtered_race_chances(allowed_races) + # Если задан регион — пул рас строится из него (остальные расы = 0), + # иначе — стандартная фильтрация по allowed_races. + if region_chances: + race_chances = _region_race_chances(region_chances, allowed_races) + else: + race_chances = _filtered_race_chances(allowed_races) age_chances = _filtered_age_chances(min_age, max_age) lang_chances = _filtered_language_chances(allowed_langs) @@ -402,6 +520,7 @@ def generate_npc(debug_description: str, constraints: list[HenchmanConstraint]) commoner_chance, lang_chances, gender_param, + region_chances, ) lines.append(f"#{j} — {npc_line}") diff --git a/generate_npc/languages.py b/generate_npc/languages.py index 055249b..3b20cca 100644 --- a/generate_npc/languages.py +++ b/generate_npc/languages.py @@ -14,7 +14,7 @@ dead_languages = [Aragrakh, Hulgorkyn, Loross, Netherese, Roushoum, Seldruin, Th # ─── Живые языки ───────────────────────────────────────────────────────────── -Aglorondan = {"name": "Aglorondan", "alphabet": "Espruar", "speakers": "Aglarond, Altumbel", "chance": 2.50} +Aglarondan = {"name": "Aglarondan", "alphabet": "Espruar", "speakers": "Aglarond, Altumbel", "chance": 2.50} Alzhedo = {"name": "Alzhedo", "alphabet": "Thorass", "speakers": "Calimshan", "chance": 4.50} Chessentan = {"name": "Chessentan", "alphabet": "Thorass", "speakers": "Chessenta", "chance": 3.00} Chondathan = {"name": "Chondathan", "alphabet": "Thorass", "speakers": "Amn, Chondath, Cormyr, the Dalelands, the Dragon Coast, the civilized North, Sembia, ...", "chance": 12.00} @@ -61,7 +61,7 @@ GithLanguage = {"name": "Gith", "alphabet": "Espruar", "speakers": "Ги LeoninLanguage= {"name": "Leonin", "alphabet": "Common", "speakers": "Леонины", "chance": 0.50} living_languages = [ - Aglorondan, Alzhedo, Chessentan, Chondathan, Chultan, Common, + Aglarondan, Alzhedo, Chessentan, Chondathan, Chultan, Common, Damaran, Dambrathan, Durpari, Halruaan, Illuskan, Lantanese, Midani, Mulani, Mulhorandi, MulhorandiVar, Nexalan, Rashemi, Serusan, Shaaran, KoryoLanguage, Shou, Tashalan, Tuigan, Turmic, diff --git a/generate_npc/abilities.js b/generate_npc/old_js/abilities.js similarity index 100% rename from generate_npc/abilities.js rename to generate_npc/old_js/abilities.js diff --git a/generate_npc/ages.js b/generate_npc/old_js/ages.js similarity index 100% rename from generate_npc/ages.js rename to generate_npc/old_js/ages.js diff --git a/generate_npc/backgrounds.js b/generate_npc/old_js/backgrounds.js similarity index 100% rename from generate_npc/backgrounds.js rename to generate_npc/old_js/backgrounds.js diff --git a/generate_npc/classes.js b/generate_npc/old_js/classes.js similarity index 100% rename from generate_npc/classes.js rename to generate_npc/old_js/classes.js diff --git a/generate_npc/generate_npc.js b/generate_npc/old_js/generate_npc.js similarity index 100% rename from generate_npc/generate_npc.js rename to generate_npc/old_js/generate_npc.js diff --git a/generate_npc/languages.js b/generate_npc/old_js/languages.js similarity index 100% rename from generate_npc/languages.js rename to generate_npc/old_js/languages.js diff --git a/generate_npc/norminv.js b/generate_npc/old_js/norminv.js similarity index 100% rename from generate_npc/norminv.js rename to generate_npc/old_js/norminv.js diff --git a/generate_npc/psychoTypes.js b/generate_npc/old_js/psychoTypes.js similarity index 100% rename from generate_npc/psychoTypes.js rename to generate_npc/old_js/psychoTypes.js diff --git a/generate_npc/races.js b/generate_npc/old_js/races.js similarity index 100% rename from generate_npc/races.js rename to generate_npc/old_js/races.js diff --git a/generate_npc/skills.js b/generate_npc/old_js/skills.js similarity index 100% rename from generate_npc/skills.js rename to generate_npc/old_js/skills.js diff --git a/generate_npc/races.py b/generate_npc/races.py index 5c41254..e272e23 100644 --- a/generate_npc/races.py +++ b/generate_npc/races.py @@ -6,6 +6,8 @@ from languages import ( Infernal, Primordial, Sylvan, Abyssal, GithLanguage, LeoninLanguage, Undercommon, Alzhedo, Chondathan, Damaran, Illuskan, KoryoLanguage, Mulani, Rashemi, Shou, + Tashalan, Halruaan, Durpari, Dambrathan, Shaaran, Tuigan, Midani, + Aglarondan, Lantanese, Nexalan, living_languages_except_common, ) @@ -43,9 +45,22 @@ RACES: dict = { "Dwarf": { "chance": 10.00, "subraces": { + # Toril "Gold/Hill": {"chance": 50.00, "languages": [Common, Dwarvish]}, "Shield/Mountain":{"chance": 40.00, "languages": [Common, Dwarvish]}, "Duergar": {"chance": 10.00, "languages": [Common, Dwarvish, Undercommon]}, + + # Krynn + "Hylar": {"names": "Dwarf", "languages": [Common, Dwarvish]}, + "Neidar": {"names": "Dwarf", "languages": [Common, Dwarvish]}, + "Daewar": {"names": "Dwarf", "languages": [Common, Dwarvish]}, + "Klar": {"names": "Dwarf", "languages": [Common, Dwarvish]}, + "Theiwar": {"names": "Dwarf", "languages": [Common, Dwarvish, Undercommon]}, + "Daergar": {"names": "Dwarf", "languages": [Common, Dwarvish, Undercommon]}, + + # Oerth + "Hill": {"names": "Dwarf", "languages": [Common, Dwarvish]}, + "Mountain": {"names": "Dwarf", "languages": [Common, Dwarvish]}, }, "names": { "M": ["Адрик","Альберих","Баренд","Барундар","Баэрн","Броттор","Бруенор","Вондал", @@ -67,6 +82,7 @@ RACES: dict = { "Elf": { "chance": 10.00, "subraces": { + # Toril "Avariel": {"chance": 0.50, "names": "Moon", "languages": [Common, Elvish]}, "Drow": { "chance": 10.00, @@ -102,6 +118,16 @@ RACES: dict = { "Shadar-kai":{"chance": 2.50, "names": "Moon", "languages": [Common, Elvish]}, "Sun": {"chance": 25.00, "names": "Moon", "languages": [Common, Elvish]}, "Wood": {"chance": 25.00, "names": "Moon", "languages": [Common, Elvish]}, + + # Krynn + "Qualinesti": {"names": "Moon", "languages": [Common, Elvish]}, + "Silvanesti": {"names": "Moon", "languages": [Common, Elvish]}, + "Kagonesti": {"names": "Moon", "languages": [Common, Elvish]}, + + # Oerth + "High": {"names": "Moon", "languages": [Common, Elvish]}, + "Gray": {"names": "Moon", "languages": [Common, Elvish]}, + "Valley": {"names": "Moon", "languages": [Common, Elvish]}, }, "languages": [Common, Elvish], }, @@ -132,8 +158,12 @@ RACES: dict = { "Gnome": { "chance": 2.00, "subraces": { + # Toril "Forest": {"chance": 50.00}, "Rock": {"chance": 50.00}, + + # Krynn + "Tinker": {"names": "Gnome", "languages": [Common, GnomishLanguage]}, }, "names": { "M": ["Алвин","Алстон","Боддинок","Брок","Бургелл","Бюргел","Варрин","Вренн","Гербо", @@ -225,6 +255,7 @@ RACES: dict = { "Human": { "chance": 54.70, "subraces": { + # Toril "Calishite": { "chance": 20.00, "names": {"M":["Асеир","Бардеид","Зашеир","Кхемед","Мехмен","Судейман","Хасеид"], @@ -284,6 +315,38 @@ RACES: dict = { "names": "Chondathan", "languages": [Common, Chondathan], }, + "Turami": {"chance": 2.00, "names": "Calishite", "languages": [Common]}, + "Chessentan": {"chance": 5.00, "names": "Mulan", "languages": [Common]}, + "Chultan": {"chance": 0.00, "names": "Calishite", "languages": [Common, Tashalan]}, + "Halruaan": {"chance": 0.00, "names": "Mulan", "languages": [Common, Halruaan]}, + "Durpari": {"chance": 0.00, "names": "Mulan", "languages": [Common, Durpari]}, + "Dambrathan": {"chance": 0.00, "names": "Calishite", "languages": [Common, Dambrathan]}, + "Shaaran": {"chance": 0.00, "names": "Calishite", "languages": [Common, Shaaran]}, + "Tuigan": {"chance": 0.00, "names": "Rashemi", "languages": [Common, Tuigan]}, + "Bedine": {"chance": 0.00, "names": "Calishite", "languages": [Common, Midani]}, + "Netherese": {"chance": 0.00, "names": "Chondathan", "languages": [Common, Chondathan]}, + "Aglarondan": {"chance": 0.00, "names": "Chondathan", "languages": [Common, Aglarondan]}, + "Lantanese": {"chance": 0.00, "names": "Chondathan", "languages": [Common, Lantanese]}, + "Nexalan": {"chance": 0.00, "names": "Calishite", "languages": [Common, Nexalan]}, + "Payit": {"chance": 0.00, "names": "Calishite", "languages": [Common, Tashalan]}, + "Ffolk": {"chance": 0.00, "names": "Illuskan", "languages": [Common, Illuskan]}, + "Northlander":{"chance": 0.00, "names": "Illuskan", "languages": [Common, Illuskan]}, + "Reghed": {"chance": 0.00, "names": "Illuskan", "languages": [Common, Illuskan]}, + "Kozakuran": {"chance": 0.00, "names": "Shou", "languages": [Common, Shou]}, + "Wanese": {"chance": 0.00, "names": "Shou", "languages": [Common, Shou]}, + "Korobokuru": {"chance": 0.00, "names": "Dwarf", "languages": [Common, Shou]}, + + # Krynn + "Solamnic": {"names": "Chondathan", "languages": [Common]}, + "Abanasinian": {"names": "Chondathan", "languages": [Common]}, + "Plainsfolk": {"names": "Rashemi", "languages": [Common]}, + "Ice Folk": {"names": "Illuskan", "languages": [Common]}, + + # Oerth + "Oeridian": {"names": "Chondathan", "languages": [Common]}, + "Suel": {"names": "Illuskan", "languages": [Common]}, + "Flan": {"names": "Rashemi", "languages": [Common]}, + "Baklunish": {"names": "Calishite", "languages": [Common]}, }, }, "Kenku": { @@ -294,6 +357,27 @@ RACES: dict = { }, "languages": [Common, Primordial], }, + + "Kender": { + "chance": 0.50, + "names": "Halfling", + "languages": [Common, HalflingLanguage], + }, + + "Minotaur": { + "chance": 0.10, + "names": { + "M": ["Астерион","Бракус","Минос","Таурус","Каллхейм"], + "F": ["Ариадна","Хелла","Мина","Таура"], + }, + "languages": [Common, GiantLanguage], + }, + "Half-Ogre": { + "chance": 0.10, + "names": "Half-Orc", + "languages": [Common, GiantLanguage], + }, + "Kobold": {"chance": 0.50, "names": [], "languages": [Common, Draconic]}, "Leonin": { "chance": 0.10, diff --git a/generate_npc/regions.py b/generate_npc/regions.py new file mode 100644 index 0000000..301a5c9 --- /dev/null +++ b/generate_npc/regions.py @@ -0,0 +1,964 @@ +# regions.py +""" +Региональные и планарные распределения рас для Forgotten Realms. +Принцип: если регион найден, расы/подрасы, НЕ указанные в нём, имеют шанс 0. +Если у расы не указан ключ "subraces" - используются стандартные подрасы из RACES. +Сопоставление введённого названия с ключом - через symspellpy по алиасам. +""" + +# ─── Прайм-миры: регионы ───────────────────────────────────────────────────── + +REGIONAL_OVERWRITES: dict = { + "Toril": { + "sword_coast_north": { + "aliases": ["Sword Coast North", "Savage Frontier", "Frozenfar", + "Luskan", "Neverwinter", "Port Llast", "Mirabar"], + "chances": { + "Human": {"chance": 58.0, "subraces": {"Illuskan": 70.0, "Damaran": 20.0, "Chondathan": 10.0}}, + "Dwarf": {"chance": 14.0, "subraces": {"Shield/Mountain": 90.0, "Gold/Hill": 10.0}}, + "Half-Elf": {"chance": 7.0, "subraces": {"Moon": 60.0, "Wood": 40.0}}, + "Elf": {"chance": 6.0, "subraces": {"Moon": 50.0, "Wood": 50.0}}, + "Orc": {"chance": 5.0}, + "Half-Orc": {"chance": 4.0}, + "Halfling": {"chance": 3.0, "subraces": {"Lightfoot": 70.0, "Stout": 30.0}}, + "Goliath": {"chance": 2.0}, + "Tiefling": {"chance": 1.0}, + }, + }, + "silver_marches": { + "aliases": ["Silver Marches", "Luruar", "Silverymoon", "Sundabar", + "Mithral Hall", "Everlund", "Citadel Adbar"], + "chances": { + "Human": {"chance": 42.0, "subraces": {"Illuskan": 50.0, "Chondathan": 40.0, "Damaran": 10.0}}, + "Dwarf": {"chance": 22.0, "subraces": {"Shield/Mountain": 95.0, "Gold/Hill": 5.0}}, + "Elf": {"chance": 13.0, "subraces": {"Moon": 60.0, "Sun": 25.0, "Wood": 15.0}}, + "Half-Elf": {"chance": 10.0, "subraces": {"Moon": 80.0, "Sun": 20.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 70.0, "Stout": 30.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 60.0, "Forest": 40.0}}, + "Orc": {"chance": 3.0}, + "Half-Orc": {"chance": 2.0}, + }, + }, + "western_heartlands": { + "aliases": ["Western Heartlands", "Elturgard", "Elturel", "Beregost", + "Candlekeep", "Sunset Mountains", "Scornubel"], + "chances": { + "Human": {"chance": 72.0, "subraces": {"Chondathan": 55.0, "Tethyrian": 35.0, "Calishite": 7.0, "Illuskan": 3.0}}, + "Halfling": {"chance": 9.0, "subraces": {"Lightfoot": 85.0, "Ghostwise": 10.0, "Stout": 5.0}}, + "Half-Elf": {"chance": 6.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Elf": {"chance": 5.0, "subraces": {"Wood": 60.0, "Moon": 40.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Shield/Mountain": 60.0, "Gold/Hill": 40.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + "Half-Orc": {"chance": 1.0}, + }, + }, + "waterdeep": { + "aliases": ["Waterdeep", "City of Splendors", "Crown of North"], + "chances": { + "Human": {"chance": 60.0, "subraces": {"Illuskan": 35.0, "Tethyrian": 30.0, "Chondathan": 25.0, "Calishite": 5.0, "Shou": 5.0}}, + "Half-Elf": {"chance": 9.0, "subraces": {"Moon": 75.0, "Sun": 15.0, "Wood": 10.0}}, + "Elf": {"chance": 8.0, "subraces": {"Moon": 65.0, "Sun": 20.0, "Wood": 15.0}}, + "Dwarf": {"chance": 8.0, "subraces": {"Shield/Mountain": 85.0, "Gold/Hill": 15.0}}, + "Halfling": {"chance": 7.0, "subraces": {"Lightfoot": 85.0, "Stout": 15.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 65.0, "Forest": 35.0}}, + "Half-Orc": {"chance": 2.0}, + "Tiefling": {"chance": 2.0}, + "Dragonborn": {"chance": 1.0}, + }, + }, + "baldurs_gate": { + "aliases": ["Baldur's Gate", "Gate", "Baldurs Gate"], + "chances": { + "Human": {"chance": 68.0, "subraces": {"Tethyrian": 45.0, "Chondathan": 30.0, "Calishite": 15.0, "Illuskan": 7.0, "Damaran": 3.0}}, + "Half-Elf": {"chance": 7.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Halfling": {"chance": 6.0, "subraces": {"Lightfoot": 80.0, "Stout": 20.0}}, + "Dwarf": {"chance": 6.0, "subraces": {"Shield/Mountain": 70.0, "Gold/Hill": 30.0}}, + "Elf": {"chance": 5.0, "subraces": {"Moon": 60.0, "Wood": 40.0}}, + "Half-Orc": {"chance": 3.0}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 60.0, "Forest": 40.0}}, + "Tiefling": {"chance": 2.0}, + "Dragonborn": {"chance": 1.0}, + }, + }, + "cormyr": { + "aliases": ["Cormyr", "Forest Kingdom", "Suzail", "Marsember", + "Arabel", "Wheloon", "Purple Dragons"], + "chances": { + "Human": {"chance": 78.0, "subraces": {"Chondathan": 80.0, "Tethyrian": 12.0, "Damaran": 5.0, "Illuskan": 3.0}}, + "Half-Elf": {"chance": 6.0, "subraces": {"Moon": 80.0, "Sun": 20.0}}, + "Elf": {"chance": 5.0, "subraces": {"Moon": 50.0, "Sun": 30.0, "Wood": 20.0}}, + "Dwarf": {"chance": 5.0, "subraces": {"Shield/Mountain": 70.0, "Gold/Hill": 30.0}}, + "Halfling": {"chance": 3.0, "subraces": {"Lightfoot": 90.0, "Stout": 10.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + "Half-Orc": {"chance": 1.0}, + }, + }, + "amn": { + "aliases": ["Amn", "Athkatla", "Crimmor", "Eshpurta", + "Purskul", "Trademeet", "Merchant's Domain"], + "chances": { + "Human": {"chance": 78.0, "subraces": {"Tethyrian": 45.0, "Calishite": 38.0, "Chondathan": 12.0, "Illuskan": 5.0}}, + "Halfling": {"chance": 8.0, "subraces": {"Lightfoot": 85.0, "Stout": 15.0}}, + "Dwarf": {"chance": 5.0, "subraces": {"Shield/Mountain": 55.0, "Gold/Hill": 45.0}}, + "Half-Elf": {"chance": 4.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Elf": {"chance": 2.0, "subraces": {"Moon": 50.0, "Wood": 50.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + "Half-Orc": {"chance": 1.0}, + }, + }, + "tethyr": { + "aliases": ["Tethyr", "Zazesspur", "Darromar", "Myratma", + "Ithmong", "Saradush", "Riatavin", "Country of Purple Marches"], + "chances": { + "Human": {"chance": 74.0, "subraces": {"Tethyrian": 70.0, "Calishite": 18.0, "Chondathan": 8.0, "Illuskan": 4.0}}, + "Halfling": {"chance": 9.0, "subraces": {"Lightfoot": 80.0, "Stout": 20.0}}, + "Half-Elf": {"chance": 5.0, "subraces": {"Moon": 65.0, "Wood": 35.0}}, + "Elf": {"chance": 5.0, "subraces": {"Wood": 55.0, "Moon": 45.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Shield/Mountain": 60.0, "Gold/Hill": 40.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + "Half-Orc": {"chance": 1.0}, + }, + }, + "calimshan": { + "aliases": ["Calimshan", "Calimport", "Memnon", "Almraiven", "Keltar"], + "chances": { + "Human": {"chance": 82.0, "subraces": {"Calishite": 88.0, "Tethyrian": 8.0, "Chondathan": 4.0}}, + "Genasi": {"chance": 8.0, "subraces": {"Fire": 35.0, "Air": 35.0, "Earth": 15.0, "Water": 15.0}}, + "Halfling": {"chance": 4.0, "subraces": {"Lightfoot": 90.0, "Stout": 10.0}}, + "Half-Orc": {"chance": 3.0}, + "Dwarf": {"chance": 2.0, "subraces": {"Gold/Hill": 60.0, "Shield/Mountain": 40.0}}, + "Tiefling": {"chance": 1.0}, + }, + }, + "chult": { + "aliases": ["Chult", "Port Nyanzaru", "Mezro", "Chultan Peninsula", "Omu"], + "chances": { + "Human": {"chance": 68.0, "subraces": {"Chultan": 90.0, "Tethyrian": 6.0, "Calishite": 4.0}}, + "Tabaxi": {"chance": 14.0}, + "Lizardfolk": {"chance": 9.0}, + "Half-Elf": {"chance": 3.0, "subraces": {"Wood": 100.0}}, + "Yuan-ti Pureblood": {"chance": 3.0}, + "Dwarf": {"chance": 2.0, "subraces": {"Shield/Mountain": 100.0}}, + "Aasimar": {"chance": 1.0, "subraces": {"Protector": 100.0}}, + }, + }, + "unapproachable_east": { + "aliases": ["Unapproachable East", "Aglarond border", + "Thesk", "Great Dale", "Bezantur", "Two Stars", + "Phsant", "Milvarune"], + "chances": { + "Human": {"chance": 78.0, "subraces": {"Mulan": 50.0, "Rashemi": 25.0, "Damaran": 20.0, "Chondathan": 5.0}}, + "Half-Orc": {"chance": 7.0}, + "Half-Elf": {"chance": 4.0, "subraces": {"Wood": 60.0, "Moon": 40.0}}, + "Elf": {"chance": 4.0, "subraces": {"Wood": 70.0, "Moon": 30.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 60.0, "Forest": 40.0}}, + "Tiefling": {"chance": 2.0}, + "Genasi": {"chance": 2.0, "subraces": {"Fire": 100.0}}, + }, + }, + "rashemen": { + "aliases": ["Rashemen", "Mulsantir", "Immilmar", "Urling", + "Citadel Rashemar", "Mulptan", "Land of Berserkers", + "Ashenwood", "Witches of Rashemen"], + "chances": { + "Human": {"chance": 84.0, "subraces": {"Rashemi": 80.0, "Damaran": 12.0, "Mulan": 8.0}}, + "Dwarf": {"chance": 5.0, "subraces": {"Shield/Mountain": 80.0, "Gold/Hill": 20.0}}, + "Half-Orc": {"chance": 4.0}, + "Half-Elf": {"chance": 3.0, "subraces": {"Wood": 70.0, "Moon": 30.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + "Elf": {"chance": 1.0, "subraces": {"Wood": 100.0}}, + "Goliath": {"chance": 1.0}, + }, + }, + "thay": { + "aliases": ["Thay", "Red Wizards", "Eltabbar", "Tyraturos", + "Amruthar", "Pyarados", "Surthay", "Nethjet", + "Thaymount", "High Thay"], + "chances": { + "Human": {"chance": 82.0, "subraces": {"Mulan": 55.0, "Rashemi": 30.0, "Chondathan": 8.0, "Damaran": 7.0}}, + "Half-Orc": {"chance": 5.0}, + "Tiefling": {"chance": 4.0}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + "Genasi": {"chance": 2.0, "subraces": {"Fire": 100.0}}, + "Dwarf": {"chance": 2.0, "subraces": {"Shield/Mountain": 70.0, "Gold/Hill": 30.0}}, + "Elf": {"chance": 1.0, "subraces": {"Sun": 60.0, "Moon": 40.0}}, + "Half-Elf": {"chance": 1.0, "subraces": {"Moon": 100.0}}, + }, + }, + "underdark": { + "aliases": ["Underdark", "Menzoberranzan", "Blingdenstone", + "Gracklstugh", "Deep Realm", "Sshamath", "Ched Nasad"], + "chances": { + "Elf": {"chance": 46.0, "subraces": {"Drow": 100.0}}, + "Dwarf": {"chance": 20.0, "subraces": {"Duergar": 100.0}}, + "Gnome": {"chance": 9.0, "subraces": {"Rock": 100.0}}, + "Orc": {"chance": 9.0}, + "Goblin": {"chance": 6.0}, + "Bugbear": {"chance": 4.0}, + "Hobgoblin": {"chance": 3.0}, + "Kobold": {"chance": 2.0}, + "Kenku": {"chance": 1.0}, + }, + }, + "moonsea": { + "aliases": ["Moonsea", "Zhentil Keep", "Melvaunt", "Hillsfar", + "Mulmaster", "Phlan", "Thentia", "Sembia border"], + "chances": { + "Human": {"chance": 74.0, "subraces": {"Chondathan": 45.0, "Damaran": 35.0, "Illuskan": 12.0, "Mulan": 8.0}}, + "Half-Orc": {"chance": 7.0}, + "Dwarf": {"chance": 5.0, "subraces": {"Shield/Mountain": 80.0, "Gold/Hill": 20.0}}, + "Half-Elf": {"chance": 4.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Tiefling": {"chance": 4.0}, + "Elf": {"chance": 3.0, "subraces": {"Moon": 60.0, "Wood": 40.0}}, + "Orc": {"chance": 3.0}, + }, + }, + "dalelands": { + "aliases": ["Dalelands", "Shadowdale", "Mistledale", "Deepingdale", + "Battledale", "Daggerdale", "Archendale"], + "chances": { + "Human": {"chance": 70.0, "subraces": {"Chondathan": 70.0, "Damaran": 20.0, "Illuskan": 10.0}}, + "Half-Elf": {"chance": 9.0, "subraces": {"Moon": 65.0, "Wood": 35.0}}, + "Elf": {"chance": 8.0, "subraces": {"Moon": 55.0, "Wood": 45.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 75.0, "Stout": 25.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Shield/Mountain": 65.0, "Gold/Hill": 35.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 60.0, "Forest": 40.0}}, + "Half-Orc": {"chance": 2.0}, + }, + }, + "sembia": { + "aliases": ["Sembia", "Selgaunt", "Ordulin", "Saerloon", + "Yhaunn", "Daerlun", "Urmlaspyr"], + "chances": { + "Human": {"chance": 80.0, "subraces": {"Chondathan": 65.0, "Damaran": 20.0, "Tethyrian": 8.0, "Shou": 7.0}}, + "Half-Elf": {"chance": 6.0, "subraces": {"Moon": 75.0, "Wood": 25.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 85.0, "Stout": 15.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Shield/Mountain": 70.0, "Gold/Hill": 30.0}}, + "Elf": {"chance": 2.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + "Tiefling": {"chance": 1.0}, + }, + }, + "vast": { + "aliases": ["Vast", "Ravens Bluff", "Tantras", "Calaunt", + "Procampur", "Sarbreen"], + "chances": { + "Human": {"chance": 72.0, "subraces": {"Chondathan": 50.0, "Damaran": 30.0, "Illuskan": 12.0, "Mulan": 8.0}}, + "Dwarf": {"chance": 8.0, "subraces": {"Shield/Mountain": 75.0, "Gold/Hill": 25.0}}, + "Half-Elf": {"chance": 6.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 80.0, "Stout": 20.0}}, + "Elf": {"chance": 4.0, "subraces": {"Moon": 60.0, "Wood": 40.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 65.0, "Forest": 35.0}}, + "Half-Orc": {"chance": 2.0}, + }, + }, + "vilhon_reach": { + "aliases": ["Vilhon Reach", "Turmish", "Chondath", "Hlondeth", + "Alaghon", "Arrabar", "Sespech", "Nimpeth"], + "chances": { + "Human": {"chance": 74.0, "subraces": {"Chondathan": 60.0, "Turami": 25.0, "Tethyrian": 10.0, "Mulan": 5.0}}, + "Half-Elf": {"chance": 6.0, "subraces": {"Wood": 60.0, "Moon": 40.0}}, + "Halfling": {"chance": 6.0, "subraces": {"Lightfoot": 80.0, "Stout": 20.0}}, + "Elf": {"chance": 4.0, "subraces": {"Wood": 65.0, "Moon": 35.0}}, + "Yuan-ti Pureblood": {"chance": 4.0}, + "Dwarf": {"chance": 3.0, "subraces": {"Gold/Hill": 55.0, "Shield/Mountain": 45.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + "Half-Orc": {"chance": 1.0}, + }, + }, + "shining_south": { + "aliases": ["Shining South", "Halruaa", "Dambrath", "Estagund", + "Durpar", "Var Golden", "Luiren", "Ulgarth"], + "chances": { + "Human": {"chance": 70.0, "subraces": {"Halruaan": 30.0, "Durpari": 25.0, "Dambrathan": 20.0, "Shaaran": 15.0, "Chondathan": 10.0}}, + "Halfling": {"chance": 12.0, "subraces": {"Ghostwise": 45.0, "Lightfoot": 35.0, "Stout": 20.0}}, + "Half-Elf": {"chance": 5.0, "subraces": {"Wood": 55.0, "Moon": 45.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Gold/Hill": 70.0, "Shield/Mountain": 30.0}}, + "Elf": {"chance": 4.0, "subraces": {"Wood": 60.0, "Moon": 40.0}}, + "Half-Orc": {"chance": 3.0}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 65.0, "Forest": 35.0}}, + }, + }, + "old_empires": { + "aliases": ["Old Empires", "Mulhorand", "Unther", "Chessenta", + "Skuld", "Messemprar", "Cimbar", "Soorenar"], + "chances": { + "Human": {"chance": 80.0, "subraces": {"Mulan": 70.0, "Chondathan": 15.0, "Turami": 10.0, "Chessentan": 5.0}}, + "Half-Orc": {"chance": 5.0}, + "Genasi": {"chance": 4.0, "subraces": {"Earth": 30.0, "Fire": 30.0, "Water": 20.0, "Air": 20.0}}, + "Halfling": {"chance": 4.0, "subraces": {"Lightfoot": 85.0, "Stout": 15.0}}, + "Dwarf": {"chance": 3.0, "subraces": {"Gold/Hill": 60.0, "Shield/Mountain": 40.0}}, + "Elf": {"chance": 2.0, "subraces": {"Sun": 60.0, "Moon": 40.0}}, + "Tiefling": {"chance": 2.0}, + }, + }, + "hordelands": { + "aliases": ["Hordelands", "Endless Wastes", "Taan", "Tuigan", + "Yaimmunahar", "Quoya Desert"], + "chances": { + "Human": {"chance": 90.0, "subraces": {"Tuigan": 85.0, "Rashemi": 10.0, "Mulan": 5.0}}, + "Half-Orc": {"chance": 4.0}, + "Halfling": {"chance": 3.0, "subraces": {"Stout": 60.0, "Lightfoot": 40.0}}, + "Goliath": {"chance": 2.0}, + "Dwarf": {"chance": 1.0, "subraces": {"Shield/Mountain": 100.0}}, + }, + }, + "kara_tur": { + "aliases": ["Kara-Tur", "Shou Lung", "Kozakura", "Wa", + "Tabot", "Koryo", "T'u Lung"], + "chances": { + "Human": {"chance": 88.0, "subraces": {"Shou": 70.0, "Kozakuran": 15.0, "Wanese": 10.0, "Korobokuru": 5.0}}, + "Half-Elf": {"chance": 3.0, "subraces": {"Wood": 100.0}}, + "Halfling": {"chance": 3.0, "subraces": {"Lightfoot": 100.0}}, + "Dwarf": {"chance": 2.0, "subraces": {"Shield/Mountain": 100.0}}, + "Elf": {"chance": 2.0, "subraces": {"Wood": 100.0}}, + "Hobgoblin": {"chance": 1.0}, + "Tabaxi": {"chance": 1.0}, + }, + }, + "moonshae_isles": { + "aliases": ["Moonshae Isles", "Moonshaes", "Gwynneth", "Alaron", + "Snowdown", "Norland", "Callidyrr", "Corwell"], + "chances": { + "Human": {"chance": 72.0, "subraces": {"Ffolk": 60.0, "Northlander": 30.0, "Illuskan": 10.0}}, + "Half-Elf": {"chance": 8.0, "subraces": {"Moon": 60.0, "Wood": 40.0}}, + "Elf": {"chance": 7.0, "subraces": {"Wood": 55.0, "Moon": 45.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 70.0, "Stout": 30.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Shield/Mountain": 60.0, "Gold/Hill": 40.0}}, + "Firbolg": {"chance": 2.0}, + "Half-Orc": {"chance": 2.0}, + }, + }, + "nelanther_isles": { + "aliases": ["Nelanther Isles", "Nelanther", "Pirate Isles", "Sword Coast pirates"], + "chances": { + "Human": {"chance": 68.0, "subraces": {"Tethyrian": 40.0, "Calishite": 30.0, "Illuskan": 20.0, "Chondathan": 10.0}}, + "Half-Orc": {"chance": 10.0}, + "Half-Elf": {"chance": 6.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 90.0, "Stout": 10.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Shield/Mountain": 60.0, "Gold/Hill": 40.0}}, + "Genasi": {"chance": 4.0, "subraces": {"Water": 60.0, "Air": 40.0}}, + "Tiefling": {"chance": 3.0}, + }, + }, + "savage_north_interior": { + "aliases": ["Evermoors", "Nether Mountains", "Cold Wood", + "Fell Pass", "Dessarin Valley", "Sword Mountains"], + "chances": { + "Human": {"chance": 50.0, "subraces": {"Illuskan": 60.0, "Chondathan": 25.0, "Damaran": 15.0}}, + "Orc": {"chance": 15.0}, + "Dwarf": {"chance": 10.0, "subraces": {"Shield/Mountain": 90.0, "Gold/Hill": 10.0}}, + "Half-Orc": {"chance": 8.0}, + "Elf": {"chance": 6.0, "subraces": {"Wood": 70.0, "Moon": 30.0}}, + "Goblin": {"chance": 5.0}, + "Half-Elf": {"chance": 4.0, "subraces": {"Wood": 60.0, "Moon": 40.0}}, + "Goliath": {"chance": 2.0}, + }, + }, + "high_forest": { + "aliases": ["High Forest", "Star Mounts", "Grandfather Tree", + "Turlang", "Dire Wood"], + "chances": { + "Elf": {"chance": 40.0, "subraces": {"Wood": 70.0, "Moon": 30.0}}, + "Human": {"chance": 22.0, "subraces": {"Illuskan": 50.0, "Chondathan": 50.0}}, + "Half-Elf": {"chance": 12.0, "subraces": {"Wood": 70.0, "Moon": 30.0}}, + "Firbolg": {"chance": 8.0}, + "Gnome": {"chance": 7.0, "subraces": {"Forest": 80.0, "Rock": 20.0}}, + "Halfling": {"chance": 6.0, "subraces": {"Ghostwise": 70.0, "Lightfoot": 30.0}}, + "Satyr": {"chance": 3.0}, + "Orc": {"chance": 2.0}, + }, + }, + "impiltur_and_bloodstone": { + "aliases": ["Impiltur", "Damara", "Bloodstone Lands", "Narfell", + "Vaasa", "Lyrabar", "Heliogabalus"], + "chances": { + "Human": {"chance": 78.0, "subraces": {"Damaran": 75.0, "Chondathan": 15.0, "Rashemi": 10.0}}, + "Dwarf": {"chance": 8.0, "subraces": {"Shield/Mountain": 85.0, "Gold/Hill": 15.0}}, + "Half-Orc": {"chance": 4.0}, + "Half-Elf": {"chance": 3.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Halfling": {"chance": 3.0, "subraces": {"Stout": 60.0, "Lightfoot": 40.0}}, + "Elf": {"chance": 2.0, "subraces": {"Moon": 60.0, "Wood": 40.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Rock": 70.0, "Forest": 30.0}}, + }, + }, + + "anauroch": { + "aliases": ["Anauroch", "Great Sand Sea", "Bedine", "Sword of North", + "Plain of Standing Stones", "Hidden Lake", "Shade Enclave"], + "chances": { + "Human": {"chance": 80.0, "subraces": {"Bedine": 55.0, "Netherese": 25.0, "Illuskan": 12.0, "Damaran": 8.0}}, + "Genasi": {"chance": 6.0, "subraces": {"Earth": 40.0, "Air": 30.0, "Fire": 30.0}}, + "Half-Orc": {"chance": 4.0}, + "Dwarf": {"chance": 3.0, "subraces": {"Shield/Mountain": 70.0, "Gold/Hill": 30.0}}, + "Tiefling": {"chance": 3.0}, + "Orc": {"chance": 2.0}, + "Elf": {"chance": 2.0, "subraces": {"Moon": 60.0, "Sun": 40.0}}, + }, + }, + "aglarond": { + "aliases": ["Aglarond", "Altumbel", "Velprintalar", "Yeshelmaar", + "Umbergoth", "Emmech"], + "chances": { + "Half-Elf": {"chance": 40.0, "subraces": {"Wood": 70.0, "Moon": 30.0}}, + "Human": {"chance": 38.0, "subraces": {"Aglarondan": 60.0, "Damaran": 25.0, "Chondathan": 15.0}}, + "Elf": {"chance": 12.0, "subraces": {"Wood": 75.0, "Moon": 25.0}}, + "Halfling": {"chance": 4.0, "subraces": {"Lightfoot": 70.0, "Stout": 30.0}}, + "Dwarf": {"chance": 3.0, "subraces": {"Shield/Mountain": 70.0, "Gold/Hill": 30.0}}, + "Gnome": {"chance": 2.0, "subraces": {"Forest": 60.0, "Rock": 40.0}}, + "Half-Orc": {"chance": 1.0}, + }, + }, + "lantan": { + "aliases": ["Lantan", "Lantanna", "Supai", "Sambar", "Adaerglass"], + "chances": { + "Human": {"chance": 68.0, "subraces": {"Lantanese": 80.0, "Chondathan": 12.0, "Calishite": 8.0}}, + "Gnome": {"chance": 20.0, "subraces": {"Rock": 85.0, "Forest": 15.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 90.0, "Stout": 10.0}}, + "Half-Elf": {"chance": 3.0, "subraces": {"Moon": 100.0}}, + "Dwarf": {"chance": 2.0, "subraces": {"Gold/Hill": 60.0, "Shield/Mountain": 40.0}}, + "Tiefling": {"chance": 2.0}, + }, + }, + "sea_of_fallen_stars": { + "aliases": ["Sea of Fallen Stars", "Inner Sea", "Pirate Isles", + "Dragonmere", "Sea of Swords bound", "Immurk's Hold", + "Whamite Isles"], + "chances": { + "Human": {"chance": 66.0, "subraces": {"Chondathan": 35.0, "Illuskan": 25.0, "Tethyrian": 20.0, "Turami": 12.0, "Mulan": 8.0}}, + "Half-Orc": {"chance": 8.0}, + "Half-Elf": {"chance": 6.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 90.0, "Stout": 10.0}}, + "Genasi": {"chance": 5.0, "subraces": {"Water": 60.0, "Air": 40.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Shield/Mountain": 60.0, "Gold/Hill": 40.0}}, + "Triton": {"chance": 3.0}, + "Tiefling": {"chance": 3.0}, + }, + }, + "sword_coast_central": { + "aliases": ["Sword Coast Central", "Daggerford", "Trade Way", + "Delimbiyr Vale", "Secomber", "Boareskyr Bridge", + "Misty Forest bound"], + "chances": { + "Human": {"chance": 70.0, "subraces": {"Tethyrian": 40.0, "Chondathan": 35.0, "Illuskan": 20.0, "Calishite": 5.0}}, + "Halfling": {"chance": 8.0, "subraces": {"Lightfoot": 80.0, "Stout": 20.0}}, + "Half-Elf": {"chance": 6.0, "subraces": {"Moon": 65.0, "Wood": 35.0}}, + "Dwarf": {"chance": 5.0, "subraces": {"Shield/Mountain": 65.0, "Gold/Hill": 35.0}}, + "Elf": {"chance": 5.0, "subraces": {"Wood": 60.0, "Moon": 40.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 65.0, "Forest": 35.0}}, + "Half-Orc": {"chance": 2.0}, + "Orc": {"chance": 1.0}, + }, + }, + "maztica": { + "aliases": ["Maztica", "True World", "Nexal", "Payit", "Kultaka", + "Huacli", "Far Payit", "Helmsport", "Ulatos"], + "chances": { + "Human": {"chance": 80.0, "subraces": {"Nexalan": 65.0, "Payit": 20.0, "Calishite": 10.0, "Tethyrian": 5.0}}, + "Lizardfolk": {"chance": 6.0}, + "Half-Orc": {"chance": 4.0}, + "Yuan-ti Pureblood": {"chance": 3.0}, + "Tabaxi": {"chance": 3.0}, + "Dwarf": {"chance": 2.0, "subraces": {"Gold/Hill": 100.0}}, + "Tiefling": {"chance": 2.0}, + }, + }, + + "icewind_dale": { + "aliases": ["Icewind Dale", "Ten Towns", "Bryn Shander", "Targos", + "Lonelywood", "Easthaven", "Termalaine", "Caer-Dineval", + "Caer-Konig", "Dougan's Hole", "Good Mead", "Reghed Glacier", + "Kelvin's Cairn", "Reghedmen"], + "chances": { + "Human": {"chance": 55.0, "subraces": {"Illuskan": 60.0, "Reghed": 30.0, "Damaran": 10.0}}, + "Dwarf": {"chance": 16.0, "subraces": {"Shield/Mountain": 95.0, "Gold/Hill": 5.0}}, + "Goliath": {"chance": 10.0}, + "Half-Orc": {"chance": 6.0}, + "Halfling": {"chance": 5.0, "subraces": {"Stout": 70.0, "Lightfoot": 30.0}}, + "Elf": {"chance": 4.0, "subraces": {"Moon": 70.0, "Wood": 30.0}}, + "Half-Elf": {"chance": 4.0, "subraces": {"Moon": 100.0}}, + }, + }, + "evermeet": { + "aliases": ["Evermeet", "Island of Evermeet", "Green Isle", + "Isle of Evermeet", "Leuthilspar"], + "chances": { + "Elf": {"chance": 88.0, "subraces": {"Sun": 50.0, "Moon": 42.0, "Avariel": 5.0, "Eladrin": 3.0}}, + "Half-Elf": {"chance": 8.0, "subraces": {"Sun": 60.0, "Moon": 40.0}}, + "Human": {"chance": 3.0, "subraces": {"Illuskan": 100.0}}, + "Gnome": {"chance": 1.0, "subraces": {"Forest": 100.0}}, + }, + }, + "myth_drannor": { + "aliases": ["Myth Drannor", "City of Song", "Cormanthyr", + "Cormanthor", "Semberholme", "Tangled Trees", + "Elven Court"], + "chances": { + "Elf": {"chance": 50.0, "subraces": {"Moon": 45.0, "Sun": 30.0, "Wood": 20.0, "Eladrin": 5.0}}, + "Human": {"chance": 22.0, "subraces": {"Chondathan": 70.0, "Damaran": 20.0, "Illuskan": 10.0}}, + "Half-Elf": {"chance": 14.0, "subraces": {"Moon": 60.0, "Sun": 25.0, "Wood": 15.0}}, + "Dwarf": {"chance": 5.0, "subraces": {"Shield/Mountain": 70.0, "Gold/Hill": 30.0}}, + "Gnome": {"chance": 4.0, "subraces": {"Rock": 55.0, "Forest": 45.0}}, + "Halfling": {"chance": 3.0, "subraces": {"Lightfoot": 80.0, "Stout": 20.0}}, + "Satyr": {"chance": 2.0}, + }, + }, + "tashalar_and_shaar": { + "aliases": ["Tashalar", "Tashluta", "Lapaliiya", "Thindol", + "Samarach", "Shaar", "Great Rift", "Channath Vale", + "Sheirtalar"], + "chances": { + "Human": {"chance": 55.0, "subraces": {"Chultan": 45.0, "Shaaran": 25.0, "Tethyrian": 20.0, "Calishite": 10.0}}, + "Yuan-ti Pureblood": {"chance": 16.0}, + "Lizardfolk": {"chance": 8.0}, + "Dwarf": {"chance": 6.0, "subraces": {"Gold/Hill": 90.0, "Shield/Mountain": 10.0}}, + "Half-Elf": {"chance": 5.0, "subraces": {"Wood": 100.0}}, + "Tabaxi": {"chance": 4.0}, + "Elf": {"chance": 3.0, "subraces": {"Wood": 100.0}}, + "Half-Orc": {"chance": 3.0}, + }, + }, + "nimbral": { + "aliases": ["Nimbral", "Nimbral Isles", "Immurk's Hold south", + "Land of Flying Hunt", "Cathtyr"], + "chances": { + "Human": {"chance": 80.0, "subraces": {"Halruaan": 45.0, "Tethyrian": 30.0, "Illuskan": 15.0, "Calishite": 10.0}}, + "Half-Elf": {"chance": 7.0, "subraces": {"Moon": 100.0}}, + "Halfling": {"chance": 5.0, "subraces": {"Lightfoot": 90.0, "Stout": 10.0}}, + "Elf": {"chance": 3.0, "subraces": {"Moon": 60.0, "Sun": 40.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 80.0, "Forest": 20.0}}, + "Dwarf": {"chance": 1.0, "subraces": {"Shield/Mountain": 100.0}}, + "Tiefling": {"chance": 1.0}, + }, + }, + }, + + "Krynn": { + "abanasinia": { + "aliases": ["Abanasinia", "Solace", "Haven", "Que-Shu", + "Crystalmir Lake", "Sancrist bound"], + "chances": { + "Human": {"chance": 72.0, "subraces": {"Plainsfolk": 45.0, "Abanasinian": 45.0, "Solamnic": 10.0}}, + "Half-Elf": {"chance": 8.0, "subraces": {"Qualinesti": 100.0}}, + "Kender": {"chance": 7.0}, + "Dwarf": {"chance": 5.0, "subraces": {"Hylar": 50.0, "Neidar": 50.0}}, + "Elf": {"chance": 4.0, "subraces": {"Qualinesti": 100.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Tinker": 100.0}}, + "Half-Ogre": {"chance": 1.0}, + }, + }, + "solamnia": { + "aliases": ["Solamnia", "Palanthas", "Vingaard", "Kalaman", + "Sancrist", "Knights of Solamnia", "Solamnic Plains"], + "chances": { + "Human": {"chance": 82.0, "subraces": {"Solamnic": 80.0, "Abanasinian": 12.0, "Plainsfolk": 8.0}}, + "Dwarf": {"chance": 5.0, "subraces": {"Hylar": 60.0, "Neidar": 40.0}}, + "Kender": {"chance": 4.0}, + "Half-Elf": {"chance": 3.0, "subraces": {"Silvanesti": 50.0, "Qualinesti": 50.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Tinker": 100.0}}, + "Elf": {"chance": 2.0, "subraces": {"Silvanesti": 60.0, "Qualinesti": 40.0}}, + "Minotaur": {"chance": 1.0}, + }, + }, + "silvanesti": { + "aliases": ["Silvanesti", "Silvanost", "Silvamori", "Elven Home"], + "chances": { + "Elf": {"chance": 90.0, "subraces": {"Silvanesti": 90.0, "Kagonesti": 10.0}}, + "Half-Elf": {"chance": 6.0, "subraces": {"Silvanesti": 100.0}}, + "Human": {"chance": 3.0, "subraces": {"Solamnic": 100.0}}, + "Kender": {"chance": 1.0}, + }, + }, + "qualinesti": { + "aliases": ["Qualinesti", "Qualinost", "Pax Tharkas bound"], + "chances": { + "Elf": {"chance": 85.0, "subraces": {"Qualinesti": 85.0, "Kagonesti": 15.0}}, + "Half-Elf": {"chance": 8.0, "subraces": {"Qualinesti": 100.0}}, + "Human": {"chance": 4.0, "subraces": {"Abanasinian": 100.0}}, + "Dwarf": {"chance": 2.0, "subraces": {"Neidar": 100.0}}, + "Kender": {"chance": 1.0}, + }, + }, + "thorbardin": { + "aliases": ["Thorbardin", "Mount Cloudseeker", "Hylar Halls", + "Kingdom of Dwarves", "Zhakar"], + "chances": { + "Dwarf": {"chance": 88.0, "subraces": {"Hylar": 40.0, "Daewar": 25.0, "Klar": 15.0, "Theiwar": 15.0, "Daergar": 5.0}}, + "Gnome": {"chance": 6.0, "subraces": {"Tinker": 100.0}}, + "Human": {"chance": 3.0, "subraces": {"Plainsfolk": 100.0}}, + "Kender": {"chance": 2.0}, + "Half-Ogre": {"chance": 1.0}, + }, + }, + "icewall_and_north": { + "aliases": ["Icewall", "Icereach", "Southern Ergoth", + "Ice Folk", "Icewall Glacier"], + "chances": { + "Human": {"chance": 76.0, "subraces": {"Ice Folk": 70.0, "Plainsfolk": 20.0, "Solamnic": 10.0}}, + "Dwarf": {"chance": 8.0, "subraces": {"Neidar": 100.0}}, + "Elf": {"chance": 6.0, "subraces": {"Kagonesti": 100.0}}, + "Kender": {"chance": 4.0}, + "Half-Ogre": {"chance": 3.0}, + "Minotaur": {"chance": 3.0}, + }, + }, + + "goodlund_and_balifor": { + "aliases": ["Goodlund", "Balifor", "Kendermore", "Port Balifor", + "Flotsam bound", "Kenderhome"], + "chances": { + "Kender": {"chance": 62.0}, + "Human": {"chance": 22.0, "subraces": {"Plainsfolk": 60.0, "Abanasinian": 25.0, "Solamnic": 15.0}}, + "Half-Elf": {"chance": 5.0, "subraces": {"Qualinesti": 100.0}}, + "Gnome": {"chance": 4.0, "subraces": {"Tinker": 100.0}}, + "Dwarf": {"chance": 3.0, "subraces": {"Neidar": 100.0}}, + "Minotaur": {"chance": 2.0}, + "Half-Ogre": {"chance": 2.0}, + }, + }, + "ogrelands_blode_and_kern": { + "aliases": ["Blode", "Kern", "Ogrelands", "Kernen", "Ogre lands"], + "chances": { + "Half-Ogre": {"chance": 40.0}, + "Human": {"chance": 28.0, "subraces": {"Plainsfolk": 50.0, "Abanasinian": 30.0, "Solamnic": 20.0}}, + "Minotaur": {"chance": 12.0}, + "Goblin": {"chance": 8.0}, + "Hobgoblin": {"chance": 6.0}, + "Bugbear": {"chance": 4.0}, + "Kender": {"chance": 2.0}, + }, + }, + "neraka_and_taman_busuk": { + "aliases": ["Neraka", "Taman Busuk", "Sanction", "Dargaard", + "Estwilde", "Dargaard Keep bound"], + "chances": { + "Human": {"chance": 60.0, "subraces": {"Solamnic": 40.0, "Plainsfolk": 35.0, "Abanasinian": 25.0}}, + "Half-Ogre": {"chance": 14.0}, + "Hobgoblin": {"chance": 8.0}, + "Goblin": {"chance": 6.0}, + "Minotaur": {"chance": 5.0}, + "Bugbear": {"chance": 4.0}, + "Dwarf": {"chance": 2.0, "subraces": {"Neidar": 100.0}}, + "Kender": {"chance": 1.0}, + }, + }, + "nordmaar_and_khur": { + "aliases": ["Nordmaar", "Khur", "North Keep", "Willik", + "Wendle", "Kernaghan", "Khurish nomads"], + "chances": { + "Human": {"chance": 78.0, "subraces": {"Plainsfolk": 55.0, "Abanasinian": 25.0, "Solamnic": 20.0}}, + "Half-Elf": {"chance": 6.0, "subraces": {"Kagonesti": 100.0}}, + "Elf": {"chance": 5.0, "subraces": {"Kagonesti": 100.0}}, + "Kender": {"chance": 4.0}, + "Dwarf": {"chance": 3.0, "subraces": {"Neidar": 100.0}}, + "Half-Ogre": {"chance": 2.0}, + "Minotaur": {"chance": 2.0}, + }, + }, + }, + + "Oerth": { + "greyhawk_city": { + "aliases": ["Greyhawk", "City of Greyhawk", "Free City of Greyhawk", + "Gem of Flanaess"], + "chances": { + "Human": {"chance": 66.0, "subraces": {"Oeridian": 40.0, "Suel": 25.0, "Flan": 20.0, "Baklunish": 15.0}}, + "Half-Elf": {"chance": 8.0, "subraces": {"Gray": 60.0, "High": 40.0}}, + "Dwarf": {"chance": 7.0, "subraces": {"Hill": 60.0, "Mountain": 40.0}}, + "Halfling": {"chance": 6.0, "subraces": {"Lightfoot": 70.0, "Stout": 30.0}}, + "Elf": {"chance": 5.0, "subraces": {"Gray": 40.0, "High": 40.0, "Wood": 20.0}}, + "Gnome": {"chance": 4.0, "subraces": {"Rock": 100.0}}, + "Half-Orc": {"chance": 3.0}, + "Tiefling": {"chance": 1.0}, + }, + }, + "keoland_and_sheldomar": { + "aliases": ["Keoland", "Sheldomar Valley", "Niole Dra", + "Gran March", "Bissel", "Ulek", "Yeomanry"], + "chances": { + "Human": {"chance": 70.0, "subraces": {"Suel": 45.0, "Oeridian": 40.0, "Flan": 15.0}}, + "Dwarf": {"chance": 9.0, "subraces": {"Hill": 55.0, "Mountain": 45.0}}, + "Halfling": {"chance": 7.0, "subraces": {"Lightfoot": 70.0, "Stout": 30.0}}, + "Elf": {"chance": 6.0, "subraces": {"High": 45.0, "Gray": 35.0, "Wood": 20.0}}, + "Half-Elf": {"chance": 4.0, "subraces": {"High": 60.0, "Gray": 40.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 100.0}}, + "Half-Orc": {"chance": 1.0}, + }, + }, + "furyondy_and_shield_lands": { + "aliases": ["Furyondy", "Chendl", "Shield Lands", "Veluna", + "Dyvers", "Willip"], + "chances": { + "Human": {"chance": 74.0, "subraces": {"Oeridian": 55.0, "Suel": 25.0, "Flan": 20.0}}, + "Dwarf": {"chance": 7.0, "subraces": {"Hill": 60.0, "Mountain": 40.0}}, + "Halfling": {"chance": 6.0, "subraces": {"Lightfoot": 75.0, "Stout": 25.0}}, + "Elf": {"chance": 5.0, "subraces": {"High": 50.0, "Gray": 30.0, "Wood": 20.0}}, + "Half-Elf": {"chance": 4.0, "subraces": {"High": 60.0, "Gray": 40.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 100.0}}, + "Half-Orc": {"chance": 1.0}, + }, + }, + "pomarj_and_wild_coast": { + "aliases": ["Pomarj", "Wild Coast", "Highport", "Suderham", + "Slave Lords bound"], + "chances": { + "Human": {"chance": 40.0, "subraces": {"Oeridian": 40.0, "Suel": 35.0, "Flan": 25.0}}, + "Orc": {"chance": 20.0}, + "Half-Orc": {"chance": 12.0}, + "Goblin": {"chance": 9.0}, + "Hobgoblin": {"chance": 7.0}, + "Dwarf": {"chance": 5.0, "subraces": {"Hill": 100.0}}, + "Bugbear": {"chance": 4.0}, + "Kobold": {"chance": 3.0}, + }, + }, + "bandit_kingdoms_and_bright_desert": { + "aliases": ["Bandit Kingdoms", "Bright Desert", "Rift Canyon", + "Rauxes bound", "Nightfen"], + "chances": { + "Human": {"chance": 76.0, "subraces": {"Oeridian": 45.0, "Baklunish": 30.0, "Flan": 25.0}}, + "Half-Orc": {"chance": 8.0}, + "Dwarf": {"chance": 4.0, "subraces": {"Hill": 60.0, "Mountain": 40.0}}, + "Halfling": {"chance": 4.0, "subraces": {"Lightfoot": 80.0, "Stout": 20.0}}, + "Elf": {"chance": 3.0, "subraces": {"Wood": 60.0, "Gray": 40.0}}, + "Half-Elf": {"chance": 3.0, "subraces": {"Gray": 100.0}}, + "Tiefling": {"chance": 2.0}, + }, + }, + + "great_kingdom_of_aerdy": { + "aliases": ["Great Kingdom", "Aerdy", "Rauxes", "North Province", + "South Province", "Ahlissa", "North Kingdom", "Rel Astra"], + "chances": { + "Human": {"chance": 74.0, "subraces": {"Oeridian": 65.0, "Suel": 20.0, "Flan": 15.0}}, + "Dwarf": {"chance": 7.0, "subraces": {"Hill": 55.0, "Mountain": 45.0}}, + "Half-Orc": {"chance": 5.0}, + "Elf": {"chance": 4.0, "subraces": {"Gray": 50.0, "High": 30.0, "Wood": 20.0}}, + "Halfling": {"chance": 4.0, "subraces": {"Lightfoot": 75.0, "Stout": 25.0}}, + "Half-Elf": {"chance": 3.0, "subraces": {"Gray": 60.0, "High": 40.0}}, + "Tiefling": {"chance": 2.0}, + "Gnome": {"chance": 1.0, "subraces": {"Rock": 100.0}}, + }, + }, + "nyrond_and_urnsts": { + "aliases": ["Nyrond", "Rel Mord", "County of Urnst", "Duchy of Urnst", + "Womtham", "Almor", "Innspa bound"], + "chances": { + "Human": {"chance": 72.0, "subraces": {"Oeridian": 50.0, "Suel": 30.0, "Flan": 20.0}}, + "Dwarf": {"chance": 8.0, "subraces": {"Hill": 60.0, "Mountain": 40.0}}, + "Halfling": {"chance": 6.0, "subraces": {"Lightfoot": 70.0, "Stout": 30.0}}, + "Elf": {"chance": 5.0, "subraces": {"High": 45.0, "Gray": 35.0, "Wood": 20.0}}, + "Half-Elf": {"chance": 4.0, "subraces": {"High": 55.0, "Gray": 45.0}}, + "Gnome": {"chance": 3.0, "subraces": {"Rock": 100.0}}, + "Half-Orc": {"chance": 2.0}, + }, + }, + "baklunish_west": { + "aliases": ["Baklunish West", "Ket", "Tusmit", "Ekbir", "Zeif", + "Ull", "Paynims", "Plains of Paynims", "Lopolla"], + "chances": { + "Human": {"chance": 82.0, "subraces": {"Baklunish": 80.0, "Oeridian": 12.0, "Suel": 8.0}}, + "Half-Orc": {"chance": 5.0}, + "Genasi": {"chance": 4.0, "subraces": {"Fire": 40.0, "Air": 40.0, "Earth": 20.0}}, + "Halfling": {"chance": 3.0, "subraces": {"Lightfoot": 85.0, "Stout": 15.0}}, + "Dwarf": {"chance": 3.0, "subraces": {"Hill": 60.0, "Mountain": 40.0}}, + "Elf": {"chance": 2.0, "subraces": {"Gray": 60.0, "High": 40.0}}, + "Tiefling": {"chance": 1.0}, + }, + }, + "iuz_and_horned_society": { + "aliases": ["Iuz", "Empire of Iuz", "Horned Society", "Molag", + "Dorakaa", "Bandit lands of Iuz", "Land of Iuz"], + "chances": { + "Human": {"chance": 50.0, "subraces": {"Flan": 45.0, "Oeridian": 35.0, "Baklunish": 20.0}}, + "Orc": {"chance": 16.0}, + "Half-Orc": {"chance": 12.0}, + "Hobgoblin": {"chance": 8.0}, + "Goblin": {"chance": 6.0}, + "Bugbear": {"chance": 4.0}, + "Tiefling": {"chance": 3.0}, + "Kobold": {"chance": 1.0}, + }, + }, + "sea_barons_and_isles": { + "aliases": ["Sea Barons", "Lordship of Isles", "Asperdi", + "Duxchan", "Aerdi Sea", "Spindrift Isles", "Densac Gulf"], + "chances": { + "Human": {"chance": 70.0, "subraces": {"Oeridian": 40.0, "Suel": 35.0, "Flan": 25.0}}, + "Half-Orc": {"chance": 8.0}, + "Halfling": {"chance": 6.0, "subraces": {"Lightfoot": 90.0, "Stout": 10.0}}, + "Half-Elf": {"chance": 5.0, "subraces": {"Gray": 60.0, "High": 40.0}}, + "Elf": {"chance": 4.0, "subraces": {"Gray": 50.0, "Wood": 50.0}}, + "Dwarf": {"chance": 4.0, "subraces": {"Hill": 60.0, "Mountain": 40.0}}, + "Triton": {"chance": 2.0}, + "Genasi": {"chance": 1.0, "subraces": {"Water": 100.0}}, + }, + }, + }, +} + + +# ─── Планы существования ───────────────────────────────────────────────────── + +PLANAR_OVERWRITES: dict = { + "feywild": { + "aliases": ["Feywild", "Plane of Faerie", "Faerie", + "Seelie Court", "Unseelie Court"], + "chances": { + "Elf": {"chance": 55.0, "subraces": {"Eladrin": 75.0, "Wood": 18.0, "Avariel": 7.0}}, + "Satyr": {"chance": 18.0}, + "Firbolg": {"chance": 10.0}, + "Gnome": {"chance": 8.0, "subraces": {"Forest": 80.0, "Rock": 20.0}}, + "Half-Elf": {"chance": 5.0, "subraces": {"Moon": 100.0}}, + "Halfling": {"chance": 4.0, "subraces": {"Lightfoot": 100.0}}, + }, + }, + "shadowfell": { + "aliases": ["Shadowfell", "Plane of Shadow", "Shadow Vale", "Gloomwrought"], + "chances": { + "Elf": {"chance": 42.0, "subraces": {"Shadar-kai": 100.0}}, + "Human": {"chance": 28.0, "subraces": {"Damaran": 50.0, "Illuskan": 30.0, "Chondathan": 20.0}}, + "Aasimar": {"chance": 12.0, "subraces": {"Fallen": 100.0}}, + "Tiefling": {"chance": 10.0}, + "Half-Elf": {"chance": 5.0, "subraces": {"Moon": 100.0}}, + "Gith": {"chance": 3.0}, + }, + }, + "lower_planes_le": { + "aliases": ["Baator", "Nine Hells", "Avernus", "Dis", "Minauros", + "Phlegethos", "Stygia", "Malbolge", "Maladomini", "Cania", "Nessus"], + "chances": { + "Tiefling": {"chance": 42.0}, + "Human": {"chance": 26.0, "subraces": {"Chondathan": 30.0, "Damaran": 25.0, "Tethyrian": 20.0, "Illuskan": 15.0, "Mulan": 10.0}}, + "Dwarf": {"chance": 12.0, "subraces": {"Duergar": 100.0}}, + "Half-Orc": {"chance": 8.0}, + "Aasimar": {"chance": 6.0, "subraces": {"Fallen": 100.0}}, + "Yuan-ti Pureblood": {"chance": 4.0}, + "Gith": {"chance": 2.0}, + }, + }, + "lower_planes_ne": { + "aliases": ["Hades", "Gray Waste", "Gehenna", "Carceri", + "Tarterus", "Oinos", "Niflheim", "Pluton"], + "chances": { + "Tiefling": {"chance": 38.0}, + "Human": {"chance": 28.0, "subraces": {"Damaran": 35.0, "Chondathan": 25.0, "Mulan": 20.0, "Tethyrian": 20.0}}, + "Orc": {"chance": 13.0}, + "Half-Orc": {"chance": 9.0}, + "Gith": {"chance": 7.0}, + "Genasi": {"chance": 5.0, "subraces": {"Earth": 50.0, "Fire": 50.0}}, + }, + }, + "lower_planes_ce": { + "aliases": ["Abyss", "Infinite Layers of Abyss", "Pazunia", + "Azzagrat", "Demonweb Pits", "Thanatos"], + "chances": { + "Tiefling": {"chance": 28.0}, + "Orc": {"chance": 22.0}, + "Half-Orc": {"chance": 15.0}, + "Human": {"chance": 14.0, "subraces": {"Damaran": 30.0, "Illuskan": 30.0, "Chondathan": 25.0, "Mulan": 15.0}}, + "Goblin": {"chance": 8.0}, + "Bugbear": {"chance": 6.0}, + "Elf": {"chance": 4.0, "subraces": {"Drow": 100.0}}, + "Yuan-ti Pureblood": {"chance": 3.0}, + }, + }, + "upper_planes_lg": { + "aliases": ["Mount Celestia", "Seven Heavens", "Celestia", "Arcadia", + "Mertion", "Solania", "Chronias"], + "chances": { + "Aasimar": {"chance": 38.0, "subraces": {"Protector": 100.0}}, + "Human": {"chance": 27.0, "subraces": {"Tethyrian": 35.0, "Chondathan": 30.0, "Damaran": 20.0, "Illuskan": 15.0}}, + "Dwarf": {"chance": 18.0, "subraces": {"Gold/Hill": 70.0, "Shield/Mountain": 30.0}}, + "Elf": {"chance": 9.0, "subraces": {"Sun": 70.0, "Moon": 30.0}}, + "Halfling": {"chance": 8.0, "subraces": {"Stout": 60.0, "Lightfoot": 40.0}}, + }, + }, + "upper_planes_ng": { + "aliases": ["Elysium", "Bytopia", "Dothion", "Shurrock", + "Amoria", "Eronia", "Belierin"], + "chances": { + "Aasimar": {"chance": 30.0, "subraces": {"Protector": 100.0}}, + "Gnome": {"chance": 22.0, "subraces": {"Forest": 55.0, "Rock": 45.0}}, + "Halfling": {"chance": 20.0, "subraces": {"Lightfoot": 55.0, "Stout": 45.0}}, + "Elf": {"chance": 15.0, "subraces": {"Wood": 60.0, "Moon": 40.0}}, + "Human": {"chance": 8.0, "subraces": {"Chondathan": 40.0, "Tethyrian": 30.0, "Illuskan": 30.0}}, + "Firbolg": {"chance": 5.0}, + }, + }, + "upper_planes_cg": { + "aliases": ["Arborea", "Arvandor", "Ysgard", "Olympus", + "Ossa", "Pelion", "Aquallor"], + "chances": { + "Elf": {"chance": 45.0, "subraces": {"Moon": 45.0, "Eladrin": 40.0, "Sun": 15.0}}, + "Aasimar": {"chance": 18.0, "subraces": {"Scourge": 60.0, "Protector": 40.0}}, + "Satyr": {"chance": 15.0}, + "Half-Elf": {"chance": 10.0, "subraces": {"Moon": 70.0, "Sun": 30.0}}, + "Genasi": {"chance": 7.0, "subraces": {"Air": 50.0, "Water": 50.0}}, + "Firbolg": {"chance": 5.0}, + }, + }, + "planes_of_chaos": { + "aliases": ["Limbo", "Pandemonium", "Everchanging Chaos", + "Pandesmos", "Cocytus", "Agathion"], + "chances": { + "Gith": {"chance": 52.0}, + "Genasi": {"chance": 20.0, "subraces": {"Air": 25.0, "Earth": 25.0, "Fire": 25.0, "Water": 25.0}}, + "Tiefling": {"chance": 12.0}, + "Human": {"chance": 9.0, "subraces": {"Illuskan": 40.0, "Damaran": 30.0, "Chondathan": 30.0}}, + "Aasimar": {"chance": 4.0, "subraces": {"Scourge": 100.0}}, + "Elf": {"chance": 3.0, "subraces": {"Eladrin": 100.0}}, + }, + }, + "plane_of_law": { + "aliases": ["Mechanus", "Clockwork Nirvana", "Regulus", "Nirvana"], + "chances": { + "Gith": {"chance": 38.0}, + "Human": {"chance": 34.0, "subraces": {"Damaran": 30.0, "Chondathan": 30.0, "Mulan": 20.0, "Tethyrian": 20.0}}, + "Dwarf": {"chance": 18.0, "subraces": {"Shield/Mountain": 80.0, "Gold/Hill": 20.0}}, + "Gnome": {"chance": 7.0, "subraces": {"Rock": 100.0}}, + "Aasimar": {"chance": 3.0, "subraces": {"Protector": 100.0}}, + }, + }, +} + + +# ─── Индекс алиасов для symspellpy ─────────────────────────────────────────── + +_ARTICLES = {"the", "a", "an", "of", "and"} + + +def _clean(text: str) -> str: + """Нижний регистр, удаление артиклей и апострофов, схлопывание пробелов.""" + text = text.lower().replace("'", "").replace(".", "") + words = [w for w in text.split() if w not in _ARTICLES] + return " ".join(words) + + +def _build_alias_index() -> tuple[dict, dict]: + """ + Возвращает два индекса: + world_index: {"toril": "Toril", "avernus": "lower_planes_le", ...} + region_index: {(scope, cleaned_alias): region_key} + scope — имя мира ("Toril") либо "__planes__". + """ + world_index: dict[str, str] = {} + region_index: dict[tuple[str, str], str] = {} + + for world_name in REGIONAL_OVERWRITES: + world_index[_clean(world_name)] = world_name + for region_key, data in REGIONAL_OVERWRITES[world_name].items(): + for alias in data["aliases"]: + region_index[(world_name, _clean(alias))] = region_key + + for plane_key, data in PLANAR_OVERWRITES.items(): + # Планы адресуются и как world, и как region + for alias in data["aliases"]: + world_index[_clean(alias)] = plane_key + region_index[("__planes__", _clean(alias))] = plane_key + world_index[_clean("planes")] = "__planes__" + + return world_index, region_index + + +WORLD_INDEX, REGION_INDEX = _build_alias_index() \ No newline at end of file