Hide keyboard shortcuts

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 

3# -- stdlib -- 

4# -- third party -- 

5# -- own -- 

6from game.base import ActionShootdown, InputTransaction 

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

8from thb.actions import GenericAction, LaunchCard, PlayerTurn, ShowCards, UserAction, ask_for_action 

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

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

11from thb.characters.base import Character, register_character_to 

12from thb.inputlets import ChooseOptionInputlet 

13from thb.mode import THBEventHandler 

14 

15 

16# -- code -- 

17class WindWalkLaunch(ActionStageLaunchCard): 

18 pass 

19 

20 

21class WindWalkSkipAction(GenericAction): 

22 def apply_action(self) -> bool: 

23 g = self.game 

24 ActionStage.force_break(g) 

25 turn = PlayerTurn.get_current(g) 

26 try: 

27 turn.pending_stages.remove(DropCardStage) 

28 turn.pending_stages.remove(FinalizeStage) 

29 except Exception: 

30 pass 

31 

32 return True 

33 

34 

35class WindWalkAction(UserAction): 

36 card_usage = 'launch' 

37 

38 def apply_action(self): 

39 g = self.game 

40 tgt = self.target 

41 

42 while True: 

43 dc = DrawCards(tgt, 1) 

44 g.process_action(dc) 

45 c, = dc.cards 

46 self.card = c 

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

48 

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

50 p, rst = ask_for_action( 

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

52 ) 

53 

54 if p is not tgt: 

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

56 break 

57 

58 assert p 

59 assert rst 

60 

61 cl, tl = rst 

62 g.players.reveal(cl) 

63 c, = cl 

64 

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

66 

67 return True 

68 

69 def cond(self, cl): 

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

71 return False 

72 

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

74 

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

76 assert len(cl) == 1 

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

78 

79 def choose_player_target(self, tl): 

80 return tl, True 

81 

82 

83class WindWalkTargetLimit(ActionShootdown): 

84 pass 

85 

86 

87class WindWalkHandler(THBEventHandler): 

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

89 

90 def handle(self, evt_type, act): 

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

92 src = act.source 

93 if act.card.is_card(WindWalk): 

94 return act 

95 

96 if not src.has_skill(WindWalk): 

97 return act 

98 

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

100 

101 elif evt_type == 'action_apply' and isinstance(act, PlayerTurn): 

102 src = act.source 

103 if not src.has_skill(WindWalk): 

104 return act 

105 

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

107 

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

109 g = self.game 

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

111 return act 

112 

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

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

115 

116 if not tl <= last_tl: 

117 raise WindWalkTargetLimit 

118 

119 return act 

120 

121 return act 

122 

123 

124class WindWalk(Skill): 

125 associated_action = WindWalkAction 

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

127 target = t_Self() 

128 usage = 'drop' 

129 

130 def check(self): 

131 cl = self.associated_cards 

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

133 return False 

134 

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

136 return False 

137 

138 return True 

139 

140 

141class Dominance(Skill): 

142 associated_action = None 

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

144 target = t_None() 

145 

146 

147class DominanceAction(PlayerTurn): 

148 pass 

149 

150 

151class DominanceHandler(THBEventHandler): 

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

153 

154 def handle(self, evt_type, act): 

155 if evt_type == 'action_apply' and isinstance(act, PlayerTurn): 

156 t = act.target.tags 

157 t['dominance_suits'] = set() 

158 t['dominance_suit_SPADE'] = False 

159 t['dominance_suit_CLUB'] = False 

160 t['dominance_suit_HEART'] = False 

161 t['dominance_suit_DIAMOND'] = False 

162 

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

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

165 return act 

166 

167 card = act.card 

168 if not card.is_card(PhysicalCard): 

169 return act 

170 

171 suit = card.suit 

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

173 return act 

174 

175 try: 

176 t = act.source.tags 

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

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

179 

180 except AttributeError: 

181 pass 

182 

183 elif evt_type == 'action_after' and isinstance(act, PlayerTurn): 

184 tgt = act.target 

185 if not tgt.has_skill(Dominance): 

186 return act 

187 

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

189 return act 

190 

191 g = self.game 

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

193 return act 

194 

195 g.process_action(DominanceAction(tgt)) 

196 

197 return act 

198 

199 

200@register_character_to('imperial') 

201class SpAya(Character): 

202 skills = [WindWalk, Dominance] 

203 eventhandlers = [WindWalkHandler, DominanceHandler] 

204 maxlife = 4