# Coverage for thb/characters/kokoro.py : 38%

## 136 statements 63 run 73 missing 0 excluded 0 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 ClassVar, Type

6

7# -- third party --

8# -- own --

9 from thb.actions import ActionStage, DropCards, ShowCards, UserAction, migrate_cards

10 from thb.actions import user_choose_cards

11 from thb.cards.base import Card, Skill

12 from thb.cards.classes import t_None, t_OtherOne

13 from thb.characters.base import Character, register_character_to

14 from thb.inputlets import ChooseOptionInputlet, HopeMaskInputlet, HopeMaskKOFInputlet, Inputlet

15 from thb.mode import THBEventHandler, THBAction

16

17

18# -- code --

19 class BaseHopeMaskAction(UserAction):

20 inputlet_cls: ClassVar[Type[Inputlet]]

21

22 def apply_action(self):

23 tgt = self.target

24 g = self.game

25 n = 1 + tgt.maxlife - tgt.life

26 cards = g.deck.getcards(n)

27

28 tgt.reveal(cards)

29 putback, acquire = g.user_input([tgt], self.inputlet_cls(self, cards), timeout=20)

30 for c in acquire:

31 c.detach()

32

33 assert not putback or set(putback) == set(g.deck.getcards(len(putback)))

34

35 deck = g.deck.cards

36 for i, c in enumerate(putback):

37 deck[i] = c

38

39 assert not putback or putback == g.deck.getcards(len(putback))

40

41 if acquire:

42 g.process_action(ShowCards(tgt, acquire))

43 migrate_cards(acquire, tgt.cards)

44

45 self.acquire = acquire

46

47 return True

48

49

50 class HopeMaskAction(BaseHopeMaskAction):

51 inputlet_cls = HopeMaskInputlet

52

53

54 class HopeMaskKOFAction(BaseHopeMaskAction):

55 inputlet_cls = HopeMaskKOFInputlet

56

57

58 class BaseHopeMaskHandler(THBEventHandler):

59 interested = ['action_apply']

60 skill: ClassVar[Type[Skill]]

61 action: ClassVar[Type[THBAction]]

62

63 def handle(self, evt_type, act):

64 if evt_type == 'action_apply' and isinstance(act, ActionStage):

65 tgt = act.target

66 if not tgt.has_skill(self.skill): return act

67 g = self.game

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

69 return act

70 g.process_action(self.action(tgt, tgt))

71

72 return act

73

74

75 class HopeMask(Skill):

76 associated_action = None

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

78 target = t_None()

79

80

81 class HopeMaskKOF(Skill):

82 associated_action = None

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

84 target = t_None()

85

86

87 class HopeMaskHandler(BaseHopeMaskHandler):

88 skill = HopeMask

89 action = HopeMaskAction

90

91

92 class HopeMaskKOFHandler(BaseHopeMaskHandler):

93 skill = HopeMaskKOF

94 action = HopeMaskKOFAction

95

96

97 class BaseDarkNohAction(UserAction):

98 card_usage = 'drop'

99

100 def apply_action(self):

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

102 g = self.game

103

104 src.tags['darknoh_tag'] = src.tags['turn_count']

105 sk = self.associated_card

106 assert isinstance(sk, (DarkNoh, DarkNohKOF))

107 card = sk.associated_cards[0]

108 self.card = card

109 migrate_cards([sk], tgt.showncards, unwrap=True)

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

111 if n <= 0: return True

112

113 cards = user_choose_cards(self, tgt, ('cards', 'showncards'))

114 if not cards:

115 cl = list(tgt.cards) + list(tgt.showncards)

116 try:

117 cl.remove(card)

118 except Exception:

119 pass

120

121 cards = cl[:n]

122

123 g.players.reveal(cards)

124 g.process_action(DropCards(src, tgt, cards))

125

126 return True

127

128 def cond(self, cards):

129 if len(cards) != self.n or self.card in cards:

130 return False

131

132 if any([c.is_card(Skill) for c in cards]):

133 return False

134

135 return True

136

137

138 class DarkNohAction(BaseDarkNohAction):

139

140 def is_valid(self):

141 src = self.source

142 tgt = self.target

143

144 if src.tags['darknoh_tag'] >= src.tags['turn_count']:

145 return False

146

147 if src.life > tgt.life:

148 return False

149

150 return True

151

152

153 class DarkNohKOFAction(BaseDarkNohAction):

154

155 def is_valid(self):

156 src = self.source

157 tgt = self.target

158

159 if src.tags['darknoh_tag'] > 0:

160 return False

161

162 if src.life > tgt.life:

163 return False

164

165 return True

166

167

168 class BaseDarkNoh(Skill):

169 no_drop = True

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

171 target = t_OtherOne()

172 usage = 'handover'

173

174 def check(self):

175 cards = self.associated_cards

176 if len(cards) != 1: return False

177 c = cards[0]

178 if c.resides_in is None: return False

179 if c.resides_in.type not in ('cards', 'showncards', 'equips'): return False

180 if c.suit not in (Card.SPADE, Card.CLUB): return False

181 return True

182

183

184 class DarkNoh(BaseDarkNoh):

185 associated_action = DarkNohAction

186

187

188 class DarkNohKOF(BaseDarkNoh):

189 associated_action = DarkNohKOFAction

190

191

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

193 class Kokoro(Character):

194 skills = [HopeMask, DarkNoh]

195 eventhandlers = [HopeMaskHandler]

196 maxlife = 3

197

198

199@register_character_to('kof')

200 class KokoroKOF(Character):

201 skills = [HopeMaskKOF, DarkNohKOF]

202 eventhandlers = [HopeMaskKOFHandler]

203 maxlife = 3