# Coverage for thb/characters/shizuha.py : 95%

## 150 statements 145 run 5 missing 0 excluded 5 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 typing import cast

6

7# -- third party --

8# -- own --

9# -- errord --

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

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

12 from thb.actions import user_choose_players

13 from thb.cards.base import Skill, VirtualCard

14 from thb.cards.classes import t_None

15 from thb.characters.base import Character, register_character_to

16 from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet

17 from thb.mode import THBEventHandler

18

19

20# -- code --

21 class 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 ↛ 32 line 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 ↛ exit line 42 didn't finish the generator expression on line 42

43

44

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

61 class 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 ↛ 95 line 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

100 class AutumnWind(Skill):

101 associated_action = None

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

103 target = t_None()

104

105

106 class DecayDrawCards(DrawCards):

107 pass

108

109

110 class 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 ↛ 116 line 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 ↛ exit line 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

151 class DecayAction(UserAction):

152 def apply_action(self):

153 tgt = self.target

154 tgt.tags['shizuha_decay'] = True

155 return True

156

157

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

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

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

219 class Decay(Skill):

220 associated_action = None

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

222 target = t_None()

223

224

225@register_character_to('common')

226 class Shizuha(Character):

227 skills = [Decay, AutumnWind]

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

229 maxlife = 3