Coverage for thb/characters/seiga.py : 98%
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 thb.actions import ActionStage, ForEach, GenericAction, LaunchCard, LifeLost, PlayerDeath
8from thb.actions import PlayerTurn, UserAction
9from thb.cards.base import Skill
10from thb.cards.classes import AttackCard, AttackCardVitalityHandler, t_None, t_Self
11from thb.characters.base import Character, register_character_to
12from thb.common import CharChoice
13from thb.inputlets import ChooseOptionInputlet
14from thb.mode import THBEventHandler
15from thb.thbkof import KOFCharacterSwitchHandler
18# -- code --
19class HeterodoxySkipAction(GenericAction):
20 def apply_action(self):
21 return True
24class HeterodoxyHandler(THBEventHandler):
25 interested = ['action_before']
26 execute_before = ['MaidenCostumeHandler']
28 def handle(self, evt_type, act):
29 if evt_type == 'action_before' and ForEach.is_group_effect(act):
30 tgt = act.target
31 if not tgt.has_skill(Heterodoxy): return act
33 g = self.game
34 for a in reversed(g.action_stack):
35 if isinstance(a, HeterodoxyAction):
36 break
37 else:
38 return act
40 if not g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):
41 return act
43 act.cancelled = True
44 g.process_action(HeterodoxySkipAction(tgt, tgt))
46 return act
49class HeterodoxyAction(UserAction):
50 def apply_action(self):
51 g = self.game
52 card = self.associated_card.associated_cards[0]
53 src = self.source
54 victim = self.target
55 tgts = self.target_list[1:]
57 g.players.reveal(card)
59 if card.is_card(AttackCard):
60 src.tags['vitality'] -= 1
62 # XXX: Use card owned by other
63 lc = LaunchCard(victim, tgts, card)
65 g = self.game
66 g.process_action(lc)
68 return True
70 def is_valid(self):
71 src = self.source
72 card = self.associated_card.associated_cards[0]
73 if card.is_card(AttackCard) and src.tags['vitality'] < 1:
74 if not AttackCardVitalityHandler.is_disabled(src): 74 ↛ 77line 74 didn't jump to line 77, because the condition on line 74 was never false
75 return False
77 if card.usage != 'launch': 77 ↛ 78line 77 didn't jump to line 78, because the condition on line 77 was never true
78 return False
80 victim = self.target
81 tgts = self.target_list[1:]
82 lc = LaunchCard(victim, tgts, card)
83 return lc.can_fire()
86class Heterodoxy(Skill):
87 no_drop = True
88 associated_action = HeterodoxyAction
89 skill_category = ['character', 'active']
90 usage = 'handover'
92 def check(self):
93 cl = self.associated_cards
94 return (
95 cl and len(cl) == 1 and
96 cl[0].resides_in.type in ('cards', 'showncards') and
97 not cl[0].is_card(Skill) and
98 getattr(cl[0], 'associated_action', None)
99 )
101 def target(self, src, tl):
102 cl = self.associated_cards
103 if not cl: return ([], False)
104 c = cl[0]
105 tname = c.target.__name__
107 tl = [t for t in tl if not t.dead]
109 if not tl: return [], False
110 if tl[0] is self.character: return [], False
112 if tname in ('t_Self', 't_All', 't_AllInclusive'): 112 ↛ 113line 112 didn't jump to line 113, because the condition on line 112 was never true
113 return tl[-1:], True
114 else:
115 _tl, valid = c.target(tl[0], tl[1:])
116 return [tl[0]] + _tl, valid
119class Summon(Skill):
120 associated_action = None
121 skill_category = ['character', 'passive', 'once']
122 target = t_None()
125class SummonAction(UserAction):
127 def apply_action(self):
128 src, tgt = self.source, self.target
130 skills = tgt.skills
131 skills = [s for s in skills if 'character' in s.skill_category]
132 skills = [s for s in skills if 'once' not in s.skill_category]
133 skills = [s for s in skills if 'awake' not in s.skill_category]
135 if not skills: return False 135 ↛ exitline 135 didn't return from function 'apply_action', because the return on line 135 wasn't executed
137 mapping = self.mapping = {s.__name__: s for s in skills}
138 names = list(sorted(mapping.keys()))
140 g = self.game
141 choice = g.user_input([src], ChooseOptionInputlet(self, names)) or names[0]
143 src.tags['summon_used'] = True
144 src.skills.append(mapping[choice])
145 self.choice = mapping[choice]
147 return True
150class SummonHandler(THBEventHandler):
151 interested = ['action_apply']
152 execute_after = ('DeathHandler', )
154 def handle(self, evt_type, act):
155 if evt_type == 'action_apply' and isinstance(act, PlayerDeath):
156 g = self.game
157 p = PlayerTurn.get_current(g).target
158 if p is act.target: return act
159 if not p.has_skill(Summon): return act
160 if p.tags['summon_used']: return act
161 if not g.user_input([p], ChooseOptionInputlet(self, (False, True))): return act
163 g.process_action(SummonAction(p, act.target))
165 return act
168class SummonKOFAction(UserAction):
169 def apply_action(self):
170 # WHOLE BUNCH OF MEGA HACK
171 old = self.target
172 g = self.game
173 old_life, maxlife_delta = old.life, old.maxlife - old.__class__.maxlife
175 ActionStage.force_break(g)
177 current = PlayerTurn.get_current(g).target
178 assert current is old
180 handler = g.dispatcher.find_by_cls(KOFCharacterSwitchHandler)
181 tgt = handler.switch(old)
183 tgt.life = old_life
184 tgt.maxlife += maxlife_delta
186 for l in ('cards', 'showncards', 'equips', 'fatetell', 'special'):
187 s, t = getattr(old, l), getattr(tgt, l)
188 for i in list(s):
189 i.move_to(t)
191 for s in old.skills:
192 if 'character' not in s.skill_category:
193 tgt.skills.append(s)
195 for act in g.action_stack:
196 # Meh... good enough
197 if act.source is old:
198 act.source = tgt
200 if act.target is old:
201 act.target = tgt
203 if isinstance(act, LaunchCard):
204 act.target_list = [
205 tgt if p is old else p
206 for p in act.target_list
207 ]
209 tgt.tags = old.tags
211 g.chosen[tgt.player].append(CharChoice(old.__class__))
213 if tgt.life > tgt.maxlife:
214 g.process_action(LifeLost(tgt, tgt, tgt.life - tgt.maxlife))
216 self.transition = [old, tgt]
218 g.emit_event('character_debut', (old, tgt))
220 return True
223class SummonKOFCollect(UserAction):
224 def apply_action(self):
225 src, tgt = self.source, self.target
226 g = self.game
227 g.chosen[src.player].append(CharChoice(tgt.__class__))
228 return True
231class SummonKOFHandler(THBEventHandler):
232 interested = ['action_apply']
234 def handle(self, evt_type, act):
235 if evt_type == 'action_apply' and isinstance(act, PlayerDeath):
236 g = self.game
237 src, tgt = act.source, act.target
238 p = g.get_opponent(tgt)
240 if not (src is p and p.has_skill(SummonKOF)):
241 return act
243 g.process_action(SummonKOFCollect(p, tgt))
245 return act
248class SummonKOF(Skill):
249 associated_action = SummonKOFAction
250 skill_category = ['character', 'active']
251 target = t_Self()
253 def check(self):
254 cl = self.associated_cards
255 return len(cl) == 0
258@register_character_to('common', '-kof')
259class Seiga(Character):
260 skills = [Heterodoxy, Summon]
261 eventhandlers = [HeterodoxyHandler, SummonHandler]
262 maxlife = 4
265@register_character_to('kof')
266class SeigaKOF(Character):
267 skills = [SummonKOF]
268 eventhandlers = [SummonKOFHandler]
269 maxlife = 4