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

2from __future__ import annotations 

3 

4# -- stdlib -- 

5from typing import cast 

6 

7# -- third party -- 

8# -- own -- 

9from thb.actions import Damage, DrawCards, DropCardStage, DropCards, FinalizeStage, GenericAction 

10from thb.actions import MigrateCardsTransaction, PlayerTurn, UserAction, random_choose_card 

11from thb.actions import user_choose_players 

12from thb.cards.base import Skill, VirtualCard 

13from thb.cards.classes import t_None 

14from thb.characters.base import Character, register_character_to 

15from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet 

16from thb.mode import THBEventHandler 

17 

18 

19# -- code -- 

20class AutumnWindEffect(GenericAction): 

21 def apply_action(self): 

22 src, tgt = self.source, self.target 

23 

24 g = self.game 

25 

26 catnames = ('cards', 'showncards', 'equips') 

27 cats = [getattr(tgt, i) for i in catnames] 

28 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames)) 

29 card = card or random_choose_card(g, cats) 

30 if not card: 

31 return False 

32 

33 self.card = card 

34 g.players.exclude(tgt).reveal(card) 

35 g.process_action(DropCards(src, tgt, cards=[card])) 

36 return True 

37 

38 def is_valid(self): 

39 tgt = self.target 

40 catnames = ('cards', 'showncards', 'equips') 

41 return not tgt.dead and any(getattr(tgt, i) for i in catnames) 

42 

43 

44class AutumnWindAction(UserAction): 

45 def __init__(self, source, target_list): 

46 self.source = source 

47 self.target = source 

48 self.target_list = target_list 

49 

50 def apply_action(self): 

51 g = self.game 

52 src = self.source 

53 

54 for p in self.target_list: 

55 g.process_action(AutumnWindEffect(src, p)) 

56 

57 return True 

58 

59 

60class AutumnWindHandler(THBEventHandler): 

61 interested = ['action_after'] 

62 

63 def handle(self, evt_type, act): 

64 if evt_type == 'action_after' and isinstance(act, DropCardStage): 

65 self.n = n = act.dropn 

66 if n <= 0: 

67 return act 

68 

69 tgt = act.target 

70 if not tgt.has_skill(AutumnWind): 

71 return act 

72 

73 g = self.game 

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

75 return act 

76 

77 candidates = [ 

78 p for p in g.players if 

79 p is not tgt and 

80 (p.cards or p.showncards or p.equips) and 

81 not p.dead 

82 ] 

83 

84 pl = candidates and user_choose_players(self, tgt, candidates) 

85 if not pl: 

86 return act 

87 

88 g.process_action(AutumnWindAction(tgt, pl)) 

89 

90 return act 

91 

92 def choose_player_target(self, tl): 

93 if not tl: 

94 return (tl, False) 

95 

96 return (tl[:self.n], True) 

97 

98 

99class AutumnWind(Skill): 

100 associated_action = None 

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

102 target = t_None() 

103 

104 

105class DecayDrawCards(DrawCards): 

106 pass 

107 

108 

109class DecayDrawCardHandler(THBEventHandler): 

110 interested = ['post_card_migration'] 

111 execute_before = ['LuckHandler'] 

112 

113 def handle(self, evt_type, arg): 

114 if evt_type != 'post_card_migration': 

115 return arg 

116 

117 g = self.game 

118 

119 try: 

120 me = PlayerTurn.get_current(g).target 

121 except IndexError: 

122 return arg 

123 

124 if me is None: return arg 

125 if me.dead: return arg 

126 if not me.has_skill(Decay): return arg 

127 

128 trans = cast(MigrateCardsTransaction, arg) 

129 

130 candidates = {} 

131 

132 for m in trans.movements: 

133 src = m.fr.owner 

134 if not src: continue 

135 if m.card.is_card(VirtualCard): continue 

136 if m.fr.type not in ('cards', 'showncards'): continue 

137 if src.cards or src.showncards: continue 

138 if src.dead: continue 

139 if src is me: continue 

140 candidates[src] = 1 

141 

142 if not candidates: 

143 return arg 

144 

145 g.process_action(DecayDrawCards(me, len(candidates))) 

146 

147 return arg 

148 

149 

150class DecayAction(UserAction): 

151 def apply_action(self): 

152 tgt = self.target 

153 tgt.tags['shizuha_decay'] = True 

154 return True 

155 

156 

157class DecayEffect(UserAction): 

158 def __init__(self, source, target, dcs): 

159 self.source = source 

160 self.target = target 

161 self.dcs = dcs 

162 

163 def apply_action(self): 

164 self.dcs.dropn = max(1, self.dcs.dropn + 1) 

165 return True 

166 

167 

168class DecayDamageHandler(THBEventHandler): 

169 interested = ['action_after', 'action_before'] 

170 execute_after = [ 

171 'DyingHandler', 

172 'AyaRoundfanHandler', 

173 'NenshaPhoneHandler', 

174 'SuwakoHatHandler', 

175 ] 

176 

177 def handle(self, evt_type, act): 

178 if evt_type == 'action_after' and isinstance(act, Damage): 

179 src, tgt = act.source, act.target 

180 if not (tgt and tgt.has_skill(Decay)): 

181 return act 

182 

183 g = self.game 

184 

185 try: 

186 cur = PlayerTurn.get_current(g).target 

187 except IndexError: 

188 # Yugi AssaultKOF & Yuuka Sadist 

189 return act 

190 

191 if cur is tgt: return act 

192 g.process_action(DecayAction(src, cur)) 

193 

194 elif evt_type == 'action_before' and isinstance(act, DropCardStage): 

195 tgt = act.target 

196 t = tgt.tags 

197 if not t['shizuha_decay']: return act 

198 

199 t['shizuha_decay'] = False 

200 g = self.game 

201 g.process_action(DecayEffect(tgt, tgt, act)) 

202 

203 return act 

204 

205 

206class DecayFadeHandler(THBEventHandler): 

207 interested = ['action_after'] 

208 

209 def handle(self, evt_type, act): 

210 if evt_type == 'action_after' and isinstance(act, FinalizeStage): 

211 tgt = act.target 

212 if tgt.tags['shizuha_decay']: 

213 tgt.tags['shizuha_decay'] = False 

214 

215 return act 

216 

217 

218class Decay(Skill): 

219 associated_action = None 

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

221 target = t_None() 

222 

223 

224@register_character_to('common') 

225class Shizuha(Character): 

226 skills = [Decay, AutumnWind] 

227 eventhandlers = [DecayDamageHandler, DecayDrawCardHandler, DecayFadeHandler, AutumnWindHandler] 

228 maxlife = 3