Coverage for thb/characters/kanako.py : 82%
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 -*-
3# -- stdlib --
4from typing import Any, Type
6# -- third party --
7# -- own --
8from thb.actions import Damage, DrawCardStage, DrawCards, DropCards, FinalizeStage, ForEach
9from thb.actions import LaunchCard, ShowCards, UserAction, migrate_cards, random_choose_card, ttags
10from thb.actions import user_choose_cards, user_choose_players
11from thb.cards.base import Card, Skill, VirtualCard
12from thb.cards.classes import AttackCard, DuelCard, TreatAs, t_None
13from thb.characters.base import Character, register_character_to
14from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet
15from thb.mode import THBEventHandler
18# -- code --
19class KanakoFaithCheers(UserAction):
20 def apply_action(self):
21 return self.game.process_action(DrawCards(self.target, 1))
24class KanakoFaithAttack(TreatAs, VirtualCard):
25 treat_as = AttackCard
28class KanakoFaithDuel(TreatAs, VirtualCard):
29 treat_as = DuelCard
32class 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
41class KanakoFaithCounteractPart1(UserAction):
43 def apply_action(self):
44 src, tgt = self.source, self.target
45 g = self.game
47 catnames = ('cards', 'showncards', 'equips')
48 cats = [getattr(tgt, i) for i in catnames]
50 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames))
51 card = card or random_choose_card(g, cats)
53 assert card
54 self.card = card
56 g.players.reveal(card)
57 g.process_action(DropCards(src, tgt, [card]))
58 return True
61class KanakoFaithCounteractPart2(UserAction):
62 def apply_action(self):
63 src, tgt = self.source, self.target
64 g = self.game
66 choice = g.user_input([tgt], ChooseOptionInputlet(self, ('duel', 'attack')))
68 cls: Type[Any]
69 if choice == 'duel':
70 cls = KanakoFaithDuel
71 elif choice == 'attack':
72 cls = KanakoFaithAttack
73 else:
74 cls = KanakoFaithAttack
76 g.process_action(LaunchCard(tgt, [src], cls(tgt), bypass_check=True))
78 return True
81class KanakoFaithEffect(UserAction):
82 card_usage = 'drop'
84 def apply_action(self):
85 src, tgt = self.source, self.target
86 g = self.game
88 has_card = src.cards or src.showncards or src.equips
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))
95 return True
98class KanakoFaithAction(ForEach):
99 action_cls = KanakoFaithEffect
101 def prepare(self):
102 self.source.tags['kanako_faith'] = True
104 def is_valid(self):
105 return not self.source.tags['kanako_faith']
108class KanakoFaith(Skill):
109 associated_action = KanakoFaithAction
110 skill_category = ['character', 'active', 'once']
112 def check(self):
113 return not self.associated_cards
115 def target(self, p, tl):
116 g = self.game
117 l = g.players.rotate_to(p)
118 del l[0]
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)
124class VirtueAction(UserAction):
125 card_usage = 'handover'
127 def apply_action(self):
128 src, tgt = self.source, self.target
129 g = self.game
130 g.process_action(DrawCards(tgt, 2))
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])
135 if not c: return False 135 ↛ exitline 135 didn't return from function 'apply_action', because the return on line 135 wasn't executed
137 g.players.reveal(c)
138 g.process_action(ShowCards(tgt, [c]))
139 migrate_cards([c], src.cards)
141 if c.suit == Card.HEART:
142 g.process_action(DrawCards(src, 1))
144 self.card = c
146 return True
148 def cond(self, cl):
149 return len(cl) == 1 and not cl[0].is_card(VirtualCard)
152class VirtueHandler(THBEventHandler):
153 interested = ['action_before']
155 def handle(self, evt_type, act):
156 if evt_type == 'action_before' and isinstance(act, DrawCardStage):
157 if act.cancelled: 157 ↛ 158line 157 didn't jump to line 158, because the condition on line 157 was never true
158 return act
160 tgt = act.target
161 if not tgt.has_skill(Virtue):
162 return act
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: 167 ↛ 168line 167 didn't jump to line 168, because the condition on line 167 was never true
168 return act
170 act.cancelled = True
171 g.process_action(VirtueAction(tgt, pl[0]))
173 return act
175 def choose_player_target(self, tl):
176 if not tl: 176 ↛ 177line 176 didn't jump to line 177, because the condition on line 176 was never true
177 return (tl, False)
179 return (tl[-1:], True)
182class KanakoFaithKOF(Skill):
183 associated_action = None
184 skill_category = ['character', 'passive', 'compulsory']
185 target = t_None()
188class KanakoFaithKOFAction(DrawCards):
189 pass
192class KanakoFaithKOFHandler(THBEventHandler):
193 interested = ['action_before', 'action_apply']
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
201 g = self.game
202 op = g.get_opponent(tgt)
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))
209 elif evt_type == 'action_apply' and isinstance(act, Damage):
210 src, tgt = act.source, act.target
211 g = self.game
213 if src and src.has_skill(KanakoFaithKOF) and tgt is g.get_opponent(src):
214 ttags(src)['kanako_faith_kof'] = True
216 return act
219class Virtue(Skill):
220 associated_action = None
221 skill_category = ['character', 'passive']
222 target = t_None()
225@register_character_to('common', '-kof')
226class Kanako(Character):
227 skills = [Virtue, KanakoFaith]
228 eventhandlers = [VirtueHandler]
229 maxlife = 4
232# @register_character_to('kof')
233class KanakoKOF(Character):
234 skills = [KanakoFaithKOF]
235 eventhandlers = [KanakoFaithKOFHandler]
236 maxlife = 4