""" wwn_assets_data.py — полный справочник активов системы фракций WWN (Worlds Without Number). Содержит три таблицы активов из книги: CUNNING_ASSETS — активы на основе Cunning (шпионаж, интриги, подкуп) FORCE_ASSETS — активы на основе Force (военная сила, солдаты, оружие) WEALTH_ASSETS — активы на основе Wealth (торговля, деньги, экономика) Каждый актив — словарь со следующими полями: name (str) — уникальное имя актива attribute (str) — "Cunning" | "Force" | "Wealth" tier_required (int) — минимальное значение атрибута для покупки (1–8) cost (int) — стоимость покупки в Treasure hp_max (int) — максимальные HP актива magic_required (str) — "None" | "Low" | "Medium" | "High" attack_attribute (str|None) — атрибут атаки ("Cunning"|"Force"|"Wealth"|None) attack_vs (str|None) — атрибут защиты цели ("Cunning"|"Force"|"Wealth"|None) attack_damage (str|None) — нотация урона ("1d6", "2d8+2") или "Special" или None counterattack (str|None) — нотация урона при контратаке или None qualities (list[str]) — список качеств: "Subtle", "Action", "Special" description (str) — краткое описание эффекта актива special_key (str|None) — ключ специального эффекта для wwn_mechanics.py или None Вспомогательная функция: get_asset(name) — найти актив по имени get_assets_by_attribute(attr) — все активы для атрибута get_available_assets(faction_data) — активы доступные фракции к покупке """ from typing import Optional # --------------------------------------------------------------------------- # Cunning Assets (стр. 328–329 WWN) # --------------------------------------------------------------------------- CUNNING_ASSETS: list[dict] = [ # Tier 1 { "name": "Informers", "attribute": "Cunning", "tier_required": 1, "cost": 2, "hp_max": 3, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "Special", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "Spend 1 Treasure as free action: Cunning vs Cunning against a chosen faction. " "On success, all Stealthed assets of that faction within one move are revealed." ), "special_key": "informers_reveal", }, { "name": "Petty Seers", "attribute": "Cunning", "tier_required": 1, "cost": 2, "hp_max": 2, "magic_required": "Medium", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d6", "qualities": ["Subtle"], "description": "Fortune-tellers and minor oracles; no attack, strong counterattack.", "special_key": None, }, { "name": "Smugglers", "attribute": "Cunning", "tier_required": 1, "cost": 2, "hp_max": 4, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Wealth", "attack_damage": "1d4", "counterattack": None, "qualities": ["Subtle", "Action"], "description": ( "Free action once per turn: move any allied Wealth or Cunning asset " "in same location to destination within movement range, bypassing restrictions." ), "special_key": "smugglers_move", }, { "name": "Useful Idiots", "attribute": "Cunning", "tier_required": 1, "cost": 1, "hp_max": 2, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "If an allied asset within one move is struck, sacrifice Useful Idiots " "to negate the attack. Only one band can be sacrificed per turn." ), "special_key": "useful_idiots_sacrifice", }, # Tier 2 { "name": "Blackmail", "attribute": "Cunning", "tier_required": 2, "cost": 4, "hp_max": 4, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "1d4", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "While present, hostile factions can't roll more than one die during Attacks " "made by or against them here, even with tags or assets granting bonus dice." ), "special_key": "blackmail_suppress_dice", }, { "name": "Dancing Girls", "attribute": "Cunning", "tier_required": 2, "cost": 4, "hp_max": 3, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Wealth", "attack_damage": "2d4", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "Immune to Attack or Counterattack damage from Force assets, " "but cannot defend against Force asset attacks." ), "special_key": "dancing_girls_force_immune", }, { "name": "Hired Friends", "attribute": "Cunning", "tier_required": 2, "cost": 4, "hp_max": 4, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "1d6", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "Free action once per turn: spend 1 Treasure to grant a Wealth asset " "within one move the Subtle quality until Hired Friends move or are destroyed." ), "special_key": "hired_friends_grant_subtle", }, { "name": "Saboteurs", "attribute": "Cunning", "tier_required": 2, "cost": 5, "hp_max": 6, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Wealth", "attack_damage": "2d4", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "An asset attacked by Saboteurs can't use any free action abilities " "next turn, regardless of attack success." ), "special_key": "saboteurs_suppress_actions", }, # Tier 3 { "name": "Bewitching Charmer", "attribute": "Cunning", "tier_required": 3, "cost": 6, "hp_max": 4, "magic_required": "Low", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "Special", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "On successful Attack: targeted asset cannot leave same location " "until Charmer moves or is destroyed. Immune to Counterattack." ), "special_key": "charmer_pin", }, { "name": "Covert Transport", "attribute": "Cunning", "tier_required": 3, "cost": 8, "hp_max": 4, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "Free action once per turn: pay 1 Treasure to move any Cunning or Wealth " "asset at same location; transported asset gains Stealth until used." ), "special_key": "covert_transport_move", }, { "name": "Occult Infiltrators", "attribute": "Cunning", "tier_required": 3, "cost": 6, "hp_max": 4, "magic_required": "Medium", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "2d6", "counterattack": None, "qualities": ["Subtle", "Special"], "description": "Magically gifted spies; always begin play with Stealth quality.", "special_key": "occult_infiltrators_start_stealthed", }, { "name": "Spymaster", "attribute": "Cunning", "tier_required": 3, "cost": 8, "hp_max": 4, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "1d6", "counterattack": "2d6", "qualities": ["Subtle"], "description": "Veteran counterintelligence operative; strong counterattack.", "special_key": None, }, # Tier 4 { "name": "Court Patronage", "attribute": "Cunning", "tier_required": 4, "cost": 8, "hp_max": 8, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "1d6", "counterattack": "1d6", "qualities": ["Subtle", "Special"], "description": "Automatically grants 1 Treasure to owning faction each turn.", "special_key": "court_patronage_income", }, { "name": "Idealistic Thugs", "attribute": "Cunning", "tier_required": 4, "cost": 8, "hp_max": 12, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Force", "attack_damage": "1d6", "counterattack": "1d6", "qualities": ["Subtle"], "description": "Easily-manipulated hotheads; high HP for a Cunning asset.", "special_key": None, }, { "name": "Seditionists", "attribute": "Cunning", "tier_required": 4, "cost": 12, "hp_max": 8, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": "Special", "counterattack": None, "qualities": ["Subtle"], "description": ( "Instead of Attack: spend 1d4 Treasure to attach to a hostile asset in same location. " "Until destroyed or moved, that asset cannot be used and grants no benefits." ), "special_key": "seditionists_infest", }, { "name": "Vigilant Agents", "attribute": "Cunning", "tier_required": 4, "cost": 12, "hp_max": 8, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d4", "qualities": ["Subtle", "Special"], "description": ( "When a Stealthed hostile asset moves into location within one move: " "Cunning vs Cunning. On success, intruder loses Stealth after completing move." ), "special_key": "vigilant_agents_detect", }, # Tier 5 { "name": "Cryptomancers", "attribute": "Cunning", "tier_required": 5, "cost": 14, "hp_max": 6, "magic_required": "Low", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "Special", "counterattack": None, "qualities": ["Subtle"], "description": ( "Instead of Attack: Cunning vs Cunning against specific hostile asset within one move. " "On success, target cannot act next turn. On failure, no counterattack." ), "special_key": "cryptomancers_disable", }, { "name": "Organization Moles", "attribute": "Cunning", "tier_required": 5, "cost": 8, "hp_max": 10, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "2d6", "counterattack": None, "qualities": ["Subtle"], "description": "Sleeper agents deep in hostile organizations; no counterattack damage.", "special_key": None, }, { "name": "Shapeshifters", "attribute": "Cunning", "tier_required": 5, "cost": 14, "hp_max": 8, "magic_required": "Medium", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "2d6", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "Free action once per turn: spend 1 Treasure to gain Stealth quality." ), "special_key": "shapeshifters_self_stealth", }, # Tier 6 { "name": "Interrupted Logistics", "attribute": "Cunning", "tier_required": 6, "cost": 20, "hp_max": 10, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "Non-Stealthed hostile units cannot enter same location without " "paying 1d4 Treasure and waiting one turn." ), "special_key": "interrupted_logistics_toll", }, { "name": "Prophet", "attribute": "Cunning", "tier_required": 6, "cost": 20, "hp_max": 10, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "2d8", "counterattack": "1d8", "qualities": ["Subtle"], "description": "Charismatic prophet or rebel leader; powerful attack and counterattack.", "special_key": None, }, { "name": "Underground Roads", "attribute": "Cunning", "tier_required": 6, "cost": 18, "hp_max": 15, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "Free action: pay 1 Treasure to move any friendly asset from within " "one move of Roads to another location within one move of Roads." ), "special_key": "underground_roads_teleport", }, # Tier 7 { "name": "Expert Treachery", "attribute": "Cunning", "tier_required": 7, "cost": 10, "hp_max": 5, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "Special", "counterattack": None, "qualities": ["Subtle"], "description": ( "On successful Attack: this asset is lost, owning faction gains 5 Treasure, " "and targeted asset switches sides (even if new owner lacks required attributes)." ), "special_key": "expert_treachery_convert", }, { "name": "Mindbenders", "attribute": "Cunning", "tier_required": 7, "cost": 20, "hp_max": 10, "magic_required": "Medium", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "2d8", "qualities": ["Subtle", "Special"], "description": ( "Once per turn: force a rival faction to reroll any check they just made, " "take whichever result Mindbenders prefer. A faction can only be affected once per turn." ), "special_key": "mindbenders_reroll", }, { "name": "Popular Movement", "attribute": "Cunning", "tier_required": 7, "cost": 25, "hp_max": 16, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "2d6", "counterattack": "1d6", "qualities": ["Subtle", "Special"], "description": ( "Any friendly asset may move into same location as Popular Movement, " "even if normally forbidden and lacking Subtle. If Popular Movement moves or is " "destroyed, such assets must also leave or suffer usual consequences." ), "special_key": "popular_movement_sanctuary", }, # Tier 8 { "name": "Just As Planned", "attribute": "Cunning", "tier_required": 8, "cost": 40, "hp_max": 15, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d10", "qualities": ["Subtle", "Special"], "description": ( "When faction's assets make a Cunning roll: may reroll a failed check " "at cost of 1d6 damage to Just As Planned. No range limit. May be used repeatedly." ), "special_key": "just_as_planned_reroll", }, { "name": "Omniscient Seers", "attribute": "Cunning", "tier_required": 8, "cost": 30, "hp_max": 10, "magic_required": "High", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d8", "qualities": ["Subtle", "Special"], "description": ( "At start of turn: each hostile Stealthed asset within one move must succeed " "Cunning vs Cunning or lose Stealth. Also grants extra Cunning die for all rolls " "within one move." ), "special_key": "omniscient_seers_mass_detect", }, ] # --------------------------------------------------------------------------- # Force Assets (стр. 330–331 WWN) # --------------------------------------------------------------------------- FORCE_ASSETS: list[dict] = [ # Tier 1 { "name": "Fearful Intimidation", "attribute": "Force", "tier_required": 1, "cost": 2, "hp_max": 4, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d4", "qualities": [], "description": "Intimidation of locals; no attack, counterattack only.", "special_key": None, }, { "name": "Local Guard", "attribute": "Force", "tier_required": 1, "cost": 3, "hp_max": 4, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "1d3+1", "counterattack": "1d4+1", "qualities": [], "description": "Citizen night watch and local guard units; better at defending.", "special_key": None, }, { "name": "Summoned Hunter", "attribute": "Force", "tier_required": 1, "cost": 4, "hp_max": 4, "magic_required": "Medium", "attack_attribute": "Cunning", "attack_vs": "Force", "attack_damage": "1d6", "counterattack": None, "qualities": ["Subtle"], "description": "Magically summoned or bound assassin-beast; begins play Stealthed.", "special_key": "summoned_hunter_start_stealthed", }, { "name": "Thugs", "attribute": "Force", "tier_required": 1, "cost": 2, "hp_max": 1, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Cunning", "attack_damage": "1d6", "counterattack": None, "qualities": ["Subtle"], "description": "Gutter ruffians and common kneebreakers; very fragile.", "special_key": None, }, # Tier 2 { "name": "Guerrilla Populace", "attribute": "Force", "tier_required": 2, "cost": 6, "hp_max": 4, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "1d4+1", "counterattack": None, "qualities": [], "description": "Locals trained in guerrilla warfare; good against supply lines.", "special_key": None, }, { "name": "Military Transport", "attribute": "Force", "tier_required": 2, "cost": 4, "hp_max": 6, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Action"], "description": ( "Free action once per turn: bring an allied asset to its location or " "move an allied asset from its location, within one move. Can chain movement." ), "special_key": "military_transport_move", }, { "name": "Reserve Corps", "attribute": "Force", "tier_required": 2, "cost": 4, "hp_max": 4, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "1d6", "counterattack": "1d6", "qualities": [], "description": "Retired military personnel spread through area as colonists.", "special_key": None, }, { "name": "Scouts", "attribute": "Force", "tier_required": 2, "cost": 5, "hp_max": 5, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "2d4", "counterattack": "1d4+1", "qualities": ["Subtle"], "description": "Long-range scouts and reconnaissance experts.", "special_key": None, }, # Tier 3 { "name": "Enchanted Elites", "attribute": "Force", "tier_required": 3, "cost": 8, "hp_max": 6, "magic_required": "Medium", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "1d10", "counterattack": "1d6", "qualities": ["Subtle"], "description": "Magically armed and blessed warriors; strong attack.", "special_key": None, }, { "name": "Infantry", "attribute": "Force", "tier_required": 3, "cost": 6, "hp_max": 6, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "1d8", "counterattack": "1d6", "qualities": [], "description": "Common foot soldiers; reliable and numerous.", "special_key": None, }, { "name": "Temple Fanatics", "attribute": "Force", "tier_required": 3, "cost": 4, "hp_max": 6, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "2d6", "counterattack": "2d6", "qualities": ["Special"], "description": ( "After every time they defend or successfully attack, take 1d4 damage. " "High damage both ways." ), "special_key": "temple_fanatics_self_damage", }, { "name": "Witch Hunters", "attribute": "Force", "tier_required": 3, "cost": 6, "hp_max": 4, "magic_required": "Low", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "1d4+1", "counterattack": "1d6", "qualities": [], "description": "Trained to find traitors, spies and hostile magic.", "special_key": None, }, # Tier 4 { "name": "Cavalry", "attribute": "Force", "tier_required": 4, "cost": 8, "hp_max": 12, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "2d6", "counterattack": "1d4", "qualities": [], "description": "Mounted troops; weak on defense, powerful charges.", "special_key": None, }, { "name": "Military Roads", "attribute": "Force", "tier_required": 4, "cost": 10, "hp_max": 10, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Action"], "description": ( "Logistical road network. Once per turn: move any one asset from any location " "within reach to any other within reach for 1 Treasure." ), "special_key": "military_roads_network", }, { "name": "Vanguard Unit", "attribute": "Force", "tier_required": 4, "cost": 10, "hp_max": 10, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d6", "qualities": ["Action"], "description": ( "During Relocate Asset action: can move itself and allied units at same location " "to any location within range, even normally-prohibited ones." ), "special_key": "vanguard_forced_march", }, { "name": "War Fleet", "attribute": "Force", "tier_required": 4, "cost": 12, "hp_max": 8, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "2d6", "counterattack": "1d8", "qualities": ["Action"], "description": ( "Can only attack assets/locations reachable by waterways. " "Once per turn: freely relocate to any coastal area within movement range." ), "special_key": "war_fleet_coastal", }, # Tier 5 { "name": "Demonic Slayer", "attribute": "Force", "tier_required": 5, "cost": 12, "hp_max": 4, "magic_required": "High", "attack_attribute": "Cunning", "attack_vs": "Cunning", "attack_damage": "2d6+2", "counterattack": None, "qualities": ["Subtle", "Special"], "description": "Summoned inhuman assassin-beast; enters play Stealthed.", "special_key": "demonic_slayer_start_stealthed", }, { "name": "Magical Logistics", "attribute": "Force", "tier_required": 5, "cost": 14, "hp_max": 6, "magic_required": "Medium", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Special"], "description": ( "Once per faction turn as free action: repair 2 hit points of damage " "to an allied Force asset." ), "special_key": "magical_logistics_repair", }, { "name": "Siege Experts", "attribute": "Force", "tier_required": 5, "cost": 10, "hp_max": 8, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Wealth", "attack_damage": "1d6", "counterattack": "1d6", "qualities": [], "description": ( "On successful Attack: target's owner loses 1d4 Treasure and this faction gains it." ), "special_key": "siege_experts_loot", }, # Tier 6 { "name": "Fortification Program", "attribute": "Force", "tier_required": 6, "cost": 20, "hp_max": 18, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "2d6", "qualities": ["Action"], "description": ( "Once per turn: when enemy makes Attack targeting faction's Force rating, " "can use Fortification Program to defend if within one move of attack." ), "special_key": "fortification_intercept", }, { "name": "Knights", "attribute": "Force", "tier_required": 6, "cost": 18, "hp_max": 16, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "2d8", "counterattack": "2d6", "qualities": [], "description": "Elite warriors of considerable personal prowess.", "special_key": None, }, { "name": "War Machines", "attribute": "Force", "tier_required": 6, "cost": 25, "hp_max": 14, "magic_required": "Medium", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "2d10+4", "counterattack": "1d10", "qualities": [], "description": "Mobile war machines driven by beasts or magical motive power.", "special_key": None, }, # Tier 7 { "name": "Brilliant General", "attribute": "Force", "tier_required": 7, "cost": 25, "hp_max": 8, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Force", "attack_damage": "1d8", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "When Brilliant General or any allied Force asset in same location " "Attacks or defends, it rolls an extra die." ), "special_key": "brilliant_general_extra_die", }, { "name": "Purity Rites", "attribute": "Force", "tier_required": 7, "cost": 20, "hp_max": 10, "magic_required": "Low", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "2d8+2", "qualities": ["Special"], "description": ( "Can only defend against attacks targeting faction's Cunning. " "Allows faction to roll extra die to defend." ), "special_key": "purity_rites_cunning_defense", }, { "name": "Warshaped", "attribute": "Force", "tier_required": 7, "cost": 30, "hp_max": 16, "magic_required": "High", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "2d8+2", "counterattack": "2d8", "qualities": ["Subtle"], "description": "Magically altered creatures designed for warfare; elusive.", "special_key": None, }, # Tier 8 { "name": "Apocalypse Engine", "attribute": "Force", "tier_required": 8, "cost": 35, "hp_max": 20, "magic_required": "Medium", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "3d10+4", "counterattack": None, "qualities": [], "description": "Ancient super-weapon; devastating attack, no counterattack.", "special_key": None, }, { "name": "Invincible Legion", "attribute": "Force", "tier_required": 8, "cost": 40, "hp_max": 30, "magic_required": "None", "attack_attribute": "Force", "attack_vs": "Force", "attack_damage": "2d10+4", "counterattack": "2d10+4", "qualities": ["Special"], "description": ( "During Relocate Asset: can move to locations normally prohibited for military, " "as if it had Subtle. It is not actually subtle." ), "special_key": "invincible_legion_forced_entry", }, ] # --------------------------------------------------------------------------- # Wealth Assets (стр. 332–333 WWN) # --------------------------------------------------------------------------- WEALTH_ASSETS: list[dict] = [ # Tier 1 { "name": "Armed Guards", "attribute": "Wealth", "tier_required": 1, "cost": 1, "hp_max": 3, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Force", "attack_damage": "1d3", "counterattack": "1d4", "qualities": [], "description": "Hired caravan guards and bodyguards.", "special_key": None, }, { "name": "Cooperative Businesses", "attribute": "Wealth", "tier_required": 1, "cost": 1, "hp_max": 2, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "1d4-1", "counterattack": None, "qualities": ["Subtle", "Special"], "description": ( "If any other faction attempts to create an asset in same location, " "cost increases by 1 Treasure. Penalty stacks." ), "special_key": "cooperative_businesses_price_hike", }, { "name": "Farmers", "attribute": "Wealth", "tier_required": 1, "cost": 2, "hp_max": 4, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d4", "qualities": ["Action"], "description": ( "Once per turn as free action: roll 1d6; on 5+ gain 1 Treasure." ), "special_key": "farmers_income", }, { "name": "Front Merchant", "attribute": "Wealth", "tier_required": 1, "cost": 2, "hp_max": 3, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "1d4", "counterattack": "1d4-1", "qualities": ["Subtle"], "description": ( "On successful Attack: target faction loses 1 Treasure (if any), " "Front Merchant's owner gains it. Once per turn." ), "special_key": "front_merchant_steal", }, # Tier 2 { "name": "Caravan", "attribute": "Wealth", "tier_required": 2, "cost": 5, "hp_max": 4, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "1d4", "counterattack": None, "qualities": ["Action"], "description": ( "Free action once per turn: spend 1 Treasure to move itself and " "one other asset in same place to new location within one move." ), "special_key": "caravan_move", }, { "name": "Dragomans", "attribute": "Wealth", "tier_required": 2, "cost": 4, "hp_max": 4, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d4", "qualities": ["Subtle", "Special"], "description": ( "Faction taking Expand Influence in same location rolls extra die on all checks. " "Free action once per turn: this asset can move." ), "special_key": "dragomans_expand_bonus", }, { "name": "Pleaders", "attribute": "Wealth", "tier_required": 2, "cost": 6, "hp_max": 4, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Wealth", "attack_damage": "2d4", "counterattack": "1d6", "qualities": ["Special"], "description": ( "Can turn local laws against enemies. Cannot Attack or inflict Counterattack " "damage on Force assets." ), "special_key": "pleaders_no_force", }, { "name": "Worker Mob", "attribute": "Wealth", "tier_required": 2, "cost": 4, "hp_max": 6, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Force", "attack_damage": "1d4+1", "counterattack": "1d4", "qualities": [], "description": "Organized brutal laborers for disciplining enemies.", "special_key": None, }, # Tier 3 { "name": "Ancient Mechanisms", "attribute": "Wealth", "tier_required": 3, "cost": 8, "hp_max": 4, "magic_required": "Medium", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Special"], "description": ( "When an asset in same location rolls to make a profit (Farmers, Manufactory), " "roll the die twice and take the better result." ), "special_key": "ancient_mechanisms_reroll", }, { "name": "Arcane Laboratory", "attribute": "Wealth", "tier_required": 3, "cost": 6, "hp_max": 4, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Special"], "description": ( "Faction's overall Magic is counted as one step higher for creating assets " "in same location. Multiple labs stack." ), "special_key": "arcane_lab_magic_boost", }, { "name": "Free Company", "attribute": "Wealth", "tier_required": 3, "cost": 8, "hp_max": 6, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Force", "attack_damage": "2d4+2", "counterattack": "1d6", "qualities": ["Action", "Special"], "description": ( "Free action once per turn: move itself. " "Costs 1 Treasure upkeep per turn; if unpaid roll 1d6: 1–3 lost, 4–6 goes rogue." ), "special_key": "free_company_upkeep", }, { "name": "Manufactory", "attribute": "Wealth", "tier_required": 3, "cost": 8, "hp_max": 4, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "1d4", "qualities": ["Action"], "description": ( "Once per turn as free action: roll 1d6: " "1 = lose 1 Treasure, 2–5 = gain 1 Treasure, 6 = gain 2 Treasure. " "If Treasure lost and none available, asset is lost." ), "special_key": "manufactory_gamble", }, # Tier 4 { "name": "Healers", "attribute": "Wealth", "tier_required": 4, "cost": 12, "hp_max": 8, "magic_required": "None", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": ["Action"], "description": ( "When an asset within one move is destroyed by Force attack: " "pay half its purchase cost (rounded up) to instantly restore it with 1 HP. " "Cannot restore Bases of Influence." ), "special_key": "healers_revive", }, { "name": "Monopoly", "attribute": "Wealth", "tier_required": 4, "cost": 8, "hp_max": 12, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "1d6", "counterattack": "1d6", "qualities": ["Action"], "description": ( "Once per turn as free action: target an asset in same location; " "that asset's owner must pay 1 Treasure to owning faction or lose the asset." ), "special_key": "monopoly_extort", }, { "name": "Occult Countermeasures", "attribute": "Wealth", "tier_required": 4, "cost": 10, "hp_max": 8, "magic_required": "Low", "attack_attribute": "Wealth", "attack_vs": "Cunning", "attack_damage": "2d10", "counterattack": "1d10", "qualities": ["Special"], "description": ( "Can only Attack or Counterattack assets that require at least Low Magic to purchase." ), "special_key": "occult_countermeasures_magic_only", }, { "name": "Usurers", "attribute": "Wealth", "tier_required": 4, "cost": 12, "hp_max": 8, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "1d10", "counterattack": None, "qualities": ["Action"], "description": ( "For each unit of Usurers owned: Treasure cost of buying assets may be decreased " "by 2 (to minimum half cost). Each use inflicts 1d4 damage on Usurers." ), "special_key": "usurers_discount", }, # Tier 5 { "name": "Mad Genius", "attribute": "Wealth", "tier_required": 5, "cost": 6, "hp_max": 2, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Cunning", "attack_damage": "1d6", "counterattack": None, "qualities": ["Action"], "description": ( "Free action once per turn: move. " "Free action once per turn: sacrifice to treat Magic in its location as High " "for purpose of buying one asset." ), "special_key": "mad_genius_magic_sacrifice", }, { "name": "Smuggling Fleet", "attribute": "Wealth", "tier_required": 5, "cost": 12, "hp_max": 6, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Force", "attack_damage": "2d6", "counterattack": None, "qualities": ["Subtle", "Action"], "description": ( "Once per turn as free action: move itself and any one asset at current location " "to any water-accessible location within one move. Moved asset gains Subtle until used." ), "special_key": "smuggling_fleet_move", }, { "name": "Supply Interruption", "attribute": "Wealth", "tier_required": 5, "cost": 10, "hp_max": 8, "magic_required": "None", "attack_attribute": "Cunning", "attack_vs": "Wealth", "attack_damage": "1d6", "counterattack": None, "qualities": ["Subtle", "Action"], "description": ( "Free action once per turn: Cunning vs Wealth check against asset in same location. " "On success: owner must sacrifice Treasure equal to half target's purchase cost, " "or asset is disabled until paid." ), "special_key": "supply_interruption_disable", }, # Tier 6 { "name": "Economic Disruption", "attribute": "Wealth", "tier_required": 6, "cost": 25, "hp_max": 10, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "2d6", "counterattack": None, "qualities": ["Subtle", "Action"], "description": "Free action once per turn: move itself without cost.", "special_key": "economic_disruption_free_move", }, { "name": "Merchant Prince", "attribute": "Wealth", "tier_required": 6, "cost": 20, "hp_max": 10, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "2d8", "counterattack": "1d8", "qualities": ["Action"], "description": ( "Free action once per turn before buying asset in same location: " "Merchant Prince takes 1d4 damage, purchased asset costs 1d8 less " "(to minimum half normal price)." ), "special_key": "merchant_prince_discount", }, { "name": "Trade Company", "attribute": "Wealth", "tier_required": 6, "cost": 15, "hp_max": 10, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "2d6", "counterattack": "1d6", "qualities": ["Action"], "description": ( "Free action once per turn: accept 1d4 damage to earn 1d6-1 Treasure." ), "special_key": "trade_company_risk", }, # Tier 7 { "name": "Ancient Workshop", "attribute": "Wealth", "tier_required": 7, "cost": 25, "hp_max": 16, "magic_required": "Medium", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": None, "qualities": [], "description": ( "Free action once per turn: take 1d6 damage and gain 1d6 Treasure." ), "special_key": "ancient_workshop_produce", }, { "name": "Lead or Silver", "attribute": "Wealth", "tier_required": 7, "cost": 20, "hp_max": 10, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "2d10", "counterattack": "2d8", "qualities": [], "description": ( "If Attack reduces enemy asset to 0 HP: owner may immediately pay " "half target's purchase cost to claim it, reviving with 1 HP." ), "special_key": "lead_or_silver_capture", }, { "name": "Transport Network", "attribute": "Wealth", "tier_required": 7, "cost": 15, "hp_max": 5, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Wealth", "attack_damage": "1d12", "counterattack": None, "qualities": ["Action"], "description": ( "Free action: spend 1 Treasure to move any friendly asset from within " "two moves to any location within one move of either target or Network." ), "special_key": "transport_network_relay", }, # Tier 8 { "name": "Golden Prosperity", "attribute": "Wealth", "tier_required": 8, "cost": 40, "hp_max": 30, "magic_required": "Medium", "attack_attribute": None, "attack_vs": None, "attack_damage": None, "counterattack": "2d10", "qualities": [], "description": ( "Each turn as free action: gain 1d6 Treasure usable only to repair damaged assets. " "Unspent Treasure from this ability is lost." ), "special_key": "golden_prosperity_repair_fund", }, { "name": "Hired Legion", "attribute": "Wealth", "tier_required": 8, "cost": 30, "hp_max": 20, "magic_required": "None", "attack_attribute": "Wealth", "attack_vs": "Force", "attack_damage": "2d10+4", "counterattack": "2d10", "qualities": ["Action", "Special"], "description": ( "Free action once per turn: move itself. " "Costs 2 Treasure upkeep per turn; if unpaid goes rogue as Free Company. " "Cannot be voluntarily sold or disbanded." ), "special_key": "hired_legion_upkeep", }, ] # --------------------------------------------------------------------------- # Объединённый справочник и вспомогательные функции # --------------------------------------------------------------------------- ALL_ASSETS: list[dict] = CUNNING_ASSETS + FORCE_ASSETS + WEALTH_ASSETS # Индекс по имени для быстрого поиска O(1) _ASSET_INDEX: dict[str, dict] = {a["name"]: a for a in ALL_ASSETS} # Числовые уровни magic для сравнения MAGIC_LEVELS: dict[str, int] = { "None": 0, "Low": 1, "Medium": 2, "High": 3, } def get_asset(name: str) -> Optional[dict]: """ Возвращает определение актива по точному имени или None. Поиск регистрозависимый — имена активов уникальны и зафиксированы. """ return _ASSET_INDEX.get(name) def get_assets_by_attribute(attribute: str) -> list[dict]: """ Возвращает все активы для указанного атрибута. attribute — "Cunning" | "Force" | "Wealth" """ return [a for a in ALL_ASSETS if a["attribute"] == attribute] def get_available_assets( faction_fm: dict, location_wikilink: str, ) -> list[dict]: """ Возвращает список активов доступных фракции для покупки прямо сейчас. Проверяет все условия WWN: 1. faction.attribute >= asset.tier_required 2. faction.magic >= asset.magic_required 3. faction.treasure >= asset.cost 4. Текущее количество активов этого атрибута < значение атрибута (лимит: не более Force Force-активов, не более Wealth Wealth-активов и т.д.) 5. Фракция имеет Base of Influence в location_wikilink 6. У фракции ещё нет актива с таким именем faction_fm — frontmatter фракции из get_faction() location_wikilink — '[[Waterdeep]]' — место где есть BoI """ import re as _re def _norm(s: str) -> str: return _re.sub(r'^\[\[|\]\]$', '', s).split('|')[0].strip().lower() loc_norm = _norm(location_wikilink) # Проверяем наличие BoI в локации has_boi = any( a.get("is_base_of_influence") and _norm(a.get("location", "")) == loc_norm for a in faction_fm.get("assets", []) ) if not has_boi: return [] # Текущие активы фракции owned = faction_fm.get("assets", []) owned_names = {a["name"] for a in owned} # Считаем сколько активов каждого атрибута уже есть attr_counts: dict[str, int] = {"Cunning": 0, "Force": 0, "Wealth": 0} for a in owned: attr = a.get("attribute") if attr in attr_counts: attr_counts[attr] += 1 faction_magic_level = MAGIC_LEVELS.get(faction_fm.get("magic", "None"), 0) treasure = faction_fm.get("treasure", 0) result = [] for asset_def in ALL_ASSETS: attr = asset_def["attribute"] tier_req = asset_def["tier_required"] # Условие 1: атрибут достаточен if faction_fm.get(attr.lower(), 0) < tier_req: continue # Условие 2: magic достаточен asset_magic_level = MAGIC_LEVELS.get(asset_def["magic_required"], 0) if faction_magic_level < asset_magic_level: continue # Условие 3: хватает Treasure if treasure < asset_def["cost"]: continue # Условие 4: не превышен лимит активов атрибута attr_limit = faction_fm.get(attr.lower(), 0) if attr_counts.get(attr, 0) >= attr_limit: continue # Условие 5: BoI уже проверен выше # Условие 6: актив ещё не куплен if asset_def["name"] in owned_names: continue result.append(asset_def) return result