# Coverage for thb/characters/kanako.py : 83%

## 152 statements 134 run 18 missing 0 excluded 3 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

3# -- stdlib --

4 from typing import Any, Type

5

6# -- third party --

7# -- own --

8 from thb.actions import Damage, DrawCardStage, DrawCards, DropCards, FinalizeStage, ForEach

9 from thb.actions import LaunchCard, ShowCards, UserAction, migrate_cards, random_choose_card, ttags

10 from thb.actions import user_choose_cards, user_choose_players

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

12 from thb.cards.classes import AttackCard, DuelCard, TreatAs, t_None

13 from thb.characters.base import Character, register_character_to

14 from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet

15 from thb.mode import THBEventHandler

16

17

18# -- code --

19 class KanakoFaithCheers(UserAction):

20 def apply_action(self):

21 return self.game.process_action(DrawCards(self.target, 1))

22

23

24 class KanakoFaithAttack(TreatAs, VirtualCard):

25 treat_as = AttackCard

26

27

28 class KanakoFaithDuel(TreatAs, VirtualCard):

29 treat_as = DuelCard

30

31

32 class KanakoFaithCounteract(UserAction):

33 def apply_action(self):

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

35 g = self.game

36 src.dead or tgt.dead or g.process_action(KanakoFaithCounteractPart1(src, tgt))

37 src.dead or tgt.dead or g.process_action(KanakoFaithCounteractPart2(src, tgt))

38 return True

39

40

41 class KanakoFaithCounteractPart1(UserAction):

42

43 def apply_action(self):

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

45 g = self.game

46

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

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

49

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

51 card = card or random_choose_card(g, cats)

52

53 assert card

54 self.card = card

55

56 g.players.reveal(card)

57 g.process_action(DropCards(src, tgt, [card]))

58 return True

59

60

61 class KanakoFaithCounteractPart2(UserAction):

62 def apply_action(self):

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

64 g = self.game

65

66 choice = g.user_input([tgt], ChooseOptionInputlet(self, ('duel', 'attack')))

67

68 cls: Type[Any]

69 if choice == 'duel':

70 cls = KanakoFaithDuel

71 elif choice == 'attack':

72 cls = KanakoFaithAttack

73 else:

74 cls = KanakoFaithAttack

75

76 g.process_action(LaunchCard(tgt, [src], cls(tgt), bypass_check=True))

77

78 return True

79

80

81 class KanakoFaithEffect(UserAction):

82 card_usage = 'drop'

83

84 def apply_action(self):

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

86 g = self.game

87

88 has_card = src.cards or src.showncards or src.equips

89

90 if has_card and g.user_input([tgt], ChooseOptionInputlet(self, ('drop', 'draw'))) == 'drop':

91 g.process_action(KanakoFaithCounteract(tgt, src))

92 else:

93 g.process_action(KanakoFaithCheers(tgt, src))

94

95 return True

96

97

98 class KanakoFaithAction(ForEach):

99 action_cls = KanakoFaithEffect

100

101 def prepare(self):

102 self.source.tags['kanako_faith'] = True

103

104 def is_valid(self):

105 return not self.source.tags['kanako_faith']

106

107

108 class KanakoFaith(Skill):

109 associated_action = KanakoFaithAction

110 skill_category = ['character', 'active', 'once']

111

112 def check(self):

113 return not self.associated_cards

114

115 def target(self, p, tl):

116 g = self.game

117 l = g.players.rotate_to(p)

118 del l[0]

119

120 dists = LaunchCard.calc_raw_distance(g, p, AttackCard())

121 return ([t for t in l if not t.dead and dists[t] <= 1], True)

122

123

124 class VirtueAction(UserAction):

125 card_usage = 'handover'

126

127 def apply_action(self):

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

129 g = self.game

130 g.process_action(DrawCards(tgt, 2))

131

132 cl = user_choose_cards(self, tgt, ('cards', 'showncards', 'equips'))

133 c = cl[0] if cl else random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips])

134

135 if not c: return False 135 ↛ exit line 135 didn't return from function 'apply_action', because the return on line 135 wasn't executed

136

137 g.players.reveal(c)

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

139 migrate_cards([c], src.cards)

140

141 if c.suit == Card.HEART:

142 g.process_action(DrawCards(src, 1))

143

144 self.card = c

145

146 return True

147

148 def cond(self, cl):

149 return len(cl) == 1 and not cl[0].is_card(VirtualCard)

150

151

152 class VirtueHandler(THBEventHandler):

153 interested = ['action_before']

154

155 def handle(self, evt_type, act):

156 if evt_type == 'action_before' and isinstance(act, DrawCardStage):

157 if act.cancelled: 157 ↛ 158 line 157 didn't jump to line 158, because the condition on line 157 was never true

158 return act

159

160 tgt = act.target

161 if not tgt.has_skill(Virtue):

162 return act

163

164 g = self.game

165 pl = [p for p in g.players if not p.dead and p is not tgt]

166 pl = pl and user_choose_players(self, tgt, pl)

167 if not pl:

168 return act

169

170 act.cancelled = True

171 g.process_action(VirtueAction(tgt, pl[0]))

172

173 return act

174

175 def choose_player_target(self, tl):

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

177 return (tl, False)

178

179 return (tl[-1:], True)

180

181

182 class KanakoFaithKOF(Skill):

183 associated_action = None

184 skill_category = ['character', 'passive', 'compulsory']

185 target = t_None()

186

187

188 class KanakoFaithKOFAction(DrawCards):

189 pass

190

191

192 class KanakoFaithKOFHandler(THBEventHandler):

193 interested = ['action_before', 'action_apply']

194

195 def handle(self, evt_type, act):

196 if evt_type == 'action_before' and isinstance(act, FinalizeStage):

197 tgt = act.target

198 if not tgt.has_skill(KanakoFaithKOF):

199 return act

200

201 g = self.game

202 op = g.get_opponent(tgt)

203

204 if tgt.life > op.life or ttags(tgt)['kanako_faith_kof']:

205 n = tgt.life - (len(tgt.cards) + len(tgt.showncards))

206 if n > 0:

207 g.process_action(KanakoFaithKOFAction(tgt, n))

208

209 elif evt_type == 'action_apply' and isinstance(act, Damage):

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

211 g = self.game

212

213 if src and src.has_skill(KanakoFaithKOF) and tgt is g.get_opponent(src):

214 ttags(src)['kanako_faith_kof'] = True

215

216 return act

217

218

219 class Virtue(Skill):

220 associated_action = None

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

222 target = t_None()

223

224

225@register_character_to('common', '-kof')

226 class Kanako(Character):

227 skills = [Virtue, KanakoFaith]

228 eventhandlers = [VirtueHandler]

229 maxlife = 4

230

231

232# @register_character_to('kof')

233 class KanakoKOF(Character):

234 skills = [KanakoFaithKOF]

235 eventhandlers = [KanakoFaithKOFHandler]

236 maxlife = 4