Coverage for thb/characters/shizuha.py : 93%
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 cast
7# -- third party --
8# -- own --
9# -- errord --
10from thb.actions import Damage, DrawCards, DropCardStage, DropCards, GenericAction
11from thb.actions import MigrateCardsTransaction, PlayerTurn, UserAction, random_choose_card
12from thb.actions import user_choose_players
13from thb.cards.base import Skill, VirtualCard
14from thb.cards.classes import t_None
15from thb.characters.base import Character, register_character_to
16from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet
17from thb.mode import THBEventHandler
20# -- code --
21class AutumnWindEffect(GenericAction):
22 def apply_action(self):
23 src, tgt = self.source, self.target
25 g = self.game
27 catnames = ('cards', 'showncards', 'equips')
28 cats = [getattr(tgt, i) for i in catnames]
29 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames))
30 card = card or random_choose_card(g, cats)
31 if not card: 31 ↛ 32line 31 didn't jump to line 32, because the condition on line 31 was never true
32 return False
34 self.card = card
35 g.players.exclude(tgt).reveal(card)
36 g.process_action(DropCards(src, tgt, cards=[card]))
37 return True
39 def is_valid(self):
40 tgt = self.target
41 catnames = ('cards', 'showncards', 'equips')
42 return not tgt.dead and any(getattr(tgt, i) for i in catnames) 42 ↛ exitline 42 didn't finish the generator expression on line 42
45class AutumnWindAction(UserAction):
46 def __init__(self, source, target_list):
47 self.source = source
48 self.target = None
49 self.target_list = target_list
51 def apply_action(self):
52 g = self.game
53 src = self.source
55 for p in self.target_list:
56 g.process_action(AutumnWindEffect(src, p))
58 return True
61class AutumnWindHandler(THBEventHandler):
62 interested = ['action_after']
64 def handle(self, evt_type, act):
65 if evt_type == 'action_after' and isinstance(act, DropCardStage):
66 self.n = n = act.dropn
67 if n <= 0:
68 return act
70 tgt = act.target
71 if not tgt.has_skill(AutumnWind):
72 return act
74 g = self.game
75 if not g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):
76 return act
78 candidates = [
79 p for p in g.players if
80 p is not tgt and
81 (p.cards or p.showncards or p.equips) and
82 not p.dead
83 ]
85 pl = candidates and user_choose_players(self, tgt, candidates)
86 if not pl: 86 ↛ 87line 86 didn't jump to line 87, because the condition on line 86 was never true
87 return act
89 g.process_action(AutumnWindAction(tgt, pl))
91 return act
93 def choose_player_target(self, tl):
94 if not tl: 94 ↛ 95line 94 didn't jump to line 95, because the condition on line 94 was never true
95 return (tl, False)
97 return (tl[:self.n], True)
100class AutumnWind(Skill):
101 associated_action = None
102 skill_category = ['character', 'passive']
103 target = t_None()
106class DecayDrawCards(DrawCards):
107 pass
110class DecayDrawCardHandler(THBEventHandler):
111 interested = ['post_card_migration']
112 execute_before = ['LuckHandler']
114 def handle(self, evt_type, arg):
115 if evt_type != 'post_card_migration': 115 ↛ 116line 115 didn't jump to line 116, because the condition on line 115 was never true
116 return arg
118 g = self.game
120 try:
121 me = PlayerTurn.get_current(g).target
122 except IndexError:
123 return arg
125 if me is None: return arg 125 ↛ exitline 125 didn't return from function 'handle', because the return on line 125 wasn't executed
126 if me.dead: return arg
127 if not me.has_skill(Decay): return arg
129 trans = cast(MigrateCardsTransaction, arg)
131 candidates = {}
133 for m in trans.movements:
134 src = m.fr.owner
135 if not src: continue
136 if m.card.is_card(VirtualCard): continue 136 ↛ 133line 136 didn't jump to line 133, because the continue on line 136 wasn't executed
137 if m.fr.type not in ('cards', 'showncards'): continue
138 if src.cards or src.showncards: continue
139 if src.dead: continue
140 if src is me: continue
141 candidates[src] = 1
143 if not candidates:
144 return arg
146 g.process_action(DecayDrawCards(me, len(candidates)))
148 return arg
151class DecayAction(UserAction):
152 def apply_action(self):
153 tgt = self.target
154 tgt.tags['shizuha_decay'] = True
155 return True
158class DecayEffect(UserAction):
159 def __init__(self, source, target, dcs):
160 self.source = source
161 self.target = target
162 self.dcs = dcs
164 def apply_action(self):
165 self.dcs.dropn = max(1, self.dcs.dropn + 1)
166 return True
169class DecayDamageHandler(THBEventHandler):
170 interested = ('action_after', 'action_before')
171 execute_after = (
172 'DyingHandler',
173 'AyaRoundfanHandler',
174 'NenshaPhoneHandler',
175 'SuwakoHatHandler',
176 )
178 def handle(self, evt_type, act):
179 if evt_type == 'action_after' and isinstance(act, Damage):
180 src, tgt = act.source, act.target
181 if not (tgt and tgt.has_skill(Decay)):
182 return act
184 g = self.game
186 try:
187 cur = PlayerTurn.get_current(g).target
188 except IndexError:
189 # Yugi AssaultKOF & Yuuka Sadist
190 return act
192 if cur is tgt: return act
193 g.process_action(DecayAction(src, cur))
195 elif evt_type == 'action_before' and isinstance(act, DropCardStage):
196 tgt = act.target
197 t = tgt.tags
198 if not t['shizuha_decay']: return act
200 t['shizuha_decay'] = False
201 g = self.game
202 g.process_action(DecayEffect(tgt, tgt, act))
204 return act
207class DecayFadeHandler(THBEventHandler):
208 interested = ('action_after', )
210 def handle(self, evt_type, act):
211 if evt_type == 'action_after' and isinstance(act, PlayerTurn):
212 tgt = act.target
213 if tgt.tags['shizuha_decay']: 213 ↛ 214line 213 didn't jump to line 214, because the condition on line 213 was never true
214 tgt.tags['shizuha_decay'] = False
216 return act
219class Decay(Skill):
220 associated_action = None
221 skill_category = ['character', 'passive']
222 target = t_None()
225@register_character_to('common')
226class Shizuha(Character):
227 skills = [Decay, AutumnWind]
228 eventhandlers = [DecayDamageHandler, DecayDrawCardHandler, DecayFadeHandler, AutumnWindHandler]
229 maxlife = 3