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 Any, TYPE_CHECKING, Type 

6 

7# -- third party -- 

8# -- own -- 

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

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

11from thb.actions import user_choose_cards, user_choose_players 

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

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

14from thb.characters.base import Character, register_character_to 

15from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet 

16from thb.mode import THBEventHandler 

17 

18# -- typing -- 

19if TYPE_CHECKING: 

20 from thb.thbkof import THBattleKOF # noqa: F401 

21 

22 

23# -- code -- 

24class KanakoFaithCheers(UserAction): 

25 def apply_action(self): 

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

27 

28 

29class KanakoFaithAttack(TreatAs, VirtualCard): 

30 treat_as = AttackCard 

31 

32 

33class KanakoFaithDuel(TreatAs, VirtualCard): 

34 treat_as = DuelCard 

35 

36 

37class KanakoFaithCounteract(UserAction): 

38 def apply_action(self): 

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

40 g = self.game 

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

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

43 return True 

44 

45 

46class KanakoFaithCounteractPart1(UserAction): 

47 

48 def apply_action(self): 

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

50 g = self.game 

51 

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

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

54 

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

56 card = card or random_choose_card(g, cats) 

57 

58 assert card 

59 self.card = card 

60 

61 g.players.reveal(card) 

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

63 return True 

64 

65 

66class KanakoFaithCounteractPart2(UserAction): 

67 def apply_action(self): 

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

69 g = self.game 

70 

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

72 

73 cls: Type[Any] 

74 if choice == 'duel': 

75 cls = KanakoFaithDuel 

76 elif choice == 'attack': 

77 cls = KanakoFaithAttack 

78 else: 

79 cls = KanakoFaithAttack 

80 

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

82 

83 return True 

84 

85 

86class KanakoFaithEffect(UserAction): 

87 card_usage = 'drop' 

88 

89 def apply_action(self): 

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

91 g = self.game 

92 

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

94 

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

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

97 else: 

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

99 

100 return True 

101 

102 

103class KanakoFaithAction(ForEach): 

104 action_cls = KanakoFaithEffect 

105 

106 def prepare(self): 

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

108 

109 def is_valid(self): 

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

111 

112 

113class KanakoFaith(Skill): 

114 associated_action = KanakoFaithAction 

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

116 

117 def check(self): 

118 return not self.associated_cards 

119 

120 def target(self, p, tl): 

121 g = self.game 

122 l = g.players.rotate_to(p) 

123 del l[0] 

124 

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

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

127 

128 

129class VirtueAction(UserAction): 

130 card_usage = 'handover' 

131 

132 def apply_action(self): 

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

134 g = self.game 

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

136 

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

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

139 

140 if not c: return False 

141 

142 g.players.reveal(c) 

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

144 

145 self.card_shadow = Card.copy(c) 

146 

147 migrate_cards([c], src.cards) 

148 

149 if c.suit == Card.HEART: 

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

151 

152 return True 

153 

154 def cond(self, cl): 

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

156 

157 

158class VirtueHandler(THBEventHandler): 

159 interested = ['action_before'] 

160 

161 def handle(self, evt_type, act): 

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

163 if act.cancelled: 

164 return act 

165 

166 tgt = act.target 

167 if not tgt.has_skill(Virtue): 

168 return act 

169 

170 g = self.game 

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

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

173 if not pl: 

174 return act 

175 

176 act.cancelled = True 

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

178 

179 return act 

180 

181 def choose_player_target(self, tl): 

182 if not tl: 

183 return (tl, False) 

184 

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

186 

187 

188class KanakoFaithKOF(Skill): 

189 associated_action = None 

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

191 target = t_None() 

192 

193 

194class KanakoFaithKOFAction(DrawCards): 

195 pass 

196 

197 

198class KanakoFaithKOFHandler(THBEventHandler): 

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

200 game: THBattleKOF 

201 

202 def handle(self, evt_type, act): 

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

204 tgt = act.target 

205 if not tgt.has_skill(KanakoFaithKOF): 

206 return act 

207 

208 g = self.game 

209 op = g.get_opponent(tgt) 

210 

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

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

213 if n > 0: 

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

215 

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

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

218 g = self.game 

219 

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

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

222 

223 return act 

224 

225 

226class Virtue(Skill): 

227 associated_action = None 

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

229 target = t_None() 

230 

231 

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

233class Kanako(Character): 

234 skills = [Virtue, KanakoFaith] 

235 eventhandlers = [VirtueHandler] 

236 maxlife = 4 

237 

238 

239# @register_character_to('kof') 

240class KanakoKOF(Character): 

241 skills = [KanakoFaithKOF] 

242 eventhandlers = [KanakoFaithKOFHandler] 

243 maxlife = 4