Coverage for thb/characters/sp_aya.py : 25%
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 --
5# -- third party --
6# -- own --
7from game.base import ActionShootdown, InputTransaction
8from thb.actions import ActionStage, ActionStageLaunchCard, DrawCards, DropCardStage, FinalizeStage
9from thb.actions import GenericAction, LaunchCard, PlayerTurn, PrepareStage, ShowCards, UserAction
10from thb.actions import ask_for_action
11from thb.cards.base import Card, Skill, VirtualCard
12from thb.cards.classes import PhysicalCard, t_None, t_Self
13from thb.characters.base import Character, register_character_to
14from thb.inputlets import ChooseOptionInputlet
15from thb.mode import THBEventHandler
18# -- code --
19class WindWalkLaunch(ActionStageLaunchCard):
20 pass
23class WindWalkSkipAction(GenericAction):
24 def apply_action(self) -> bool:
25 g = self.game
26 ActionStage.force_break(g)
27 turn = PlayerTurn.get_current(g)
28 try:
29 turn.pending_stages.remove(DropCardStage)
30 turn.pending_stages.remove(FinalizeStage)
31 except Exception:
32 pass
34 return True
37class WindWalkAction(UserAction):
38 card_usage = 'launch'
40 def apply_action(self):
41 g = self.game
42 tgt = self.target
44 while True:
45 dc = DrawCards(tgt, 1)
46 g.process_action(dc)
47 c, = dc.cards
48 self.card = c
49 g.process_action(ShowCards(tgt, [c]))
51 with InputTransaction('ActionStageAction', [tgt]) as trans:
52 p, rst = ask_for_action(
53 self, [tgt], ('cards', 'showncards'), g.players, trans=trans
54 )
56 if p is not tgt:
57 g.process_action(WindWalkSkipAction(tgt, tgt))
58 break
60 assert p
61 assert rst
63 cl, tl = rst
64 g.players.reveal(cl)
65 c, = cl
67 g.process_action(WindWalkLaunch(p, tl, c))
69 return True
71 def cond(self, cl):
72 if not (cl and len(cl) == 1):
73 return False
75 return bool(cl[0].associated_action) and [self.card] == VirtualCard.unwrap(cl)
77 def ask_for_action_verify(self, p, cl, tl):
78 assert len(cl) == 1
79 return WindWalkLaunch(p, tl, cl[0]).can_fire()
81 def choose_player_target(self, tl):
82 return tl, True
85class WindWalkTargetLimit(ActionShootdown):
86 pass
89class WindWalkHandler(THBEventHandler):
90 interested = ['action_apply', 'action_shootdown']
92 def handle(self, evt_type, act):
93 if evt_type == 'action_apply' and isinstance(act, LaunchCard):
94 src = act.source
95 if act.card.is_card(WindWalk):
96 return act
98 if not src.has_skill(WindWalk):
99 return act
101 src.tags['windwalk_last_targets'] = set(act.target_list)
103 elif evt_type == 'action_apply' and isinstance(act, PrepareStage):
104 src = act.source
105 if not src.has_skill(WindWalk):
106 return act
108 src.tags['windwalk_last_targets'] = set()
110 elif evt_type == 'action_shootdown' and isinstance(act, LaunchCard):
111 g = self.game
112 if not isinstance(g.action_stack[-1], WindWalkAction):
113 return act
115 src, tl = act.source, set(act.target_list)
116 last_tl = src.tags['windwalk_last_targets'] or set()
118 if not tl <= last_tl:
119 raise WindWalkTargetLimit
121 return act
123 return act
126class WindWalk(Skill):
127 associated_action = WindWalkAction
128 skill_category = ['character', 'active']
129 target = t_Self()
130 usage = 'drop'
132 def check(self):
133 cl = self.associated_cards
134 if not(cl and len(cl) == 1):
135 return False
137 if cl[0].is_card(Skill):
138 return False
140 return True
143class Dominance(Skill):
144 associated_action = None
145 skill_category = ['character', 'passive']
146 target = t_None()
149class DominanceAction(PlayerTurn):
150 pass
153class DominanceHandler(THBEventHandler):
154 interested = ['action_after', 'action_apply']
156 def handle(self, evt_type, act):
157 if evt_type == 'action_apply' and isinstance(act, PrepareStage):
158 t = act.target.tags
159 t['dominance_suits'] = set()
160 t['dominance_suit_SPADE'] = False
161 t['dominance_suit_CLUB'] = False
162 t['dominance_suit_HEART'] = False
163 t['dominance_suit_DIAMOND'] = False
165 elif evt_type == 'action_apply' and isinstance(act, LaunchCard):
166 if not act.source.has_skill(Dominance):
167 return act
169 card = act.card
170 if not card.is_card(PhysicalCard):
171 return act
173 suit = card.suit
174 if suit not in (Card.SPADE, Card.CLUB, Card.HEART, Card.DIAMOND):
175 return act
177 try:
178 t = act.source.tags
179 t['dominance_suits'].add(suit)
180 t['dominance_suit_%s' % Card.SUIT_REV[suit]] = True
182 except AttributeError:
183 pass
185 elif evt_type == 'action_after' and isinstance(act, FinalizeStage):
186 tgt = act.target
187 if not tgt.has_skill(Dominance):
188 return act
190 if len(tgt.tags['dominance_suits'] or set()) != 4:
191 return act
193 g = self.game
194 if not g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):
195 return act
197 g.process_action(DominanceAction(tgt))
199 return act
202@register_character_to('imperial')
203class SpAya(Character):
204 skills = [WindWalk, Dominance]
205 eventhandlers = [WindWalkHandler, DominanceHandler]
206 maxlife = 4