Coverage for thb/characters/kokoro.py : 38%
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 ClassVar, Type
7# -- third party --
8# -- own --
9from thb.actions import ActionStage, DropCards, ShowCards, UserAction, migrate_cards
10from thb.actions import user_choose_cards
11from thb.cards.base import Card, Skill
12from thb.cards.classes import t_None, t_OtherOne
13from thb.characters.base import Character, register_character_to
14from thb.inputlets import ChooseOptionInputlet, HopeMaskInputlet, HopeMaskKOFInputlet, Inputlet
15from thb.mode import THBEventHandler, THBAction
18# -- code --
19class BaseHopeMaskAction(UserAction):
20 inputlet_cls: ClassVar[Type[Inputlet]]
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)
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()
33 assert not putback or set(putback) == set(g.deck.getcards(len(putback)))
35 deck = g.deck.cards
36 for i, c in enumerate(putback):
37 deck[i] = c
39 assert not putback or putback == g.deck.getcards(len(putback))
41 if acquire:
42 g.process_action(ShowCards(tgt, acquire))
43 migrate_cards(acquire, tgt.cards)
45 self.acquire = acquire
47 return True
50class HopeMaskAction(BaseHopeMaskAction):
51 inputlet_cls = HopeMaskInputlet
54class HopeMaskKOFAction(BaseHopeMaskAction):
55 inputlet_cls = HopeMaskKOFInputlet
58class BaseHopeMaskHandler(THBEventHandler):
59 interested = ['action_apply']
60 skill: ClassVar[Type[Skill]]
61 action: ClassVar[Type[THBAction]]
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))
72 return act
75class HopeMask(Skill):
76 associated_action = None
77 skill_category = ['character', 'passive']
78 target = t_None()
81class HopeMaskKOF(Skill):
82 associated_action = None
83 skill_category = ['character', 'passive']
84 target = t_None()
87class HopeMaskHandler(BaseHopeMaskHandler):
88 skill = HopeMask
89 action = HopeMaskAction
92class HopeMaskKOFHandler(BaseHopeMaskHandler):
93 skill = HopeMaskKOF
94 action = HopeMaskKOFAction
97class BaseDarkNohAction(UserAction):
98 card_usage = 'drop'
100 def apply_action(self):
101 src, tgt = self.source, self.target
102 g = self.game
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
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
121 cards = cl[:n]
123 g.players.reveal(cards)
124 g.process_action(DropCards(src, tgt, cards))
126 return True
128 def cond(self, cards):
129 if len(cards) != self.n or self.card in cards:
130 return False
132 if any([c.is_card(Skill) for c in cards]):
133 return False
135 return True
138class DarkNohAction(BaseDarkNohAction):
140 def is_valid(self):
141 src = self.source
142 tgt = self.target
144 if src.tags['darknoh_tag'] >= src.tags['turn_count']:
145 return False
147 if src.life > tgt.life:
148 return False
150 return True
153class DarkNohKOFAction(BaseDarkNohAction):
155 def is_valid(self):
156 src = self.source
157 tgt = self.target
159 if src.tags['darknoh_tag'] > 0:
160 return False
162 if src.life > tgt.life:
163 return False
165 return True
168class BaseDarkNoh(Skill):
169 no_drop = True
170 skill_category = ['character', 'active']
171 target = t_OtherOne()
172 usage = 'handover'
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
184class DarkNoh(BaseDarkNoh):
185 associated_action = DarkNohAction
188class DarkNohKOF(BaseDarkNoh):
189 associated_action = DarkNohKOFAction
192@register_character_to('common', '-kof')
193class Kokoro(Character):
194 skills = [HopeMask, DarkNoh]
195 eventhandlers = [HopeMaskHandler]
196 maxlife = 3
199@register_character_to('kof')
200class KokoroKOF(Character):
201 skills = [HopeMaskKOF, DarkNohKOF]
202 eventhandlers = [HopeMaskKOFHandler]
203 maxlife = 3