492 lines
28 KiB
Python
492 lines
28 KiB
Python
# races.py
|
||
import random
|
||
from languages import (
|
||
Common, Celestial, Elvish, Dwarvish, Draconic, GiantLanguage,
|
||
GnomishLanguage, GoblinLanguage, HalflingLanguage, OrcLanguage,
|
||
Infernal, Primordial, Sylvan, Abyssal, GithLanguage, LeoninLanguage,
|
||
Undercommon, Alzhedo, Chondathan, Damaran, Illuskan, KoryoLanguage,
|
||
Mulani, Rashemi, Shou,
|
||
living_languages_except_common,
|
||
)
|
||
|
||
# ─── Расы ────────────────────────────────────────────────────────────────────
|
||
|
||
RACES: dict = {
|
||
"Aasimar": {
|
||
"chance": 1.00,
|
||
"subraces": {
|
||
"Protector": {"chance": 50.00},
|
||
"Scourge": {"chance": 30.00},
|
||
"Fallen": {"chance": 20.00},
|
||
},
|
||
"names": "Human",
|
||
"languages": [Common, Celestial],
|
||
},
|
||
"Bugbear": {
|
||
"chance": 0.50,
|
||
"names": "Goblin",
|
||
"languages": [Common, GoblinLanguage],
|
||
},
|
||
"Dragonborn": {
|
||
"chance": 1.00,
|
||
"subraces": {color: {"chance": 10.00} for color in
|
||
["Black","Blue","Brass","Bronze","Copper",
|
||
"Gold","Green","Red","Silver","White"]},
|
||
"names": {
|
||
"M": ["Арджхан","Баласар","Бхараш","Гхеш","Донаар","Крив","Медраш","Мехен",
|
||
"Надарр","Панджед","Патрин","Рхогар","Тархун","Торинн","Хескан","Шамаш","Шединн"],
|
||
"F": ["Акра","Бири","Даар","Джхери","Кава","Коринн","Мисханн","Нала",
|
||
"Перра","Райанн","Сора","Сурина","Тхава","Уаджит","Фаридэ","Хавилар","Харанн"],
|
||
},
|
||
"languages": [Common, Draconic],
|
||
},
|
||
"Dwarf": {
|
||
"chance": 10.00,
|
||
"subraces": {
|
||
"Gold/Hill": {"chance": 50.00, "languages": [Common, Dwarvish]},
|
||
"Shield/Mountain":{"chance": 40.00, "languages": [Common, Dwarvish]},
|
||
"Duergar": {"chance": 10.00, "languages": [Common, Dwarvish, Undercommon]},
|
||
},
|
||
"names": {
|
||
"M": ["Адрик","Альберих","Баренд","Барундар","Баэрн","Броттор","Бруенор","Вондал",
|
||
"Вулгар","Вэйт","Гардаин","Даин","Даррак","Делг","Джойин","Дорн","Килдрак",
|
||
"Моргран","Орсик","Оскар","Рангрим","Рорин","Рюрик","Сторн","Таклинн",
|
||
"Торадин","Тордек","Торин","Торик","Травок","Траубон","Ульфгар","Фаргрим",
|
||
"Флинт","Харбек","Хондар","Эберк","Эйнкиль"],
|
||
"F": ["Артин","Бардрин","Белмара","Вистра","Гуннлода","Гурдис","Дагнал","Джойлин",
|
||
"Диеза","Дорна","Илде","Катра","Киира","Кристид","Лифтраса","Мардред",
|
||
"Одхильд","Рисвин","Самбрил","Саннл","Тейс","Торбера","Торгга","Умиль",
|
||
"Фалкрунн","Финеллен","Хельджа","Хлин","Эльдет","Эмбер"],
|
||
"S": ["Балдерк","Блейтбайт","Боевой Молот","Гордривер","Горунн","Данкил",
|
||
"Железный Кулак","Каменный Щит","Крауншилд","Крепкая Наковальня",
|
||
"Ледяная Борода","Лодерр","Лютгер","Огненная Кузня","Рамнахейм",
|
||
"Рог","Скуллдарк","Стракелн","Торунн","Унгарт","Холдерхек"],
|
||
},
|
||
"languages": [Common, Dwarvish],
|
||
},
|
||
"Elf": {
|
||
"chance": 10.00,
|
||
"subraces": {
|
||
"Avariel": {"chance": 0.50, "names": "Moon", "languages": [Common, Elvish]},
|
||
"Drow": {
|
||
"chance": 10.00,
|
||
"names": {
|
||
"M": ["Алак","Дриззт","Ильмрин","Меринид","Фарон","Риззон","Тебрин","Закнафейн"],
|
||
"F": ["Акордия","Чалитра","Эклавдра","Джаэлрина","Недилен","Квилуэ","СиНафай","Влондрил"],
|
||
"S": ["Абафейр","Колоара","Гланнат","Иллистин","Фарн","Сиреар","Вринн","Ксилтин"],
|
||
},
|
||
"languages": [Common, Elvish, Undercommon],
|
||
},
|
||
"Eladrin": {"chance": 2.50, "names": "Moon", "languages": [Common, Elvish]},
|
||
"Moon": {
|
||
"chance": 34.50,
|
||
"names": {
|
||
"M": ["Адран","Аравилар","Арамиль","Араннис","Ауст","Аэлар","Бейро","Берриан",
|
||
"Варис","Галинндан","Ивеллиос","Иммераль","Каррик","Куарион","Лаусиан",
|
||
"Миндартис","Морн","Ним","Орлпар","Паэлиас","Перен","Респен","Риардон",
|
||
"Ристель","Ролен","Саэвел","Совелисс","Тамиорн","Таривол","Терен",
|
||
"Тэген","Фейлар","Хадарай","Химо","Хэйян","Эниалис","Эрдан","Эреван"],
|
||
"F": ["Адрие","Альтеа","Амра","Анастрианна","Андрасте","Антинуа","Бетринна",
|
||
"Бирель","Вадания","Валанте","Вестеле","Джаумрит","Джеленетт","Друсилиа",
|
||
"Имизаэль","Йелениа","Каэлинн","Квамара","Квеленна","Квиласи","Кейлет",
|
||
"Ксанафия","Лешанна","Лиа","Миали","Мэриэль","Найвара","Сариэль","Силакви",
|
||
"Талиндра","Теирастра","Тиа","Фелосиаль","Хакатра","Шава","Шанайра","Энна","Ястра"],
|
||
"S": ["Амакиир (Сверкающий Цветок)","Амалит","Амастасия (Звёздный Цветок)",
|
||
"Брэген","Галанодель (Лунный Шёпот)","Ивнингфолл","Ильфелкиир (Сверкающий Бутон)",
|
||
"Калаудра","Ксилосент (Золотой Лепесток)","Лаэлитар","Лиадон (Серебряный Лист)",
|
||
"Мундаун","Найло (Ночной Бриз)","Сианодель (Лунный Ручей)","Тарнрут",
|
||
"Холимион (Алмазная Роса)"],
|
||
},
|
||
"languages": [Common, Elvish],
|
||
},
|
||
"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]},
|
||
},
|
||
"languages": [Common, Elvish],
|
||
},
|
||
"Firbolg": {
|
||
"chance": 0.20,
|
||
"names": "Elf",
|
||
"languages": [Common, Elvish, GiantLanguage],
|
||
},
|
||
"Genasi": {
|
||
"chance": 1.00,
|
||
"subraces": {
|
||
"Air": {"chance": 25.00},
|
||
"Earth": {"chance": 25.00},
|
||
"Fire": {"chance": 25.00},
|
||
"Water": {"chance": 25.00},
|
||
},
|
||
"names": "Human",
|
||
"languages": [Common, Primordial],
|
||
},
|
||
"Gith": {
|
||
"chance": 0.20,
|
||
"names": {
|
||
"M": ["Al'chaia","Dak'kon","Lae'zel","Vhostym","Yrlakka"],
|
||
"F": ["Al'chaia","Dak'kon","Lae'zel","Vhostym","Yrlakka"],
|
||
},
|
||
"languages": [Common, GithLanguage],
|
||
},
|
||
"Gnome": {
|
||
"chance": 2.00,
|
||
"subraces": {
|
||
"Forest": {"chance": 50.00},
|
||
"Rock": {"chance": 50.00},
|
||
},
|
||
"names": {
|
||
"M": ["Алвин","Алстон","Боддинок","Брок","Бургелл","Бюргел","Варрин","Вренн","Гербо",
|
||
"Гимбл","Глим","Джебеддо","Димбл","Дорган","Зук","Келлен","Колмарр","Намфудл",
|
||
"Орламм","Оррин","Ронделл","Рундар","Сибо","Синдри","Столиг","Фалринн","Фонкин",
|
||
"Фрюг","Халбринн","Элдон","Эрки"],
|
||
"F": ["Бимпноттин","Брина","Вейуокет","Джари","Донелла","Дувамил","Занна","Зелазадда",
|
||
"Ийисс","Каландра","Карамип","Карлин","Лилли","Лисса","Лорилла","Лупмоттин",
|
||
"Маднаб","Мери","Нати","Никс","Нисса","Ода","Орла","Ройвин","Тана","Шамил",
|
||
"Эливик","Элиджобелл","Элла","Эрисс"],
|
||
"S": ["Берен","Блэкрок","Блимс","Гаррик","Грейторм","Даергел","Мёрниг","Накл",
|
||
"Нингел","Раулнор","Ривенстоун","Тавартарр","Тимберс","Турен","Уайтхорн",
|
||
"Уварикк","Фолькор","Шеппен"],
|
||
},
|
||
"languages": [Common, GnomishLanguage],
|
||
},
|
||
"Goblin": {
|
||
"chance": 1.00,
|
||
"names": {
|
||
"M": ["Аругет","Вании","Ванон","Вуударадж","Гудруун","Даавн","Дабрак","Дагии",
|
||
"Джазаал","Древдуул","Дуулан","Каллаад","Кракуул","Кроотад","Мазаан","Мунта",
|
||
"Насаар","Ракари","Рексиит","Тарик","Тарууж","Туун","Феник","Четин","Халуун",
|
||
"Харуук","Аззинакс","Баболакс","Бликсаникс","Вазозав","Вексин","Даззаз",
|
||
"Зиззикс","Калузакс","Криксизикс","Лизакса","Миззикс","Мизнар","Никсиспикс",
|
||
"Паксизаз","Равиксиз","Стиксил","Сунникс","Тозинокс","Уксивози","Эстрикс",
|
||
"Финизикс","Юзба"],
|
||
"F": ["Ааспар","Агуус","Белалуур","Вали","Вуун","Грааль","Гадуул","Денаал","Джелуум",
|
||
"Драраар","Дууша","Келаал","Мулаан","Насри","Разу","Ралин","Рексин","Сенен",
|
||
"Таджин","Тюнер","Хашак","Шедруор","Эхаас","Элуун"],
|
||
},
|
||
"languages": [Common, GoblinLanguage],
|
||
},
|
||
"Goliath": {
|
||
"chance": 0.50,
|
||
"names": {
|
||
"M": ["Аукан","Ваунеа","Вимак","Гае-Ал","Иглат","Иликан","Кеотхи","Куори",
|
||
"Ло-Каг","Мавейт","Маннео","Налла","Орило","Пааву","Петани","Талай","Тхотам","Утхал"],
|
||
"F": ["Аукан","Ваунеа","Вимак","Гае-Ал","Иглат","Иликан","Кеотхи","Куори",
|
||
"Ло-Каг","Мавейт","Маннео","Налла","Орило","Пааву","Петани","Талай","Тхотам","Утхал"],
|
||
},
|
||
"languages": [Common, GiantLanguage],
|
||
},
|
||
"Half-Elf": {
|
||
"chance": 6.00,
|
||
"subraces": {
|
||
"Drow": {"chance": 3.50},
|
||
"Eladrin": {"chance": 2.50},
|
||
"Moon": {"chance": 56.50},
|
||
"Shadar-kai":{"chance": 2.50},
|
||
"Sun": {"chance": 10.00},
|
||
"Wood": {"chance": 25.00},
|
||
},
|
||
"names": ["Human", "Elf"],
|
||
"languages": [Common, Elvish, living_languages_except_common],
|
||
},
|
||
"Half-Orc": {
|
||
"chance": 3.00,
|
||
"names": {
|
||
"M": ["Гел","Денч","Имш","Кет","Краск","Муррен","Ронт","Токк","Фенг","Хенк","Холг","Шамп"],
|
||
"F": ["Багги","Вола","Волен","Евельда","Кансиф","Мев","Нига","Овак","Оунка","Сута","Шаута","Эмен","Энгонг"],
|
||
},
|
||
"languages": [Common, OrcLanguage],
|
||
},
|
||
"Halfling": {
|
||
"chance": 3.00,
|
||
"subraces": {
|
||
"Ghostwise": {"chance": 10.00},
|
||
"Lightfoot": {"chance": 60.00},
|
||
"Stout": {"chance": 30.00},
|
||
},
|
||
"names": {
|
||
"M": ["Альтон","Андер","Блазанар","Вилимак","Гаррет","Далабрак","Кейд","Коррин",
|
||
"Коркаури","Лайл","Линдал","Майло","Меррик","Омберт","Осборн","Перрин","Рид",
|
||
"Роберк","Роско","Тирраури","Уэллби","Финнан","Халандар","Элдон","Эррих"],
|
||
"F": ["Алонирра","Бри","Вани","Венинда","Верна","Делдира","Джиллиан","Калатра",
|
||
"Китри","Кора","Кэлли","Лавиния","Лидда","Мелинден","Мерла","Недда","Олпара",
|
||
"Паэла","Портия","Росинден","Серафина","Тара","Трим","Шаэна","Эндри","Юфемия"],
|
||
"S": ["Аумбль","Брамблфут","Вверхтормашкин","Высокохолм","Галькоброс","Дардаргон",
|
||
"Добробочка","Зеленофляг","Кустосбор","Лугодуг","Мерримар","Подветкин",
|
||
"Репейник","Старнхап","Хардингдейл","Чайнолист"],
|
||
},
|
||
"languages": [Common, HalflingLanguage],
|
||
},
|
||
"Hobgoblin": {
|
||
"chance": 0.50,
|
||
"names": "Goblin",
|
||
"languages": [Common, GoblinLanguage],
|
||
},
|
||
"Human": {
|
||
"chance": 54.70,
|
||
"subraces": {
|
||
"Calishite": {
|
||
"chance": 20.00,
|
||
"names": {"M":["Асеир","Бардеид","Зашеир","Кхемед","Мехмен","Судейман","Хасеид"],
|
||
"F":["Атала","Джасмаль","Зашеида","Мейлиль","Сейдиль","Сейпора","Хама","Яшеира"],
|
||
"S":["Баша","Джассан","Думеин","Кхалид","Мостана","Пашар","Рейн"]},
|
||
"languages": [Common, Alzhedo],
|
||
},
|
||
"Chondathan": {
|
||
"chance": 20.00,
|
||
"names": {"M":["Горстаг","Грим","Дарвин","Дорн","Маларк","Морн","Рэндал","Стедд","Хельм","Эвендур"],
|
||
"F":["Арвин","Джессаиль","Керри","Лурин","Мири","Рован","Тесселе","Шандри","Эсвель"],
|
||
"S":["Бакмэн","Грэйкасл","Дандрэгон","Толстаг","Эвенвуд","Эмблкроун"]},
|
||
"languages": [Common, Chondathan],
|
||
},
|
||
"Damaran": {
|
||
"chance": 15.00,
|
||
"names": {"M":["Айвор","Бор","Глэр","Григор","Иган","Козеф","Миваль","Орел","Павел","Сергор","Фодель"],
|
||
"F":["Алетра","Зора","Кара","Катернин","Мара","Натали","Ольма","Тана"],
|
||
"S":["Берск","Дотск","Куленов","Марск","Немецк","Стараг","Чернин","Шемов"]},
|
||
"languages": [Common, Damaran],
|
||
},
|
||
"Illuskan": {
|
||
"chance": 15.00,
|
||
"names": {"M":["Блас","Бран","Гет","Ландер","Лют","Малсер","Стор","Таман","Урт","Фрат","Эндер"],
|
||
"F":["Амафрея","Бетха","Вестра","Кетра","Мара","Ольга","Силифрей","Цефрея"],
|
||
"S":["Брайтвуд","Виндривер","Лакмэн","Хелдер","Хорнрейвен","Штормвинд"]},
|
||
"languages": [Common, Illuskan],
|
||
},
|
||
"Koryoan": {
|
||
"chance": 2.50,
|
||
"names": "Shou",
|
||
"languages": [Common, KoryoLanguage],
|
||
},
|
||
"Mulan": {
|
||
"chance": 10.00,
|
||
"names": {"M":["Аот","Барерис","Кетот","Мумед","Рамас","Со-Кехур","Тхазар-Де","Урхур","ЭхпутКи"],
|
||
"F":["Аризима","Золис","Мурити","Нефис","Нулара","Сефрис","Тола","Умара","Чатхи"],
|
||
"S":["Анкхалаб","Анскульд","Натандем","Серпет","Уутракт","Фезим","Хахпет"]},
|
||
"languages": [Common, Mulani],
|
||
},
|
||
"Rashemi": {
|
||
"chance": 7.50,
|
||
"names": {"M":["Боривик","Владислак","Джандар","Канитар","Мадислак","Ральмевик","Фаургар","Шаумар"],
|
||
"F":["Имзель","Иммит","Наварра","Таммит","Файварра","Хулмарра","Шеварра","Юльдра"],
|
||
"S":["Дайрнина","Илтазяра","Мурнитара","Стаянога","Улмокина","Чергоба"]},
|
||
"languages": [Common, Rashemi],
|
||
},
|
||
"Shou": {
|
||
"chance": 2.50,
|
||
"names": {"M":["Ан","Вэнь","Лонг","Лянь","Менг","Он","Фай","Цзюн","Цзянь","Чен","Чи","Шань","Шуй"],
|
||
"F":["Бай","Ксяо","Лей","Мей","Тай","Цзя","Чао","Шуй"],
|
||
"S":["Вань","Као","Кунг","Лао","Линг","Мей","Пинь","Сум","Тань","Хуан","Чиень","Шин"]},
|
||
"languages": [Common, Shou],
|
||
},
|
||
"Tethyrian": {
|
||
"chance": 7.50,
|
||
"names": "Chondathan",
|
||
"languages": [Common, Chondathan],
|
||
},
|
||
},
|
||
},
|
||
"Kenku": {
|
||
"chance": 0.50,
|
||
"names": {
|
||
"M": ["Разбиватель","Лязгун","Разрезатель","Грохотун","Крысиный Скрежет","Свистун","Мышатник","Рычун"],
|
||
"F": ["Свистун","Мышатник","Рычун"],
|
||
},
|
||
"languages": [Common, Primordial],
|
||
},
|
||
"Kobold": {"chance": 0.50, "names": [], "languages": [Common, Draconic]},
|
||
"Leonin": {
|
||
"chance": 0.10,
|
||
"names": {
|
||
"M": ["Апто","Ат","Барагон","Брыг","Горий","Грекс","Земн","Зиор","Орий","Пиксатор","Тей","Эрем"],
|
||
"F": ["Алета","Атагона","Грагонда","Демна","Доксия","Зиора","Иадма","Койла","Орамна","Сеза","Эката","Эриз"],
|
||
},
|
||
"languages": [Common, LeoninLanguage],
|
||
},
|
||
"Lizardfolk": {"chance": 0.50, "names": [], "languages": [Common, Draconic]},
|
||
"Orc": {
|
||
"chance": 2.00,
|
||
"names": {
|
||
"M": ["Абзуг","Бажок","Беск","Готог","Груток","Джахрук","Дурт","Кеск","Лортар","Мобад",
|
||
"Мугруб","Орруск","Рук","Рхог","Тараг","Тог","Угурт","Урзул","Фанг","Харл","Шамок","Шугог"],
|
||
"F": ["Багги","Бетарра","Варра","Вола","Волен","Дуваега","Имен","Йескарра","Кансиф","Креске",
|
||
"Муай","Неске","Ниига","Овак","Орваега","Оунка","Сута","Шаута","Эдарреске","Энгонг"],
|
||
"S": ["Думник","Хортор","Ламмар","Сормужик","Турнчерпал","Улькруннар","Зоргар"],
|
||
},
|
||
"languages": [Common, OrcLanguage],
|
||
},
|
||
"Satyr": {"chance": 0.10, "names": [], "languages": [Common, Sylvan]},
|
||
"Tabaxi": {"chance": 0.50, "names": [], "languages": [Common, living_languages_except_common]},
|
||
"Tiefling": {
|
||
"chance": 1.00,
|
||
"names": {
|
||
"M": ["Акменос","Амнон","Баракас","Дамакос","Йадос","Кайрон","Люцис","Мелех","Мордай",
|
||
"Мортос","Пелайос","Скамос","Терай","Экемон"],
|
||
"F": ["Акта","Анакис","Брисеис","Дамая","Каллиста","Криелла","Лерисса","Макария",
|
||
"Немея","Орианна","Риета","Фелая","Эа"],
|
||
"S": ["Амезис","Барбатос","Дракос","Инфернус","Мефисто","Фирнас","Хекатон","Харгрейв","Эбони"],
|
||
},
|
||
"languages": [Common, Infernal],
|
||
},
|
||
"Triton": {
|
||
"chance": 0.10,
|
||
"names": {
|
||
"M": ["Водос","Делнис","Джимас","Зунис","Керос","Корус","Молос","Налос"],
|
||
"F": ["Белтин","Вуолин","Влэрин","Дьютин","Отанин","Филорин","Шелрин","Эрин"],
|
||
},
|
||
"languages": [Common, Primordial],
|
||
},
|
||
"Yuan-ti Pureblood": {
|
||
"chance": 0.10,
|
||
"names": [],
|
||
"languages": [Common, Abyssal, Draconic],
|
||
},
|
||
}
|
||
|
||
|
||
# ─── Вспомогательные функции ─────────────────────────────────────────────────
|
||
|
||
def _weighted_choice(items: list[tuple[str, float]]) -> str:
|
||
"""items = [(name, chance), ...]. Возвращает выбранное name."""
|
||
total = sum(c for _, c in items)
|
||
r = random.random() * total
|
||
cumulative = 0.0
|
||
for name, chance in items:
|
||
cumulative += chance
|
||
if r <= cumulative:
|
||
return name
|
||
return items[-1][0]
|
||
|
||
|
||
def select_random_race_and_subrace() -> dict:
|
||
"""Возвращает {"race": str, "subrace": str | None}."""
|
||
race_items = [(r, d["chance"]) for r, d in RACES.items()]
|
||
race_name = _weighted_choice(race_items)
|
||
race_data = RACES[race_name]
|
||
|
||
subrace_name = None
|
||
if "subraces" in race_data and race_data["subraces"]:
|
||
sub_items = [(s, d["chance"]) for s, d in race_data["subraces"].items()]
|
||
subrace_name = _weighted_choice(sub_items)
|
||
|
||
return {"race": race_name, "subrace": subrace_name}
|
||
|
||
|
||
def get_name(race_obj: dict, gender: str) -> dict:
|
||
"""Возвращает {"name": str, "race": str, "subrace": str | None}."""
|
||
gender_key = gender.upper()
|
||
race_key = race_obj["race"]
|
||
subrace_key = race_obj.get("subrace")
|
||
|
||
def resolve_names(r_key: str, s_key: str | None):
|
||
"""Рекурсивно разрешает ссылки на имена, возвращает (nameData, source_race, source_subrace)."""
|
||
race_data = RACES.get(r_key)
|
||
if not race_data:
|
||
return None, None, None
|
||
|
||
# Определяем имена
|
||
names = None
|
||
is_race_level_ref = False
|
||
|
||
if s_key and "subraces" in race_data and s_key in race_data["subraces"]:
|
||
sub_data = race_data["subraces"][s_key]
|
||
if "names" in sub_data:
|
||
names = sub_data["names"]
|
||
else:
|
||
names = race_data.get("names")
|
||
is_race_level_ref = True
|
||
else:
|
||
if "subraces" in race_data and race_data["subraces"]:
|
||
# Выбираем случайную подрасу с именами
|
||
for candidate in random.sample(list(race_data["subraces"].keys()),
|
||
len(race_data["subraces"])):
|
||
if "names" in race_data["subraces"][candidate]:
|
||
names = race_data["subraces"][candidate]["names"]
|
||
s_key = candidate
|
||
break
|
||
if names is None:
|
||
names = race_data.get("names")
|
||
is_race_level_ref = True
|
||
else:
|
||
names = race_data.get("names")
|
||
is_race_level_ref = True
|
||
|
||
if names is None:
|
||
return None, None, None
|
||
|
||
# Строка-ссылка
|
||
if isinstance(names, str):
|
||
if is_race_level_ref:
|
||
return resolve_names(names, None)
|
||
else:
|
||
return resolve_names(r_key, names)
|
||
|
||
# Список ссылок (Half-Elf: ["Human", "Elf"])
|
||
if isinstance(names, list) and names and isinstance(names[0], str):
|
||
ref = random.choice(names)
|
||
return resolve_names(ref, None)
|
||
|
||
# Прямой словарь {"M":[], "F":[], "S":[]}
|
||
if isinstance(names, dict):
|
||
name_data: dict[str, list] = {"M": [], "F": [], "S": []}
|
||
for g in ("M", "F", "S"):
|
||
if g in names:
|
||
name_data[g] = list(names[g])
|
||
return name_data, r_key, s_key
|
||
|
||
return None, None, None
|
||
|
||
name_data, src_race, src_subrace = resolve_names(race_key, subrace_key)
|
||
if not name_data:
|
||
return {"name": "", "race": race_key, "subrace": subrace_key}
|
||
|
||
first_names = name_data.get(gender_key, [])
|
||
if not first_names:
|
||
first_names = name_data.get("F" if gender_key == "M" else "M", [])
|
||
if not first_names:
|
||
return {"name": "", "race": src_race, "subrace": src_subrace}
|
||
|
||
surname_list = name_data.get("S", [])
|
||
first_name = random.choice(first_names)
|
||
surname = random.choice(surname_list) if surname_list else ""
|
||
|
||
return {
|
||
"name": f"{first_name} {surname}".strip(),
|
||
"race": src_race,
|
||
"subrace": src_subrace,
|
||
}
|
||
|
||
|
||
def get_languages(race_obj: dict, full_name_obj: dict) -> list[dict]:
|
||
"""Возвращает список языков для расы/подрасы персонажа."""
|
||
race_key = race_obj["race"]
|
||
subrace_key = race_obj.get("subrace")
|
||
|
||
def get_lang_list(r_key: str, s_key: str | None) -> list:
|
||
r_data = RACES.get(r_key)
|
||
if not r_data:
|
||
return []
|
||
if s_key and "subraces" in r_data and s_key in r_data["subraces"]:
|
||
sub = r_data["subraces"][s_key]
|
||
if "languages" in sub:
|
||
return sub["languages"]
|
||
return r_data.get("languages", [])
|
||
|
||
lang_list = get_lang_list(race_key, subrace_key)
|
||
languages: list[dict] = []
|
||
|
||
for item in lang_list:
|
||
if isinstance(item, list):
|
||
# Массив языков на выбор — выбираем один случайный, не совпадающий с уже добавленными
|
||
available = [l for l in item if l not in languages]
|
||
if available:
|
||
languages.append(random.choice(available))
|
||
else:
|
||
languages.append(item)
|
||
|
||
# Дедупликация с сохранением порядка
|
||
seen = []
|
||
for lang in languages:
|
||
if lang not in seen:
|
||
seen.append(lang)
|
||
return seen |