Fix propose/resolve faction turn
This commit is contained in:
parent
1ec3b18bb1
commit
74d580e740
|
|
@ -40,6 +40,7 @@ from faction_utils import (
|
||||||
roll_dice,
|
roll_dice,
|
||||||
roll_attribute_check,
|
roll_attribute_check,
|
||||||
are_within_one_move,
|
are_within_one_move,
|
||||||
|
write_faction,
|
||||||
)
|
)
|
||||||
from wwn_assets_data import (
|
from wwn_assets_data import (
|
||||||
get_available_assets,
|
get_available_assets,
|
||||||
|
|
@ -279,7 +280,7 @@ def _build_hide_asset_options(faction_fm: dict, all_factions: list[dict], locati
|
||||||
if tf.get("name") != faction_fm.get("name")
|
if tf.get("name") != faction_fm.get("name")
|
||||||
)
|
)
|
||||||
if hostile_boi_in_loc:
|
if hostile_boi_in_loc:
|
||||||
return []
|
return [] # <--- скрытие запрещено
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
for a in faction_fm.get("assets", []):
|
for a in faction_fm.get("assets", []):
|
||||||
|
|
@ -363,7 +364,7 @@ def _build_create_asset_options(faction_fm: dict, location_wikilink: str) -> lis
|
||||||
"""
|
"""
|
||||||
available = get_available_assets(faction_fm, location_wikilink)
|
available = get_available_assets(faction_fm, location_wikilink)
|
||||||
if not available:
|
if not available:
|
||||||
return [" - *(нет доступных активов для покупки)*"]
|
return [] # <--- теперь возвращаем пустой список, а не заглушку
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
for a in available:
|
for a in available:
|
||||||
|
|
@ -478,19 +479,42 @@ def _format_faction_section(
|
||||||
create_opts = _build_create_asset_options(fm, location_wikilink)
|
create_opts = _build_create_asset_options(fm, location_wikilink)
|
||||||
sell_opts = _build_sell_options(fm)
|
sell_opts = _build_sell_options(fm)
|
||||||
|
|
||||||
# Expand Influence
|
# ----- ИСПРАВЛЕННАЯ ВСПОМОГАТЕЛЬНАЯ ФУНКЦИЯ -----
|
||||||
treasure = fm.get("treasure", 0)
|
# Возвращает пустую строку, если список опций пуст
|
||||||
expand_str = (
|
|
||||||
f" - Создать BoI в новой локации: 1 Treasure/HP нового BoI "
|
|
||||||
f"(текущий Treasure: {treasure}, максимальный BoI HP: {treasure})\n"
|
|
||||||
f" Потребует Cunning vs Cunning vs всех фракций с BoI в целевой локации"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _section(title: str, lines: list[str]) -> str:
|
def _section(title: str, lines: list[str]) -> str:
|
||||||
if not lines:
|
if not lines:
|
||||||
return f"\n**[{title}]** *(недоступно)*"
|
return "" # <-- теперь не выводится заголовок "недоступно"
|
||||||
return f"\n**[{title}]**\n" + "\n".join(lines)
|
return f"\n**[{title}]**\n" + "\n".join(lines)
|
||||||
|
|
||||||
|
# ----- Блок Expand Influence (теперь только если доступен) -----
|
||||||
|
treasure = fm.get("treasure", 0)
|
||||||
|
has_regular_asset_in_loc = any(
|
||||||
|
not a.get("is_base_of_influence") and normalize_wikilink(a.get("location", "")) == loc_norm
|
||||||
|
for a in fm.get("assets", [])
|
||||||
|
)
|
||||||
|
expand_section = ""
|
||||||
|
if has_regular_asset_in_loc:
|
||||||
|
expand_str = (
|
||||||
|
f" - Создать BoI в новой локации: 1 Treasure/HP нового BoI "
|
||||||
|
f"(текущий Treasure: {treasure}, максимальный BoI HP: {treasure})\n"
|
||||||
|
f" Потребует Cunning vs Cunning vs всех фракций с любым активом в целевой локации"
|
||||||
|
)
|
||||||
|
expand_section = f"\n**[EXPAND INFLUENCE]**\n{expand_str}"
|
||||||
|
|
||||||
|
# Собираем все секции, кроме EXPAND INFLUENCE (он уже отдельно)
|
||||||
|
actions_sections = (
|
||||||
|
_section("ATTACK", attack_opts) +
|
||||||
|
_section("CREATE ASSET", create_opts) +
|
||||||
|
_section("HIDE ASSET", hide_opts) +
|
||||||
|
_section("MOVE ASSET", move_opts) +
|
||||||
|
_section("REPAIR ASSET", repair_opts) +
|
||||||
|
expand_section +
|
||||||
|
_section("SELL ASSET", sell_opts)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not actions_sections.strip():
|
||||||
|
actions_sections = "\n*(нет доступных действий — фракция пропускает ход)*"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
f"### {idx}. {name} | Initiative: {initiative} | {state_label.upper()}\n"
|
f"### {idx}. {name} | Initiative: {initiative} | {state_label.upper()}\n"
|
||||||
f"**Статы:** {status_str}"
|
f"**Статы:** {status_str}"
|
||||||
|
|
@ -500,16 +524,87 @@ def _format_faction_section(
|
||||||
f"\n**Доход этого хода:** {income_str}{upkeep_str}"
|
f"\n**Доход этого хода:** {income_str}{upkeep_str}"
|
||||||
f"\n\n**Активы в локации:**\n{assets_str}"
|
f"\n\n**Активы в локации:**\n{assets_str}"
|
||||||
f"\n\n#### Доступные действия:"
|
f"\n\n#### Доступные действия:"
|
||||||
+ _section("ATTACK", attack_opts)
|
f"{actions_sections}"
|
||||||
+ _section("CREATE ASSET", create_opts)
|
|
||||||
+ _section("HIDE ASSET", hide_opts)
|
|
||||||
+ _section("MOVE ASSET", move_opts)
|
|
||||||
+ _section("REPAIR ASSET", repair_opts)
|
|
||||||
+ f"\n**[EXPAND INFLUENCE]**\n{expand_str}"
|
|
||||||
+ _section("SELL ASSET", sell_opts)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_startup_asset(
|
||||||
|
faction: dict,
|
||||||
|
factions_folder: str,
|
||||||
|
default_location: str = "[[Уотердип]]"
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Проверяет, есть ли у фракции активы (включая BoI). Если нет, и фракция новая
|
||||||
|
(нет last_turn_date и last_action), то добавляет самый дешёвый актив tier 1
|
||||||
|
в первую локацию из списка locations (или default_location) БЕСПЛАТНО.
|
||||||
|
"""
|
||||||
|
fm = faction.get("frontmatter", {})
|
||||||
|
# Пропускаем побеждённые или мёртвые
|
||||||
|
if fm.get("state") == "defeated" or fm.get("hp", 0) <= 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Если есть хоть один актив (включая BoI) — ничего не делаем
|
||||||
|
if fm.get("assets"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Если фракция уже действовала ранее — не даём халявный актив
|
||||||
|
if fm.get("last_turn_date") or fm.get("last_action"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Выбираем локацию
|
||||||
|
locations = fm.get("locations", [])
|
||||||
|
if not locations:
|
||||||
|
loc = default_location
|
||||||
|
fm.setdefault("locations", []).append(default_location)
|
||||||
|
else:
|
||||||
|
loc = locations[0]
|
||||||
|
|
||||||
|
# Определяем наивысший атрибут (приоритет: Cunning > Force > Wealth)
|
||||||
|
attrs = {
|
||||||
|
"Cunning": fm.get("cunning", 0),
|
||||||
|
"Force": fm.get("force", 0),
|
||||||
|
"Wealth": fm.get("wealth", 0),
|
||||||
|
}
|
||||||
|
priority = {"Cunning": 0, "Force": 1, "Wealth": 2}
|
||||||
|
sorted_attrs = sorted(attrs.items(), key=lambda x: (-x[1], priority[x[0]]))
|
||||||
|
best_attr = sorted_attrs[0][0]
|
||||||
|
|
||||||
|
from wwn_assets_data import get_assets_by_attribute, ALL_ASSETS
|
||||||
|
candidates = [a for a in get_assets_by_attribute(best_attr) if a["tier_required"] == 1]
|
||||||
|
if not candidates:
|
||||||
|
candidates = [a for a in ALL_ASSETS if a["tier_required"] == 1]
|
||||||
|
if not candidates:
|
||||||
|
return False
|
||||||
|
|
||||||
|
best_asset = min(candidates, key=lambda a: a["cost"])
|
||||||
|
|
||||||
|
# Создаём актив бесплатно (не тратим Treasure)
|
||||||
|
new_asset = {
|
||||||
|
"name": best_asset["name"],
|
||||||
|
"is_base_of_influence": False,
|
||||||
|
"attribute": best_asset["attribute"],
|
||||||
|
"tier_required": best_asset["tier_required"],
|
||||||
|
"cost": best_asset["cost"],
|
||||||
|
"hp": best_asset["hp_max"],
|
||||||
|
"hp_max": best_asset["hp_max"],
|
||||||
|
"location": loc,
|
||||||
|
"stealthed": False, # по умолчанию активы не скрыты, даже если имеют Subtle
|
||||||
|
"subtle": False,
|
||||||
|
}
|
||||||
|
fm.setdefault("assets", []).append(new_asset)
|
||||||
|
# Treasure не изменяется
|
||||||
|
|
||||||
|
# Сохраняем изменения в файл
|
||||||
|
file_path = faction.get("path")
|
||||||
|
if file_path:
|
||||||
|
body = faction.get("body", "")
|
||||||
|
success = write_faction(file_path, fm, body)
|
||||||
|
if success:
|
||||||
|
faction["frontmatter"] = fm
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Главная функция
|
# Главная функция
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -602,6 +697,9 @@ def propose_faction_turn(
|
||||||
faction["_income"] = income
|
faction["_income"] = income
|
||||||
faction["_upkeep"] = upkeep
|
faction["_upkeep"] = upkeep
|
||||||
|
|
||||||
|
# Добавляем бесплатный стартовый актив, если надо
|
||||||
|
_ensure_startup_asset(faction, factions_folder, location_wikilink)
|
||||||
|
|
||||||
# ── Сборка вывода ────────────────────────────────────────────────
|
# ── Сборка вывода ────────────────────────────────────────────────
|
||||||
header = (
|
header = (
|
||||||
f"# Ход фракций: {location_wikilink} | {game_date}\n\n"
|
f"# Ход фракций: {location_wikilink} | {game_date}\n\n"
|
||||||
|
|
|
||||||
|
|
@ -346,7 +346,7 @@ def _make_event_content(
|
||||||
def _resolve_attack(
|
def _resolve_attack(
|
||||||
action: dict,
|
action: dict,
|
||||||
faction_fm: dict,
|
faction_fm: dict,
|
||||||
all_factions_data: dict, # faction_file → (fm, body, path)
|
all_factions_data: dict,
|
||||||
factions_folder: str,
|
factions_folder: str,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
|
|
@ -355,8 +355,6 @@ def _resolve_attack(
|
||||||
Бросает 1d10+attr vs 1d10+attr с учётом faction tags.
|
Бросает 1d10+attr vs 1d10+attr с учётом faction tags.
|
||||||
Применяет урон к target_asset или BoI.
|
Применяет урон к target_asset или BoI.
|
||||||
При ударе по BoI — также наносит урон HP фракции.
|
При ударе по BoI — также наносит урон HP фракции.
|
||||||
|
|
||||||
Возвращает словарь с результатами для отчёта.
|
|
||||||
"""
|
"""
|
||||||
attacking_asset_name = action.get("attacking_asset", "")
|
attacking_asset_name = action.get("attacking_asset", "")
|
||||||
target_faction_file = action.get("target_faction", "")
|
target_faction_file = action.get("target_faction", "")
|
||||||
|
|
@ -377,6 +375,21 @@ def _resolve_attack(
|
||||||
result["error"] = f"Актив '{attacking_asset_name}' не найден в таблицах WWN"
|
result["error"] = f"Актив '{attacking_asset_name}' не найден в таблицах WWN"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
# ----- Проверка: атакующий актив должен находиться в той же локации, что и цель -----
|
||||||
|
own_asset_ref = _find_asset_in_faction(faction_fm, attacking_asset_name)
|
||||||
|
if not own_asset_ref:
|
||||||
|
result["error"] = f"Актив '{attacking_asset_name}' не найден у атакующей фракции"
|
||||||
|
return result
|
||||||
|
|
||||||
|
own_loc = normalize_wikilink(own_asset_ref.get("location", ""))
|
||||||
|
target_loc = normalize_wikilink(location) # цель должна быть в этой локации
|
||||||
|
if own_loc != target_loc:
|
||||||
|
result["error"] = (
|
||||||
|
f"Атакующий актив находится в {own_loc}, а цель указана в {target_loc}. "
|
||||||
|
"Атака возможна только в одной локации."
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
atk_attr = own_asset_def.get("attack_attribute", "Force")
|
atk_attr = own_asset_def.get("attack_attribute", "Force")
|
||||||
atk_vs = own_asset_def.get("attack_vs", "Force")
|
atk_vs = own_asset_def.get("attack_vs", "Force")
|
||||||
atk_dmg_notation = own_asset_def.get("attack_damage", "1d6")
|
atk_dmg_notation = own_asset_def.get("attack_damage", "1d6")
|
||||||
|
|
@ -594,7 +607,12 @@ def _resolve_create_asset(
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _resolve_hide_asset(action: dict, faction_fm: dict) -> dict:
|
def _resolve_hide_asset(
|
||||||
|
action: dict,
|
||||||
|
faction_fm: dict,
|
||||||
|
all_factions_data: dict,
|
||||||
|
own_faction_file: str,
|
||||||
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Выполняет действие Hide Asset по правилам WWN.
|
Выполняет действие Hide Asset по правилам WWN.
|
||||||
|
|
||||||
|
|
@ -602,6 +620,7 @@ def _resolve_hide_asset(action: dict, faction_fm: dict) -> dict:
|
||||||
нельзя скрывать в локации с вражеским BoI.
|
нельзя скрывать в локации с вражеским BoI.
|
||||||
"""
|
"""
|
||||||
asset_name = action.get("asset_name", "")
|
asset_name = action.get("asset_name", "")
|
||||||
|
location = action.get("location", "")
|
||||||
result = {"action": "Hide Asset", "asset": asset_name}
|
result = {"action": "Hide Asset", "asset": asset_name}
|
||||||
|
|
||||||
if faction_fm.get("cunning", 0) < 3:
|
if faction_fm.get("cunning", 0) < 3:
|
||||||
|
|
@ -617,6 +636,23 @@ def _resolve_hide_asset(action: dict, faction_fm: dict) -> dict:
|
||||||
result["error"] = f"Актив '{asset_name}' не найден"
|
result["error"] = f"Актив '{asset_name}' не найден"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
# ----- Проверка на вражеский BoI в той же локации -----
|
||||||
|
loc_norm = normalize_wikilink(location)
|
||||||
|
hostile_boi = any(
|
||||||
|
normalize_wikilink(a.get("location", "")) == loc_norm
|
||||||
|
and a.get("is_base_of_influence")
|
||||||
|
for tf_file, (tf_fm, _, _) in all_factions_data.items()
|
||||||
|
if tf_file != own_faction_file
|
||||||
|
for a in tf_fm.get("assets", [])
|
||||||
|
)
|
||||||
|
if hostile_boi:
|
||||||
|
result["error"] = (
|
||||||
|
f"В локации {location} присутствует вражеский BoI — скрытие невозможно "
|
||||||
|
"(правило стр. 326)."
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Применяем скрытие
|
||||||
asset["stealthed"] = True
|
asset["stealthed"] = True
|
||||||
faction_fm["treasure"] = faction_fm.get("treasure", 0) - 2
|
faction_fm["treasure"] = faction_fm.get("treasure", 0) - 2
|
||||||
result["treasure_spent"] = 2
|
result["treasure_spent"] = 2
|
||||||
|
|
@ -654,24 +690,32 @@ def _resolve_move_asset(action: dict, faction_fm: dict) -> dict:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _resolve_repair_asset(action: dict, faction_fm: dict) -> dict:
|
def _resolve_repair_asset(
|
||||||
|
action: dict,
|
||||||
|
faction_fm: dict,
|
||||||
|
repair_application_number: int, # номер применения ремонта для данного целевого объекта (актива или faction)
|
||||||
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Выполняет действие Repair Asset по правилам WWN.
|
Выполняет действие Repair Asset по правилам WWN.
|
||||||
|
|
||||||
repair_asset = "faction" → ремонтирует HP самой фракции.
|
repair_asset = "faction" → ремонтирует HP самой фракции.
|
||||||
repair_asset = имя актива → ремонтирует актив.
|
repair_asset = имя актива → ремонтирует актив.
|
||||||
|
|
||||||
|
Параметр repair_application_number — номер применения ремонта к этому объекту в текущем ходу
|
||||||
|
(1 – первое применение, 2 – второе и т.д.).
|
||||||
|
Стоимость = номер применения (1 Treasure за первое, 2 за второе, ...).
|
||||||
"""
|
"""
|
||||||
repair_target = action.get("repair_asset", "")
|
repair_target = action.get("repair_asset", "")
|
||||||
result = {"action": "Repair Asset", "target": repair_target}
|
result = {"action": "Repair Asset", "target": repair_target}
|
||||||
|
|
||||||
treasure = faction_fm.get("treasure", 0)
|
treasure = faction_fm.get("treasure", 0)
|
||||||
|
cost = repair_application_number # стоимость равна номеру применения
|
||||||
|
|
||||||
if repair_target == "faction":
|
if repair_target == "faction":
|
||||||
if treasure < 1:
|
if treasure < cost:
|
||||||
result["error"] = "Недостаточно Treasure для ремонта фракции"
|
result["error"] = f"Недостаточно Treasure (нужно {cost}, есть {treasure})"
|
||||||
return result
|
return result
|
||||||
repair_info = calc_faction_repair_heal(faction_fm)
|
repair_info = calc_faction_repair_heal(faction_fm)
|
||||||
cost = repair_info["cost"]
|
|
||||||
heal = repair_info["heal"]
|
heal = repair_info["heal"]
|
||||||
old_hp = faction_fm.get("hp", 0)
|
old_hp = faction_fm.get("hp", 0)
|
||||||
hp_max = faction_fm.get("hp_max", 1)
|
hp_max = faction_fm.get("hp_max", 1)
|
||||||
|
|
@ -686,15 +730,15 @@ def _resolve_repair_asset(action: dict, faction_fm: dict) -> dict:
|
||||||
result["error"] = f"Актив '{repair_target}' не найден"
|
result["error"] = f"Актив '{repair_target}' не найден"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
attr = asset.get("attribute", "Force")
|
|
||||||
repair_info = calc_repair_heal(faction_fm, attr, 1)
|
|
||||||
cost = repair_info["cost"]
|
|
||||||
heal = repair_info["heal"]
|
|
||||||
|
|
||||||
if treasure < cost:
|
if treasure < cost:
|
||||||
result["error"] = f"Недостаточно Treasure (нужно {cost}, есть {treasure})"
|
result["error"] = f"Недостаточно Treasure (нужно {cost}, есть {treasure})"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
attr = asset.get("attribute", "Force")
|
||||||
|
# heal вычисляется по формуле из WWN: ceil(attr_value / 2)
|
||||||
|
attr_value = faction_fm.get(attr.lower(), 0)
|
||||||
|
heal = math.ceil(attr_value / 2)
|
||||||
|
|
||||||
old_hp = asset.get("hp", 0)
|
old_hp = asset.get("hp", 0)
|
||||||
hp_max = asset.get("hp_max", 1)
|
hp_max = asset.get("hp_max", 1)
|
||||||
asset["hp"] = min(hp_max, old_hp + heal)
|
asset["hp"] = min(hp_max, old_hp + heal)
|
||||||
|
|
@ -716,7 +760,7 @@ def _resolve_expand_influence(
|
||||||
Выполняет действие Expand Influence по правилам WWN.
|
Выполняет действие Expand Influence по правилам WWN.
|
||||||
|
|
||||||
Создаёт новый BoI в expand_location за expand_hp Treasure.
|
Создаёт новый BoI в expand_location за expand_hp Treasure.
|
||||||
Затем выполняет Cunning vs Cunning против каждой фракции с BoI в локации.
|
Затем выполняет Cunning vs Cunning против каждой фракции с любым активом в локации.
|
||||||
При проигрыше — враг получает право на немедленную атаку (помечается в отчёте).
|
При проигрыше — враг получает право на немедленную атаку (помечается в отчёте).
|
||||||
"""
|
"""
|
||||||
expand_loc = action.get("expand_location", action.get("location", ""))
|
expand_loc = action.get("expand_location", action.get("location", ""))
|
||||||
|
|
@ -727,32 +771,28 @@ def _resolve_expand_influence(
|
||||||
"boi_hp": desired_hp,
|
"boi_hp": desired_hp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Получаем текущий список (если его нет, создаем пустой)
|
|
||||||
if "locations" not in faction_fm:
|
|
||||||
faction_fm["locations"] = []
|
|
||||||
|
|
||||||
# Проверяем, нет ли уже этой локации в списке (нормализованно)
|
|
||||||
loc_norm = normalize_wikilink(expand_loc)
|
loc_norm = normalize_wikilink(expand_loc)
|
||||||
existing_locs_norm = [normalize_wikilink(l) for l in faction_fm["locations"]]
|
|
||||||
|
|
||||||
if loc_norm not in existing_locs_norm:
|
|
||||||
# Добавляем именно в том виде, в котором пришло (например, "[[Neverwinter]]")
|
|
||||||
faction_fm["locations"].append(expand_loc)
|
|
||||||
|
|
||||||
|
# ----- Проверка: у создающей фракции должен быть хотя бы один актив в локации (не BoI) -----
|
||||||
|
has_asset = any(
|
||||||
|
not a.get("is_base_of_influence") and normalize_wikilink(a.get("location", "")) == loc_norm
|
||||||
|
for a in faction_fm.get("assets", [])
|
||||||
|
)
|
||||||
|
if not has_asset:
|
||||||
|
result["error"] = (
|
||||||
|
f"Нет активов (кроме BoI) в локации {expand_loc} для создания нового BoI. "
|
||||||
|
"Требуется хотя бы один обычный актив."
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
# ----- Проверка Treasure -----
|
||||||
cost = desired_hp # 1 Treasure per HP
|
cost = desired_hp # 1 Treasure per HP
|
||||||
treasure = faction_fm.get("treasure", 0)
|
treasure = faction_fm.get("treasure", 0)
|
||||||
|
|
||||||
if treasure < cost:
|
if treasure < cost:
|
||||||
# Если денег не хватило, удаляем локацию обратно (откат)
|
|
||||||
if expand_loc in faction_fm["locations"]:
|
|
||||||
faction_fm["locations"].remove(expand_loc)
|
|
||||||
result["error"] = f"Недостаточно Treasure (нужно {cost}, есть {treasure})"
|
result["error"] = f"Недостаточно Treasure (нужно {cost}, есть {treasure})"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Создаём BoI
|
# ----- Создаём BoI -----
|
||||||
loc_norm = normalize_wikilink(expand_loc)
|
|
||||||
boi_name = f"Base of Influence — {expand_loc.strip('[]')}"
|
boi_name = f"Base of Influence — {expand_loc.strip('[]')}"
|
||||||
new_boi = {
|
new_boi = {
|
||||||
"name": boi_name,
|
"name": boi_name,
|
||||||
|
|
@ -770,17 +810,28 @@ def _resolve_expand_influence(
|
||||||
result["treasure_after"] = faction_fm["treasure"]
|
result["treasure_after"] = faction_fm["treasure"]
|
||||||
result["boi_created"] = boi_name
|
result["boi_created"] = boi_name
|
||||||
|
|
||||||
# Cunning checks против враждебных фракций в локации
|
# ----- Добавляем локацию в список regions (если ещё нет) -----
|
||||||
|
if "locations" not in faction_fm:
|
||||||
|
faction_fm["locations"] = []
|
||||||
|
existing_locs_norm = [normalize_wikilink(l) for l in faction_fm["locations"]]
|
||||||
|
if loc_norm not in existing_locs_norm:
|
||||||
|
faction_fm["locations"].append(expand_loc)
|
||||||
|
|
||||||
|
# ----- Cunning checks против ВСЕХ фракций, у которых есть ЛЮБОЙ актив в локации -----
|
||||||
own_cunning = faction_fm.get("cunning", 0)
|
own_cunning = faction_fm.get("cunning", 0)
|
||||||
contesting_checks = []
|
contesting_checks = []
|
||||||
|
|
||||||
for tf_file, (tf_fm, _tf_body, _tf_path) in all_factions_data.items():
|
for tf_file, (tf_fm, _tf_body, _tf_path) in all_factions_data.items():
|
||||||
# Проверяем есть ли у них BoI в expand_loc
|
# Пропускаем себя
|
||||||
tf_has_boi = any(
|
if tf_file == action.get("faction_file", ""):
|
||||||
a.get("is_base_of_influence") and normalize_wikilink(a.get("location", "")) == loc_norm
|
continue
|
||||||
|
|
||||||
|
# Проверяем, есть ли у этой фракции хотя бы один актив (любой) в локации
|
||||||
|
tf_has_asset = any(
|
||||||
|
normalize_wikilink(a.get("location", "")) == loc_norm
|
||||||
for a in tf_fm.get("assets", [])
|
for a in tf_fm.get("assets", [])
|
||||||
)
|
)
|
||||||
if not tf_has_boi:
|
if not tf_has_asset:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tf_cunning = tf_fm.get("cunning", 0)
|
tf_cunning = tf_fm.get("cunning", 0)
|
||||||
|
|
@ -952,6 +1003,9 @@ def resolve_faction_turn(
|
||||||
action_results: list[dict] = []
|
action_results: list[dict] = []
|
||||||
event_files_created: list[str] = []
|
event_files_created: list[str] = []
|
||||||
|
|
||||||
|
# ---- В начале цикла обработки действий инициализируем счётчики ремонта ----
|
||||||
|
repair_counters = {} # ключ: (faction_file, target_name) -> количество применений в этом ходу
|
||||||
|
|
||||||
for action in actions:
|
for action in actions:
|
||||||
faction_file = action.get("faction_file", "")
|
faction_file = action.get("faction_file", "")
|
||||||
action_type = action.get("action_type", "")
|
action_type = action.get("action_type", "")
|
||||||
|
|
@ -968,21 +1022,26 @@ def resolve_faction_turn(
|
||||||
fm, body, path = all_factions_data[faction_file]
|
fm, body, path = all_factions_data[faction_file]
|
||||||
result = {"faction": faction_file}
|
result = {"faction": faction_file}
|
||||||
|
|
||||||
if action_type == "Attack":
|
# ----- Для Repair Asset обновляем счётчик -----
|
||||||
|
if action_type == "Repair Asset":
|
||||||
|
repair_target = action.get("repair_asset", "")
|
||||||
|
key = (faction_file, repair_target)
|
||||||
|
repair_counters[key] = repair_counters.get(key, 0) + 1
|
||||||
|
repair_application_number = repair_counters[key]
|
||||||
|
result.update(_resolve_repair_asset(action, fm, repair_application_number))
|
||||||
|
# ----- Остальные действия без изменений (кроме Hide Asset и Attack, которые теперь требуют дополнительные параметры) -----
|
||||||
|
elif action_type == "Attack":
|
||||||
result.update(_resolve_attack(action, fm, all_factions_data, factions_folder))
|
result.update(_resolve_attack(action, fm, all_factions_data, factions_folder))
|
||||||
|
|
||||||
elif action_type == "Create Asset":
|
elif action_type == "Create Asset":
|
||||||
result.update(_resolve_create_asset(action, fm, location))
|
result.update(_resolve_create_asset(action, fm, location))
|
||||||
|
|
||||||
elif action_type == "Hide Asset":
|
elif action_type == "Hide Asset":
|
||||||
result.update(_resolve_hide_asset(action, fm))
|
result.update(_resolve_hide_asset(action, fm, all_factions_data, faction_file))
|
||||||
|
|
||||||
elif action_type == "Move Asset":
|
elif action_type == "Move Asset":
|
||||||
result.update(_resolve_move_asset(action, fm))
|
result.update(_resolve_move_asset(action, fm))
|
||||||
|
|
||||||
elif action_type == "Repair Asset":
|
|
||||||
result.update(_resolve_repair_asset(action, fm))
|
|
||||||
|
|
||||||
elif action_type == "Expand Influence":
|
elif action_type == "Expand Influence":
|
||||||
result.update(_resolve_expand_influence(action, fm, all_factions_data))
|
result.update(_resolve_expand_influence(action, fm, all_factions_data))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user