Add regions logic for generate npc tool

This commit is contained in:
dimitrievgs 2026-07-12 14:15:57 +03:00
parent 14e6222cf1
commit 8d8eff6863
18 changed files with 1392 additions and 13 deletions

View File

@ -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)

View File

@ -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}"

View File

@ -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] = []

View File

@ -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)
# ---------------------------------------------------------------------------
# Тест
# ---------------------------------------------------------------------------

View File

@ -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}")

View File

@ -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,

View File

@ -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,

964
generate_npc/regions.py Normal file
View File

@ -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()