# Coverage for thb/meta/inputlets.py : 11%

## 170 statements 26 run 144 missing 0 excluded 0 partial

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 -*-

2 from __future__ import annotations

3

4# -- stdlib --

5 from functools import wraps

6 from typing import List, Sequence

7 import logging

8

9# -- third party --

10# -- own --

11 from thb import actions

12 from thb.actions import BaseActionStage

13 from thb.cards.base import Card, Skill

14 from thb.characters.base import Character

15 from thb.meta.common import ui_meta

16

17

18# -- code --

19 log = logging.getLogger('Inputlets UI Meta')

20

21

22 class ActionDisplayResult(Exception):

23 __slots__ = (

24 'valid',

25 'prompt',

26 'pl_selecting',

27 'pl_disabled',

28 'pl_selected',

29 )

30

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

38

39

40 def walk_wrapped(g, cl, check_is_complete):

41 for c in cl:

42 if not isinstance(c, Skill):

43 continue

44

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, [], [])

51

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, [], [])

57

58 if not rst:

59 raise ActionDisplayResult(False, reason, False, [], [])

60

61 walk_wrapped(g, c.associated_cards, True)

62

63

64 def 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

72

73 except Exception as e:

74 log.exception("ActionInputlet meta failure")

75 return ActionDisplayResult(False, str(e), False, [], [])

76

77 wrapper.__name__ = f.__name__

78 return wrapper

79

80

81@ui_meta(actions.ActionInputlet)

82 class ActionInputlet:

83 @action_disp_func

84 def passive_action_disp(self, ilet, skills, rawcards, params, players):

85 me = self.me

86

87 usage = getattr(ilet.initiator, 'card_usage', 'none')

88

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

96

97 cards, prompt_card = self.pasv_handle_card_selection(ilet, cards)

98 plsel, disables, players, prompt_target = self.pasv_handle_player_selection(ilet, players)

99

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)

104

105 raise ActionDisplayResult(True, prompt_target or prompt_card, plsel, disables, players)

106

107 def pasv_handle_card_selection(self, ilet, cards):

108 g = self.game

109

110 if not ilet.categories:

111 return [], ''

112

113 if cards:

114 walk_wrapped(g, cards, True)

115

116 from thb.cards.base import Skill

117

118 if cards[0].is_card(Skill) and not actions.skill_check(cards[0]):

119 raise ActionDisplayResult(False, '您不能这样出牌', False, [], [])

120

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)

124

125 if not c:

126 raise ActionDisplayResult(False, text, False, [], [])

127

128 return cards, text

129

130 def pasv_handle_player_selection(self, ilet, players):

131 g = self.game

132

133 if not ilet.candidates:

134 return False, [], [], ''

135

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]

138

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)

146

147 if not logic_valid:

148 raise ActionDisplayResult(False, reason, True, disables, players)

149

150 return True, disables, players, reason

151

152 def passive_action_recommend(self, ilet):

153 me = self.me

154

155 if not ilet.categories: return

156

157 for c in me.showncards:

158 if not ilet.initiator.cond([c]): continue

159 return c

160

161 for c in me.cards:

162 if not ilet.initiator.cond([c]): continue

163 return c

164

165 @action_disp_func

166 def active_action_disp(self, ilet, skills, rawcards, params, players):

167 me = self.me

168

169 stage = ilet.initiator

170

171 if not skills and not rawcards:

172 raise ActionDisplayResult(False, stage.ui_meta.idle_prompt, False, [], [])

173

174 usage = getattr(ilet.initiator, 'card_usage', 'none')

175

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

183

184 card = self.actv_handle_card_selection(stage, cards)

185 players, disables, prompt = self.actv_handle_target_selection(stage, card, players)

186

187 act = stage.launch_card_cls(me, players, card)

188

189 shootdown = act.action_shootdown()

190 if shootdown is not None:

191 raise ActionDisplayResult(False, shootdown.ui_meta.shootdown_message, True, disables, players)

192

193 raise ActionDisplayResult(True, prompt, True, disables, players)

194

195 def actv_handle_card_selection(self, act, cards):

196 g = self.game

197 if len(cards) != 1:

198 raise ActionDisplayResult(False, '请选择一张牌使用', False, [], [])

199

200 walk_wrapped(g, cards, False)

201 card = cards[0]

202

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)

206

207 if not c:

208 raise ActionDisplayResult(False, text, False, [], [])

209

210 return card

211

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

218

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, [], [])

230

231 disables.append(p)

232

233 plsel = True

234 for i in disables:

235 try:

236 tl.remove(i)

237 except ValueError:

238 pass

239

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, [], [])

245

246 if not rst:

247 raise ActionDisplayResult(False, reason, plsel, disables, selected)

248

249 if not tl_valid: # honor result of game logic

250 raise ActionDisplayResult(False, '您选择的目标不符合规则', plsel, disables, selected)

251

252 return tl, disables, reason