323 lines
21 KiB
Python
323 lines
21 KiB
Python
"""
|
||
hooks_data.py — данные о хуках и весах типов встреч.
|
||
|
||
Содержит:
|
||
- ENCOUNTER_TYPE_WEIGHTS: веса типов встреч по биому.
|
||
- HOOK_WEIGHTS: веса хуков внутри каждого типа (универсальные).
|
||
- HOOKS: схема субъектов для каждого хука.
|
||
"""
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# Веса ТИПОВ встреч по биому/плану.
|
||
# Добавление нового плана = одна новая строка здесь.
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
|
||
ENCOUNTER_TYPE_WEIGHTS: dict[str, dict[str, int]] = {
|
||
"arctic": {"PvP": 50, "PvE": 25, "PvX": 20, "PvSelf": 5},
|
||
"coastal": {"PvP": 45, "PvE": 20, "PvX": 30, "PvSelf": 5}, # 10
|
||
"desert": {"PvP": 45, "PvE": 30, "PvX": 20, "PvSelf": 5}, # 10
|
||
"forest": {"PvP": 50, "PvE": 20, "PvX": 25, "PvSelf": 5}, # 10
|
||
"grassland": {"PvP": 50, "PvE": 20, "PvX": 25, "PvSelf": 5}, # 10
|
||
"hill": {"PvP": 50, "PvE": 20, "PvX": 25, "PvSelf": 5}, # 10
|
||
"mountain": {"PvP": 40, "PvE": 30, "PvX": 25, "PvSelf": 5},
|
||
"swamp": {"PvP": 35, "PvE": 25, "PvX": 35, "PvSelf": 5},
|
||
"underdark": {"PvP": 50, "PvE": 15, "PvX": 30, "PvSelf": 5},
|
||
"underwater": {"PvP": 30, "PvE": 35, "PvX": 30, "PvSelf": 5},
|
||
"urban": {"PvP": 40, "PvE": 15, "PvX": 25, "PvSelf": 20}, # 45
|
||
# Планы — новые строки:
|
||
"nine_hells": {"PvP": 55, "PvE": 25, "PvX": 15, "PvSelf": 5},
|
||
"astral_sea": {"PvP": 20, "PvE": 40, "PvX": 35, "PvSelf": 5},
|
||
"feywild": {"PvP": 35, "PvE": 25, "PvX": 30, "PvSelf": 10}, # 25
|
||
"plane_of_air": {"PvP": 35, "PvE": 30, "PvX": 30, "PvSelf": 5},
|
||
"plane_of_earth": {"PvP": 40, "PvE": 30, "PvX": 25, "PvSelf": 5},
|
||
"plane_of_fire": {"PvP": 45, "PvE": 25, "PvX": 25, "PvSelf": 5},
|
||
"plane_of_water": {"PvP": 35, "PvE": 30, "PvX": 30, "PvSelf": 5},
|
||
}
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# Веса ХУКОВ внутри каждого типа.
|
||
# Универсальны — не зависят от биома или плана.
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
|
||
HOOK_WEIGHTS: dict[str, dict[str, int]] = {
|
||
"PvP": {
|
||
"The Hunt": 18, # Охота, засада, набег
|
||
"The Routine": 20, # Патруль, миграция, фуражирование
|
||
"The Claim": 15, # Охрана территории/чека
|
||
"The Social Event": 12, # Процессия, ритуал, состязание
|
||
"The Interaction": 12, # Поиск контакта (торговля, помощь)
|
||
"The Internal Conflict": 10, # Две группы дерутся
|
||
"The Shadow": 7, # Скрытое наблюдение, шпионаж
|
||
"The Chase": 5,
|
||
"The Challenge": 1,
|
||
},
|
||
"PvE": {
|
||
"The Sudden Crisis": 28, # Сбежавшие животные, пожар, телега
|
||
"The Obstacle": 25, # Блок пути (завал, айсберг, водоросли)
|
||
"The Hazard": 18, # Опасная зона (водоворот, гейзер)
|
||
"The Anomaly": 17, # Дикая магия, корабль-призрак
|
||
"The Weather": 12,
|
||
},
|
||
"PvX": {
|
||
"The Landmark": 20, # Пустые руины, статуя, кораблекрушение
|
||
"The Remnant": 25, # Трупы, следы, обломки
|
||
"The Cache": 25, # Сокровище, жемчуг, магические ягоды
|
||
"The Lair": 20, # Обитаемая пещера, логово
|
||
"The Omen": 10, # Звуки, предзнаменования, эльфийская песня
|
||
},
|
||
"PvSelf": {
|
||
"The Plea": 30, # Выживший в воде, сирота
|
||
"The Moral Crossroads": 35, # Задиры, произвол стражи, призыв
|
||
"The Temptation": 22, # Теневая сделка, бесхозный лут
|
||
"The Betrayal": 13,
|
||
},
|
||
}
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# Схема субъектов для каждого хука.
|
||
#
|
||
# subject_sources — список слотов субъектов. Каждый слот:
|
||
# source: "creatures" → get_monsters_or_animals()
|
||
# "npc" → get_npc()
|
||
# count: строка с dice-нотацией или "1"
|
||
# role: роль в сцене (для нейросети): "primary", "aggressor",
|
||
# "defender", "victim", "bystander"
|
||
# optional: если True — слот появляется с вероятностью 50%
|
||
#
|
||
# latent_threat: True → существа могут появиться как следствие
|
||
# провала броска Surprise (решается в encounter_generator)
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
|
||
HOOKS: dict[str, dict[str, dict]] = {
|
||
"PvP": {
|
||
"The Hunt": {
|
||
"description": "Целенаправленная атака, засада или поиск добычи (хищники, пираты, бандиты).",
|
||
"subject_sources": [
|
||
{"source": "creatures", "count": "1d4+1", "role": "aggressor"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Routine": {
|
||
"description": "Существа заняты своей жизнью: миграция, патруль, фуражирование, прогулка.",
|
||
"subject_sources": [
|
||
{"source": "creatures", "count": "1d4+1", "role": "primary"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Claim": {
|
||
"description": "Существа активно охраняют логово, проход, предмет или территорию.",
|
||
"subject_sources": [
|
||
{"source": "creatures", "count": "1d4+1", "role": "defender"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Social Event": {
|
||
"description": (
|
||
"Массовое или социальное действие существ. "
|
||
"Мирные варианты: процессия, ярмарка, состязание, представление, "
|
||
"глашатай зачитывает указ, свадьба гоблинов. "
|
||
"Тёмные варианты: культисты призывают демона через ритуал жертвоприношения, "
|
||
"кобольды пытаются оживить каменного идола, "
|
||
"друиды проводят обряд плодородия — прервать можно, но опасно."
|
||
),
|
||
"subject_sources": [
|
||
{"source": "npc", "count": "1d4+2", "role": "bystander"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Internal Conflict": {
|
||
"description": "Две группы существ конфликтуют между собой: бунт, драка за добычу, политический протест/митинг, разборки между бандами, хищник - напавший на патруль.",
|
||
"subject_sources": [
|
||
{"source": "creatures", "count": "1d4", "role": "aggressor"},
|
||
{"source": "creatures", "count": "1d4", "role": "defender"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Shadow": {
|
||
"description": "Существа скрытно наблюдают, крадутся следом, пытаются обокрасть кого-то или совершают тайное действие.",
|
||
"subject_sources": [
|
||
{"source": "creatures", "count": "1d3", "role": "primary"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Interaction": {
|
||
"description": (
|
||
"Существа или NPC инициативно ищут контакта с героями. "
|
||
"Диапазон от мирного (торговцы, проводники, пьяный с байками) "
|
||
"до условного (медуза предлагает пропуск в обмен на историю, "
|
||
"хаг торгуется за услугу, дьявол — за душу, "
|
||
"культист вербует в орден)."
|
||
),
|
||
"subject_sources": [
|
||
{"source": "npc", "count": "1d3", "role": "primary"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Chase": {
|
||
"description": (
|
||
"Динамичное преследование или гонка, требующие проверок навыков, "
|
||
"а не прямого боя. "
|
||
"Примеры: конная погоня за курьером с секретным письмом, "
|
||
"скачки на грифонах над городом, гонки на лодках по подземной реке, "
|
||
"беглый преступник петляет по рыночным рядам, "
|
||
"гарпии преследуют торговый корабль."
|
||
),
|
||
"subject_sources": [
|
||
{"source": "creatures", "count": "1d3", "role": "aggressor"},
|
||
{"source": "npc", "count": "1", "role": "victim"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Challenge": {
|
||
"description": (
|
||
"Прямой вызов на состязание, дуэль или испытание навыков без цели убить. "
|
||
"Примеры: дворянин требует сатисфакции за оскорбление, варвар вызывает на "
|
||
"кулачный бой у костра, эльфийский следопыт предлагает пари на точность стрельбы, "
|
||
"монах вызывает на испытание воли (кто дольше простоит под ледяным водопадом)."
|
||
),
|
||
"subject_sources": [
|
||
{"source": "npc", "count": "1", "role": "aggressor"},
|
||
{"source": "npc", "count": "1d4", "role": "bystander", "optional": True},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
},
|
||
|
||
"PvE": {
|
||
"The Sudden Crisis": {
|
||
"description": "Динамичная, срочная проблема среды (пожар, сбежавшая телега/животное, обвал, обрушение конструкции, землетрясение, тонущее судно, паника на рынке, т.п.).",
|
||
"subject_sources": [
|
||
{"source": "npc", "count": "1", "role": "victim", "optional": True},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Obstacle": {
|
||
"description": "Масштабная преграда, блокирующая путь (завал на лесной тропе, айсберг, заросли, пропасть, запертые ворота).",
|
||
"subject_sources": [],
|
||
"latent_threat": False,
|
||
},
|
||
"The Hazard": {
|
||
"description": "Пассивная опасная зона или ловушка (водоворот, гейзер, ядовитые споры, зыбучие пески).",
|
||
"subject_sources": [],
|
||
"latent_threat": True,
|
||
},
|
||
"The Anomaly": {
|
||
"description": "Сложное природное или магическое явление (мираж, корабль-призрак, зона дикой магии, парящие острова, светящиеся туманы, портал).",
|
||
"subject_sources": [],
|
||
"latent_threat": True,
|
||
},
|
||
"The Weather": {
|
||
"description": (
|
||
"Экстремальное погодное явление, меняющее условия всей зоны встречи. "
|
||
"Примеры: внезапная метель обнуляет видимость и замедляет движение, "
|
||
"пылевая буря на равнине, ураган у побережья, "
|
||
"кислотный дождь в Nine Hells разъедает снаряжение, "
|
||
"густой туман скрывает всё дальше 10 футов, "
|
||
"магическая гроза бьёт молниями случайно."
|
||
),
|
||
"subject_sources": [],
|
||
"latent_threat": True, # в буре может скрываться хищник
|
||
},
|
||
},
|
||
|
||
"PvX": {
|
||
"The Landmark": {
|
||
"description": (
|
||
"Заметный объект инфраструктуры, древности или магии. "
|
||
"Пассивные: пустые руины, статуя героя, монолит с надписями, "
|
||
"кораблекрушение, каменный круг. "
|
||
"Интерактивные (Puzzle): древняя дверь с загадкой на трёх языках, "
|
||
"магический замок с шифром из созвездий, "
|
||
"каменная плита с рунами, требующими правильного порядка нажатий, "
|
||
"зеркало, отражающее иное место."
|
||
),
|
||
"subject_sources": [],
|
||
"latent_threat": True,
|
||
},
|
||
"The Remnant": {
|
||
"description": (
|
||
"Свидетельства недавних или давних событий. "
|
||
"Краткосрочные следы: трупы, сломанные повозки, плавающие обломки, "
|
||
"остатки лагеря, брошенные вещи, сожжённая роща. "
|
||
"Долгосрочные последствия (Fallout): отравленная река после битвы армий, "
|
||
"выжженная земля проклятием личей, "
|
||
"зона некротической энергии — земля мертва уже десять лет, "
|
||
"пепелище деревни с ловушками мародёров."
|
||
),
|
||
"subject_sources": [],
|
||
"latent_threat": True,
|
||
},
|
||
"The Cache": {
|
||
"description": "Находка, имеющая ценность (магическое растение, утерянный груз, схрон, жемчуг, сундук с сокровищами, полезный предмет на тротуаре).",
|
||
"subject_sources": [],
|
||
"latent_threat": True,
|
||
},
|
||
"The Lair": {
|
||
"description": "Место обитания монстров, героев может ждать хозяин дома (грот осьминога, заселенные руины, гнездо грифона, бандитский притон, т.п.).",
|
||
"subject_sources": [
|
||
{"source": "creatures", "count": "1d6", "role": "primary"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Omen": {
|
||
"description": (
|
||
"Информационный сигнал из среды — от неясного до конкретного. "
|
||
"Знамения: странная мелодия из тумана, дым трёх цветов на горизонте, "
|
||
"все птицы разом умолкли, мертвая рыба плывёт по реке вверх. "
|
||
"Сообщения (Message): послание в бутылке с зашифрованными координатами, "
|
||
"магический камень с записью голоса мёртвого мага, "
|
||
"надпись на стене древним языком («они уже внутри»), "
|
||
"застывший в янтаре свиток с предупреждением."
|
||
),
|
||
"subject_sources": [],
|
||
"latent_threat": True,
|
||
},
|
||
},
|
||
|
||
"PvSelf": {
|
||
"The Plea": {
|
||
"description": (
|
||
"Кто-то молит о помощи или ситуация требует самопожертвования. "
|
||
"Просьба о действии: выживший на обломках судна, "
|
||
"раненый рыцарь у дороги, пленник в клетке, "
|
||
"умоляющий купец («они убьют мою семью»). "
|
||
"Просьба об отказе (Sacrifice): сломай магический меч — снимешь проклятие с деревни, "
|
||
"сожги эти записи — они единственное доказательство твоей невиновности, "
|
||
"потеряй память о прошлом, чтобы пройти через врата богов."
|
||
),
|
||
"subject_sources": [
|
||
{"source": "npc", "count": "1", "role": "victim"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Moral Crossroads": {
|
||
"description": "Столкновение с явной несправедливостью или злоупотреблением (задиры, произвол стражи или налоговика, несправедливый арест, жестокое обращение с животными, лорд приказывает выдать беглого слугу, т.п.).",
|
||
"subject_sources": [
|
||
{"source": "npc", "count": "1d3+", "role": "aggressor"},
|
||
{"source": "npc", "count": "1", "role": "victim"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Temptation": {
|
||
"description": "Соблазн получить выгоду сомнительным путем (теневая сделка, бесхозный кошелёк или груз на дороге, возможность украсть у пьяного, забытые в храме деньги, т.п.).",
|
||
"subject_sources": [
|
||
{"source": "npc", "count": "1", "role": "primary", "optional": True},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
"The Betrayal": {
|
||
"description": (
|
||
"Герой обнаруживает, что союзник, друг или заказчик предал его. "
|
||
"Примеры: контрактник сбежал с авансом и уликами на героев, "
|
||
"NPC которого спасли — оказывается осведомителем врага, "
|
||
"заказчик нанимал, зная что посылает на верную смерть, "
|
||
"проводник ведёт прямо в засаду, "
|
||
"союзный маг тайно работает на гильдию убийц."
|
||
),
|
||
"subject_sources": [
|
||
{"source": "npc", "count": "1", "role": "traitor"},
|
||
],
|
||
"latent_threat": False,
|
||
},
|
||
},
|
||
} |