Coverage for thb/characters/seija.py : 91%
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 Sequence, Tuple, List
6# -- third party --
7# -- own --
8from thb.actions import DrawCards, LaunchCard, Pindian, UserAction
9from thb.cards.base import Skill, VirtualCard
10from thb.cards.classes import AttackCard, BaseAttack, DuelCard, TreatAs, t_None
11from thb.characters.base import Character, register_character_to
12from thb.inputlets import ChooseOptionInputlet
13from thb.mode import THBEventHandler, THBattle
16# -- code --
17class InciteAttack(TreatAs, VirtualCard):
18 treat_as = AttackCard
20 def check(self):
21 return not self.associated_cards
24class InciteFailAttack(TreatAs, VirtualCard):
25 treat_as = AttackCard
26 distance = 99999
28 def check(self):
29 return not self.associated_cards
32class InciteSilentFailAction(UserAction):
33 def __init__(self, source, target):
34 self.source = source
35 self.target = target
37 def apply_action(self):
38 return True
41class InciteAction(UserAction):
42 def apply_action(self):
43 src = self.source
44 tags = src.tags
45 tgt, victim = self.target_list
47 tags['incite_tag'] = tags['turn_count']
49 g = self.game
50 if g.process_action(Pindian(src, tgt)):
51 g.process_action(LaunchCard(tgt, [victim], InciteAttack(tgt)))
53 else:
54 if g.user_input([tgt], ChooseOptionInputlet(self, (False, True))): 54 ↛ 57line 54 didn't jump to line 57, because the condition on line 54 was never false
55 g.process_action(LaunchCard(tgt, [src], InciteFailAttack(tgt)))
56 else:
57 g.process_action(InciteSilentFailAction(src, tgt))
59 return True
61 def is_valid(self):
62 src = self.source
63 tags = src.tags
64 if tags['turn_count'] <= tags['incite_tag']:
65 return False
67 tgt, victim = self.target_list
68 Pindian(src, tgt).action_shootdown_exception()
69 return LaunchCard(tgt, [victim], InciteAttack(tgt)).can_fire()
72class Incite(Skill):
73 associated_action = InciteAction
74 skill_category = ['character', 'active']
75 usage = 'none'
77 def target(self, src: Character, tl: Sequence[Character]) -> Tuple[List[Character], bool]:
78 tl = [t for t in tl if not t.dead and t is not src]
80 if not tl:
81 return ([], False)
83 tl_, valid = AttackCard().target(tl[0], tl[1:])
84 tl = tl[:1]
85 tl.extend(tl_)
86 return tl, valid
88 def check(self):
89 return not self.associated_cards
92class Reversal(Skill):
93 associated_action = None
94 skill_category = ['character', 'passive']
95 target = t_None()
98class ReversalDuel(TreatAs, VirtualCard):
99 treat_as = DuelCard
101 def check(self):
102 return not self.associated_cards
105class ReversalHandler(THBEventHandler):
106 interested = ['action_before']
107 execute_before = [
108 'HouraiJewelHandler',
109 'RejectHandler',
110 'FreakingPowerHandler',
111 'RoukankenEffectHandler',
112 ]
114 execute_after = ['DeathSickleHandler']
116 def handle(self, evt_type, act):
117 if evt_type == 'action_before' and isinstance(act, BaseAttack):
118 if hasattr(act, 'roukanken_tag'): 118 ↛ 119line 118 didn't jump to line 119, because the condition on line 118 was never true
119 return act
121 src = act.source
122 tgt = act.target
123 g = self.game
125 if not tgt.has_skill(Reversal):
126 return act
128 if not g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):
129 return act
131 def nhand(p):
132 return len(p.cards) + len(p.showncards)
134 g.process_action(DrawCards(tgt, 1))
135 if nhand(tgt) > nhand(src):
136 g.process_action(LaunchCard(src, [tgt], ReversalDuel(src)))
137 act.cancelled = True
139 return act
142@register_character_to('common', '-kof')
143class Seija(Character):
144 skills = [Incite, Reversal]
145 eventhandlers = [ReversalHandler]
146 maxlife = 3