Coverage for thb/characters/yuuka.py : 83%
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 --
4# -- third party --
5# -- own --
6from thb.actions import Damage, DrawCards, ForEach, LaunchCard, PlayerDeath, UserAction, PlayerTurn
7from thb.actions import user_choose_players
8from thb.cards.base import Skill
9from thb.cards.classes import AttackCard, Duel, InstantSpellCardAction, Reject, TreatAs, t_None
10from thb.characters.base import Character, register_character_to
11from thb.inputlets import ChooseOptionInputlet
12from thb.mode import THBEventHandler
15# -- code --
16class ReversedScales(TreatAs, Skill):
17 skill_category = ['character', 'active', 'compulsory']
18 treat_as = AttackCard
20 def check(self):
21 cl = self.associated_cards
22 if not cl or len(cl) != 1:
23 return False
25 g = self.game
26 if PlayerTurn.get_current(g).target is self.character:
27 return False
29 return cl[0].resides_in.type in ('cards', 'showncards')
32class Sadist(Skill):
33 associated_action = None
34 skill_category = ['character', 'passive']
35 target = t_None()
38class SadistKOF(Skill):
39 associated_action = None
40 skill_category = ['character', 'passive']
41 target = t_None()
44class SadistKOFDrawCards(DrawCards):
45 pass
48class SadistKOFDamageAction(UserAction):
49 def apply_action(self):
50 g = self.game
51 src, tgt = self.source, self.target
52 return g.process_action(Damage(src, tgt, 1))
55class SadistKOFHandler(THBEventHandler):
56 interested = ['action_after', 'character_debut']
57 execute_after = ['DeathHandler']
59 def handle(self, evt_type, arg):
60 if evt_type == 'action_after' and isinstance(arg, PlayerDeath):
61 tgt = arg.target
62 g = self.game
63 op = g.get_opponent(tgt)
64 if arg.source is op and op.has_skill(SadistKOF):
65 g.process_action(SadistKOFDrawCards(op, 2))
66 op.tags['sadist_kof_fire'] = True
68 elif evt_type == 'character_debut':
69 old, new = arg
70 if not old: return arg
72 g = self.game
73 op = g.get_opponent(new)
75 if op.has_skill(SadistKOF) and op.tags['sadist_kof_fire']:
76 op.tags['sadist_kof_fire'] = False
77 g.process_action(SadistKOFDamageAction(op, new))
79 return arg
82class ReversedScalesAction(UserAction):
83 def __init__(self, target, action):
84 self.source = target
85 self.target = target
86 self.action = action
88 def apply_action(self):
89 self.action.__class__ = Duel
90 return True
93class ReversedScalesHandler(THBEventHandler):
94 interested = ['action_before']
95 execute_before = ['MaidenCostumeHandler']
97 def handle(self, evt_type, act):
98 if evt_type != 'action_before': 98 ↛ 99line 98 didn't jump to line 99, because the condition on line 98 was never true
99 return act
101 if not isinstance(act, InstantSpellCardAction):
102 return act
104 if isinstance(act, Reject):
105 # HACK
106 return act
108 if ForEach.is_group_effect(act):
109 return act
111 src = act.source
112 tgt = act.target
114 if not src or tgt is src:
115 return act
117 if not tgt.has_skill(ReversedScales):
118 return act
120 g = self.game
121 if not g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):
122 return act
124 g.process_action(ReversedScalesAction(tgt, act))
126 return act
129class SadistAction(UserAction):
130 def __init__(self, source, target):
131 self.source = source
132 self.target = target
134 def apply_action(self):
135 g = self.game
136 src, tgt = self.source, self.target
137 tgt.tags['sadist_target'] = False
138 g.process_action(Damage(src, tgt, 1))
139 return True
142class SadistHandler(THBEventHandler):
143 interested = ['action_after', 'action_before']
144 card_usage = 'drop'
145 execute_before = ['WineHandler']
146 execute_after = ['DeathHandler']
148 def handle(self, evt_type, act):
149 if evt_type == 'action_after' and isinstance(act, PlayerDeath):
150 src = act.source
151 if not src or not src.has_skill(Sadist):
152 return act
154 g = self.game
155 dist = LaunchCard.calc_distance(g, src, AttackCard())
156 candidates = [k for k, v in dist.items() if v <= 0 and k is not src]
158 if not candidates:
159 return act
161 pl = user_choose_players(self, src, candidates)
162 if pl: 162 ↛ 178line 162 didn't jump to line 178, because the condition on line 162 was never false
163 self.game.process_action(SadistAction(src, pl[0]))
165 elif evt_type == 'action_before' and isinstance(act, Damage):
166 src = act.source
167 tgt = act.target
169 if not src or src is tgt:
170 return act
172 if not src.has_skill(Sadist):
173 return act
175 if tgt.life == 1:
176 act.amount += 1
178 return act
180 def choose_player_target(self, pl):
181 return pl[-1:], len(pl)
184@register_character_to('common', '-kof')
185class Yuuka(Character):
186 skills = [ReversedScales, Sadist]
187 eventhandlers = [ReversedScalesHandler, SadistHandler]
188 maxlife = 4
191@register_character_to('kof')
192class YuukaKOF(Character):
193 skills = [ReversedScales, SadistKOF]
194 eventhandlers = [ReversedScalesHandler, SadistKOFHandler]
195 maxlife = 4