Coverage for thb/meta/inputlets.py : 11%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2from __future__ import annotations
4# -- stdlib --
5from functools import wraps
6from typing import List, Sequence
7import logging
9# -- third party --
10# -- own --
11from thb import actions
12from thb.actions import BaseActionStage
13from thb.cards.base import Card, Skill
14from thb.characters.base import Character
15from thb.meta.common import ui_meta
18# -- code --
19log = logging.getLogger('Inputlets UI Meta')
22class ActionDisplayResult(Exception):
23 __slots__ = (
24 'valid',
25 'prompt',
26 'pl_selecting',
27 'pl_disabled',
28 'pl_selected',
29 )
31 def __init__(self, valid, prompt, pl_selecting, pl_disabled, pl_selected):
32 Exception.__init__(self)
33 self.valid = valid
34 self.prompt = prompt
35 self.pl_selecting = pl_selecting
36 self.pl_disabled = pl_disabled
37 self.pl_selected = pl_selected
40def walk_wrapped(g, cl, check_is_complete):
41 for c in cl:
42 if not isinstance(c, Skill):
43 continue
45 if check_is_complete:
46 try:
47 is_complete = c.ui_meta.is_complete
48 except Exception:
49 # skills that cannot combined with other skill
50 raise ActionDisplayResult(False, '您不能这样出牌', False, [], [])
52 try:
53 rst, reason = is_complete(c)
54 except Exception:
55 log.exception('card.ui_meta.is_complete error')
56 raise ActionDisplayResult(False, '[card.ui_meta.is_complete错误]', False, [], [])
58 if not rst:
59 raise ActionDisplayResult(False, reason, False, [], [])
61 walk_wrapped(g, c.associated_cards, True)
64def action_disp_func(f):
65 @wraps(f)
66 def wrapper(*a, **k):
67 try:
68 f(*a, **k)
69 raise Exception("Didn't raise ActionDisplayResult!")
70 except ActionDisplayResult as e:
71 return e
73 except Exception as e:
74 log.exception("ActionInputlet meta failure")
75 return ActionDisplayResult(False, str(e), False, [], [])
77 wrapper.__name__ = f.__name__
78 return wrapper
81@ui_meta(actions.ActionInputlet)
82class ActionInputlet:
83 @action_disp_func
84 def passive_action_disp(self, ilet, skills, rawcards, params, players):
85 me = self.me
87 usage = getattr(ilet.initiator, 'card_usage', 'none')
89 if skills:
90 if any(not me.has_skill(s) for s in skills):
91 raise ActionDisplayResult(False, '您不能这样出牌', False, [], [])
92 cards = [actions.skill_wrap(me, skills, rawcards, params)]
93 usage = cards[0].usage if usage == 'launch' else usage
94 else:
95 cards = rawcards
97 cards, prompt_card = self.pasv_handle_card_selection(ilet, cards)
98 plsel, disables, players, prompt_target = self.pasv_handle_player_selection(ilet, players)
100 verify = getattr(ilet.initiator, 'ask_for_action_verify', None)
101 rst = not verify or verify(me, cards, players)
102 if not rst:
103 raise ActionDisplayResult(False, rst.ui_meta.shootdown_message, plsel, disables, players)
105 raise ActionDisplayResult(True, prompt_target or prompt_card, plsel, disables, players)
107 def pasv_handle_card_selection(self, ilet, cards):
108 g = self.game
110 if not ilet.categories:
111 return [], ''
113 if cards:
114 walk_wrapped(g, cards, True)
116 from thb.cards.base import Skill
118 if cards[0].is_card(Skill) and not actions.skill_check(cards[0]):
119 raise ActionDisplayResult(False, '您不能这样出牌', False, [], [])
121 c = ilet.initiator.cond(cards)
122 c1, text = ilet.initiator.ui_meta.choose_card_text(g, ilet.initiator, cards)
123 assert c == c1, 'cond = %s, meta = %s' % (c, c1)
125 if not c:
126 raise ActionDisplayResult(False, text, False, [], [])
128 return cards, text
130 def pasv_handle_player_selection(self, ilet, players):
131 g = self.game
133 if not ilet.candidates:
134 return False, [], [], ''
136 disables = [p for p in g.players if p not in ilet.candidates]
137 players = [p for p in players if p not in disables]
139 players, logic_valid = ilet.initiator.choose_player_target(players)
140 try:
141 ui_meta_valid, reason = ilet.initiator.ui_meta.target(players)
142 assert bool(logic_valid) == bool(ui_meta_valid), 'logic: %s, ui: %s' % (logic_valid, ui_meta_valid)
143 except Exception:
144 log.exception('act.ui_meta.target error')
145 raise ActionDisplayResult(False, '[act.ui_meta.target错误]', bool(ilet.candidates), disables, players)
147 if not logic_valid:
148 raise ActionDisplayResult(False, reason, True, disables, players)
150 return True, disables, players, reason
152 def passive_action_recommend(self, ilet):
153 me = self.me
155 if not ilet.categories: return
157 for c in me.showncards:
158 if not ilet.initiator.cond([c]): continue
159 return c
161 for c in me.cards:
162 if not ilet.initiator.cond([c]): continue
163 return c
165 @action_disp_func
166 def active_action_disp(self, ilet, skills, rawcards, params, players):
167 me = self.me
169 stage = ilet.initiator
171 if not skills and not rawcards:
172 raise ActionDisplayResult(False, stage.ui_meta.idle_prompt, False, [], [])
174 usage = getattr(ilet.initiator, 'card_usage', 'none')
176 if skills:
177 if any(not me.has_skill(s) for s in skills):
178 raise ActionDisplayResult(False, '您不能这样出牌', False, [], [])
179 cards = [actions.skill_wrap(me, skills, rawcards, params)]
180 usage = cards[0].usage if usage == 'launch' else usage
181 else:
182 cards = rawcards
184 card = self.actv_handle_card_selection(stage, cards)
185 players, disables, prompt = self.actv_handle_target_selection(stage, card, players)
187 act = stage.launch_card_cls(me, players, card)
189 shootdown = act.action_shootdown()
190 if shootdown is not None:
191 raise ActionDisplayResult(False, shootdown.ui_meta.shootdown_message, True, disables, players)
193 raise ActionDisplayResult(True, prompt, True, disables, players)
195 def actv_handle_card_selection(self, act, cards):
196 g = self.game
197 if len(cards) != 1:
198 raise ActionDisplayResult(False, '请选择一张牌使用', False, [], [])
200 walk_wrapped(g, cards, False)
201 card = cards[0]
203 c = act.cond(cards)
204 c1, text = act.ui_meta.choose_card_text(g, act, cards)
205 assert c == c1, 'cond = %s, meta = %s' % (c, c1)
207 if not c:
208 raise ActionDisplayResult(False, text, False, [], [])
210 return card
212 def actv_handle_target_selection(self, stage: BaseActionStage, card: Card, players: Sequence[Character]):
213 plsel = False
214 selected: List[Character] = []
215 disables: List[Character] = []
216 g = self.game
217 me = self.me
219 tl, tl_valid = card.target(me, players)
220 if tl is not None:
221 selected = list(tl)
222 if card.target.__name__ in ('t_One', 't_OtherOne'):
223 for p in g.players:
224 act = stage.launch_card_cls(me, [p], card)
225 shootdown = act.action_shootdown()
226 if shootdown is not None:
227 if shootdown.ui_meta.target_independent:
228 reason = shootdown.ui_meta.shootdown_message
229 raise ActionDisplayResult(False, reason, False, [], [])
231 disables.append(p)
233 plsel = True
234 for i in disables:
235 try:
236 tl.remove(i)
237 except ValueError:
238 pass
240 try:
241 rst, reason = card.ui_meta.is_action_valid(card, tl)
242 except Exception:
243 log.exception('card.ui_meta.is_action_valid error')
244 raise ActionDisplayResult(False, '[card.ui_meta.is_action_valid错误]', False, [], [])
246 if not rst:
247 raise ActionDisplayResult(False, reason, plsel, disables, selected)
249 if not tl_valid: # honor result of game logic
250 raise ActionDisplayResult(False, '您选择的目标不符合规则', plsel, disables, selected)
252 return tl, disables, reason