# Coverage for thb/characters/sp_aya.py : 25%

## 141 statements 45 run 96 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# -- third party --

6# -- own --

7 from game.base import ActionShootdown, InputTransaction

8 from thb.actions import ActionStage, ActionStageLaunchCard, DrawCards, DropCardStage, FinalizeStage

9 from thb.actions import GenericAction, LaunchCard, PlayerTurn, PrepareStage, ShowCards, UserAction

10 from thb.actions import ask_for_action

11 from thb.cards.base import Card, Skill, VirtualCard

12 from thb.cards.classes import PhysicalCard, t_None, t_Self

13 from thb.characters.base import Character, register_character_to

14 from thb.inputlets import ChooseOptionInputlet

15 from thb.mode import THBEventHandler

16

17

18# -- code --

19 class WindWalkLaunch(ActionStageLaunchCard):

20 pass

21

22

23 class WindWalkSkipAction(GenericAction):

24 def apply_action(self) -> bool:

25 g = self.game

26 ActionStage.force_break(g)

27 turn = PlayerTurn.get_current(g)

28 try:

29 turn.pending_stages.remove(DropCardStage)

30 turn.pending_stages.remove(FinalizeStage)

31 except Exception:

32 pass

33

34 return True

35

36

37 class WindWalkAction(UserAction):

38 card_usage = 'launch'

39

40 def apply_action(self):

41 g = self.game

42 tgt = self.target

43

44 while True:

45 dc = DrawCards(tgt, 1)

46 g.process_action(dc)

47 c, = dc.cards

48 self.card = c

49 g.process_action(ShowCards(tgt, [c]))

50

51 with InputTransaction('ActionStageAction', [tgt]) as trans:

52 p, rst = ask_for_action(

53 self, [tgt], ('cards', 'showncards'), g.players, trans=trans

54 )

55

56 if p is not tgt:

57 g.process_action(WindWalkSkipAction(tgt, tgt))

58 break

59

60 assert p

61 assert rst

62

63 cl, tl = rst

64 g.players.reveal(cl)

65 c, = cl

66

67 g.process_action(WindWalkLaunch(p, tl, c))

68

69 return True

70

71 def cond(self, cl):

72 if not (cl and len(cl) == 1):

73 return False

74

75 return bool(cl[0].associated_action) and [self.card] == VirtualCard.unwrap(cl)

76

77 def ask_for_action_verify(self, p, cl, tl):

78 assert len(cl) == 1

79 return WindWalkLaunch(p, tl, cl[0]).can_fire()

80

81 def choose_player_target(self, tl):

82 return tl, True

83

84

85 class WindWalkTargetLimit(ActionShootdown):

86 pass

87

88

89 class WindWalkHandler(THBEventHandler):

90 interested = ['action_apply', 'action_shootdown']

91

92 def handle(self, evt_type, act):

93 if evt_type == 'action_apply' and isinstance(act, LaunchCard):

94 src = act.source

95 if act.card.is_card(WindWalk):

96 return act

97

98 if not src.has_skill(WindWalk):

99 return act

100

101 src.tags['windwalk_last_targets'] = set(act.target_list)

102

103 elif evt_type == 'action_apply' and isinstance(act, PrepareStage):

104 src = act.source

105 if not src.has_skill(WindWalk):

106 return act

107

108 src.tags['windwalk_last_targets'] = set()

109

110 elif evt_type == 'action_shootdown' and isinstance(act, LaunchCard):

111 g = self.game

112 if not isinstance(g.action_stack[-1], WindWalkAction):

113 return act

114

115 src, tl = act.source, set(act.target_list)

116 last_tl = src.tags['windwalk_last_targets'] or set()

117

118 if not tl <= last_tl:

119 raise WindWalkTargetLimit

120

121 return act

122

123 return act

124

125

126 class WindWalk(Skill):

127 associated_action = WindWalkAction

128 skill_category = ['character', 'active']

129 target = t_Self()

130 usage = 'drop'

131

132 def check(self):

133 cl = self.associated_cards

134 if not(cl and len(cl) == 1):

135 return False

136

137 if cl[0].is_card(Skill):

138 return False

139

140 return True

141

142

143 class Dominance(Skill):

144 associated_action = None

145 skill_category = ['character', 'passive']

146 target = t_None()

147

148

149 class DominanceAction(PlayerTurn):

150 pass

151

152

153 class DominanceHandler(THBEventHandler):

154 interested = ['action_after', 'action_apply']

155

156 def handle(self, evt_type, act):

157 if evt_type == 'action_apply' and isinstance(act, PrepareStage):

158 t = act.target.tags

159 t['dominance_suits'] = set()

160 t['dominance_suit_SPADE'] = False

161 t['dominance_suit_CLUB'] = False

162 t['dominance_suit_HEART'] = False

163 t['dominance_suit_DIAMOND'] = False

164

165 elif evt_type == 'action_apply' and isinstance(act, LaunchCard):

166 if not act.source.has_skill(Dominance):

167 return act

168

169 card = act.card

170 if not card.is_card(PhysicalCard):

171 return act

172

173 suit = card.suit

174 if suit not in (Card.SPADE, Card.CLUB, Card.HEART, Card.DIAMOND):

175 return act

176

177 try:

178 t = act.source.tags

179 t['dominance_suits'].add(suit)

180 t['dominance_suit_%s' % Card.SUIT_REV[suit]] = True

181

182 except AttributeError:

183 pass

184

185 elif evt_type == 'action_after' and isinstance(act, FinalizeStage):

186 tgt = act.target

187 if not tgt.has_skill(Dominance):

188 return act

189

190 if len(tgt.tags['dominance_suits'] or set()) != 4:

191 return act

192

193 g = self.game

194 if not g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):

195 return act

196

197 g.process_action(DominanceAction(tgt))

198

199 return act

200

201

202@register_character_to('imperial')

203 class SpAya(Character):

204 skills = [WindWalk, Dominance]

205 eventhandlers = [WindWalkHandler, DominanceHandler]

206 maxlife = 4