Coverage for thb/characters/kanako.py : 35%
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
4# -- stdlib --
5from typing import Any, TYPE_CHECKING, Type
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
18# -- typing --
19if TYPE_CHECKING:
20 from thb.thbkof import THBattleKOF # noqa: F401
23# -- code --
24class KanakoFaithCheers(UserAction):
25 def apply_action(self):
26 return self.game.process_action(DrawCards(self.target, 1))
29class KanakoFaithAttack(TreatAs, VirtualCard):
30 treat_as = AttackCard
33class KanakoFaithDuel(TreatAs, VirtualCard):
34 treat_as = DuelCard
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
46class KanakoFaithCounteractPart1(UserAction):
48 def apply_action(self):
49 src, tgt = self.source, self.target
50 g = self.game
52 catnames = ('cards', 'showncards', 'equips')
53 cats = [getattr(tgt, i) for i in catnames]
55 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames))
56 card = card or random_choose_card(g, cats)
58 assert card
59 self.card = card
61 g.players.reveal(card)
62 g.process_action(DropCards(src, tgt, [card]))
63 return True
66class KanakoFaithCounteractPart2(UserAction):
67 def apply_action(self):
68 src, tgt = self.source, self.target
69 g = self.game
71 choice = g.user_input([tgt], ChooseOptionInputlet(self, ('duel', 'attack')))
73 cls: Type[Any]
74 if choice == 'duel':
75 cls = KanakoFaithDuel
76 elif choice == 'attack':
77 cls = KanakoFaithAttack
78 else:
79 cls = KanakoFaithAttack
81 g.process_action(LaunchCard(tgt, [src], cls(tgt), bypass_check=True))
83 return True
86class KanakoFaithEffect(UserAction):
87 card_usage = 'drop'
89 def apply_action(self):
90 src, tgt = self.source, self.target
91 g = self.game
93 has_card = src.cards or src.showncards or src.equips
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))
100 return True
103class KanakoFaithAction(ForEach):
104 action_cls = KanakoFaithEffect
106 def prepare(self):
107 self.source.tags['kanako_faith'] = True
109 def is_valid(self):
110 return not self.source.tags['kanako_faith']
113class KanakoFaith(Skill):
114 associated_action = KanakoFaithAction
115 skill_category = ['character', 'active', 'once']
117 def check(self):
118 return not self.associated_cards
120 def target(self, p, tl):
121 g = self.game
122 l = g.players.rotate_to(p)
123 del l[0]
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)
129class VirtueAction(UserAction):
130 card_usage = 'handover'
132 def apply_action(self):
133 src, tgt = self.source, self.target
134 g = self.game
135 g.process_action(DrawCards(tgt, 2))
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])
140 if not c: return False
142 g.players.reveal(c)
143 g.process_action(ShowCards(tgt, [c]))
145 self.card_shadow = Card.copy(c)
147 migrate_cards([c], src.cards)
149 if c.suit == Card.HEART:
150 g.process_action(DrawCards(src, 1))
152 return True
154 def cond(self, cl):
155 return len(cl) == 1 and not cl[0].is_card(VirtualCard)
158class VirtueHandler(THBEventHandler):
159 interested = ['action_before']
161 def handle(self, evt_type, act):
162 if evt_type == 'action_before' and isinstance(act, DrawCardStage):
163 if act.cancelled:
164 return act
166 tgt = act.target
167 if not tgt.has_skill(Virtue):
168 return act
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
176 act.cancelled = True
177 g.process_action(VirtueAction(tgt, pl[0]))
179 return act
181 def choose_player_target(self, tl):
182 if not tl:
183 return (tl, False)
185 return (tl[-1:], True)
188class KanakoFaithKOF(Skill):
189 associated_action = None
190 skill_category = ['character', 'passive', 'compulsory']
191 target = t_None()
194class KanakoFaithKOFAction(DrawCards):
195 pass
198class KanakoFaithKOFHandler(THBEventHandler):
199 interested = ['action_before', 'action_apply']
200 game: THBattleKOF
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
208 g = self.game
209 op = g.get_opponent(tgt)
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))
216 elif evt_type == 'action_apply' and isinstance(act, Damage):
217 src, tgt = act.source, act.target
218 g = self.game
220 if src and src.has_skill(KanakoFaithKOF) and tgt is g.get_opponent(src):
221 ttags(src)['kanako_faith_kof'] = True
223 return act
226class Virtue(Skill):
227 associated_action = None
228 skill_category = ['character', 'passive']
229 target = t_None()
232@register_character_to('common', '-kof')
233class Kanako(Character):
234 skills = [Virtue, KanakoFaith]
235 eventhandlers = [VirtueHandler]
236 maxlife = 4
239# @register_character_to('kof')
240class KanakoKOF(Character):
241 skills = [KanakoFaithKOF]
242 eventhandlers = [KanakoFaithKOFHandler]
243 maxlife = 4