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

9# -- errord -- 

10from thb.actions import Damage, DrawCards, DropCardStage, DropCards, GenericAction 

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

12from thb.actions import user_choose_players 

13from thb.cards.base import Skill, VirtualCard 

14from thb.cards.classes import t_None 

15from thb.characters.base import Character, register_character_to 

16from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet 

17from thb.mode import THBEventHandler 

18 

19 

20# -- code -- 

21class AutumnWindEffect(GenericAction): 

22 def apply_action(self): 

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

24 

25 g = self.game 

26 

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

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

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

30 card = card or random_choose_card(g, cats) 

31 if not card: 31 ↛ 32line 31 didn't jump to line 32, because the condition on line 31 was never true

32 return False 

33 

34 self.card = card 

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

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

37 return True 

38 

39 def is_valid(self): 

40 tgt = self.target 

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

42 return not tgt.dead and any(getattr(tgt, i) for i in catnames) 42 ↛ exitline 42 didn't finish the generator expression on line 42

43 

44 

45class AutumnWindAction(UserAction): 

46 def __init__(self, source, target_list): 

47 self.source = source 

48 self.target = None 

49 self.target_list = target_list 

50 

51 def apply_action(self): 

52 g = self.game 

53 src = self.source 

54 

55 for p in self.target_list: 

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

57 

58 return True 

59 

60 

61class AutumnWindHandler(THBEventHandler): 

62 interested = ['action_after'] 

63 

64 def handle(self, evt_type, act): 

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

66 self.n = n = act.dropn 

67 if n <= 0: 

68 return act 

69 

70 tgt = act.target 

71 if not tgt.has_skill(AutumnWind): 

72 return act 

73 

74 g = self.game 

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

76 return act 

77 

78 candidates = [ 

79 p for p in g.players if 

80 p is not tgt and 

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

82 not p.dead 

83 ] 

84 

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

86 if not pl: 

87 return act 

88 

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

90 

91 return act 

92 

93 def choose_player_target(self, tl): 

94 if not tl: 94 ↛ 95line 94 didn't jump to line 95, because the condition on line 94 was never true

95 return (tl, False) 

96 

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

98 

99 

100class AutumnWind(Skill): 

101 associated_action = None 

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

103 target = t_None() 

104 

105 

106class DecayDrawCards(DrawCards): 

107 pass 

108 

109 

110class DecayDrawCardHandler(THBEventHandler): 

111 interested = ['post_card_migration'] 

112 execute_before = ['LuckHandler'] 

113 

114 def handle(self, evt_type, arg): 

115 if evt_type != 'post_card_migration': 115 ↛ 116line 115 didn't jump to line 116, because the condition on line 115 was never true

116 return arg 

117 

118 g = self.game 

119 

120 try: 

121 me = PlayerTurn.get_current(g).target 

122 except IndexError: 

123 return arg 

124 

125 if me is None: return arg 125 ↛ exitline 125 didn't return from function 'handle', because the return on line 125 wasn't executed

126 if me.dead: return arg 

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

128 

129 trans = cast(MigrateCardsTransaction, arg) 

130 

131 candidates = {} 

132 

133 for m in trans.movements: 

134 src = m.fr.owner 

135 if not src: continue 

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

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

138 if src.cards or src.showncards: continue 

139 if src.dead: continue 

140 if src is me: continue 

141 candidates[src] = 1 

142 

143 if not candidates: 

144 return arg 

145 

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

147 

148 return arg 

149 

150 

151class DecayAction(UserAction): 

152 def apply_action(self): 

153 tgt = self.target 

154 tgt.tags['shizuha_decay'] = True 

155 return True 

156 

157 

158class DecayEffect(UserAction): 

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

160 self.source = source 

161 self.target = target 

162 self.dcs = dcs 

163 

164 def apply_action(self): 

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

166 return True 

167 

168 

169class DecayDamageHandler(THBEventHandler): 

170 interested = ('action_after', 'action_before') 

171 execute_after = ( 

172 'DyingHandler', 

173 'AyaRoundfanHandler', 

174 'NenshaPhoneHandler', 

175 'SuwakoHatHandler', 

176 ) 

177 

178 def handle(self, evt_type, act): 

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

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

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

182 return act 

183 

184 g = self.game 

185 

186 try: 

187 cur = PlayerTurn.get_current(g).target 

188 except IndexError: 

189 # Yugi AssaultKOF & Yuuka Sadist 

190 return act 

191 

192 if cur is tgt: return act 

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

194 

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

196 tgt = act.target 

197 t = tgt.tags 

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

199 

200 t['shizuha_decay'] = False 

201 g = self.game 

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

203 

204 return act 

205 

206 

207class DecayFadeHandler(THBEventHandler): 

208 interested = ('action_after', ) 

209 

210 def handle(self, evt_type, act): 

211 if evt_type == 'action_after' and isinstance(act, PlayerTurn): 

212 tgt = act.target 

213 if tgt.tags['shizuha_decay']: 

214 tgt.tags['shizuha_decay'] = False 

215 

216 return act 

217 

218 

219class Decay(Skill): 

220 associated_action = None 

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

222 target = t_None() 

223 

224 

225@register_character_to('common') 

226class Shizuha(Character): 

227 skills = [Decay, AutumnWind] 

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

229 maxlife = 3