Coverage for thb/characters/remilia.py : 69%
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, Any
5# -- third party --
6# -- own --
7from thb.actions import ActionShootdown, Damage, GenericAction, LaunchCard, PrepareStage, UserAction
8from thb.cards.base import Card, DummyCard, Skill, t_None
9from thb.cards.classes import Attack, AttackCard, Heal, InevitableAttack
10from thb.characters.base import Character, register_character_to
11from thb.inputlets import ChooseOptionInputlet
12from thb.mode import THBEventHandler
15# -- code --
16class SpearTheGungnir(Skill):
17 associated_action = None
18 skill_category = ['character', 'passive']
19 target = t_None()
22class SpearTheGungnirAction(GenericAction):
23 def __init__(self, act):
24 self.act = act
25 self.source = act.source
26 self.target = act.target
28 def apply_action(self):
29 self.act.__class__ = InevitableAttack
30 return True
33class SpearTheGungnirHandler(THBEventHandler):
34 interested = ['action_before']
35 execute_before = ['ScarletRhapsodySwordHandler']
36 execute_after = ['HakuroukenEffectHandler', 'HouraiJewelHandler']
38 def handle(self, evt_type, act):
39 if evt_type == 'action_before' and isinstance(act, Attack):
40 src = act.source
41 if not src.has_skill(SpearTheGungnir): return act
42 if isinstance(act, InevitableAttack): return act 42 ↛ exitline 42 didn't return from function 'handle', because the return on line 42 wasn't executed
44 tgt = act.target
46 while True:
47 if tgt.life > src.life: break
48 if len(tgt.cards) + len(tgt.showncards) < len(src.cards) + len(src.showncards): break
49 return act
51 g = self.game
52 if g.user_input([act.source], ChooseOptionInputlet(self, (False, True))):
53 g.process_action(SpearTheGungnirAction(act))
55 return act
58class VampireKiss(Skill):
59 associated_action = None
60 skill_category = ['character', 'passive', 'compulsory']
61 target = t_None()
64class VampireKissAction(GenericAction):
65 def apply_action(self):
66 return self.game.process_action(
67 Heal(self.target, self.source)
68 )
71class VampireKissHandler(THBEventHandler):
72 interested = ['action_apply', 'calcdistance']
74 def handle(self, evt_type, act):
75 if evt_type == 'action_apply' and isinstance(act, Damage):
76 src, tgt = act.source, act.target
77 if not (src and src.has_skill(VampireKiss)): return act
78 if src.life >= src.maxlife: return act
79 g = self.game
80 pact = g.action_stack[-1]
81 if not isinstance(pact, Attack): return act
82 card = pact.associated_card
83 if (not card) or card.color != Card.RED: return act
84 g.process_action(VampireKissAction(src, tgt))
86 elif evt_type == 'calcdistance':
87 src, card, dist = act
89 if not card.is_card(AttackCard): return act
90 if not src.has_skill(VampireKiss): return act
91 if card.color != Card.RED: return act
93 for p in dist:
94 dist[p] -= 99999
96 return act
99class ScarletMistAttackLimit(ActionShootdown):
100 pass
103class ScarletMistHandler(THBEventHandler):
104 interested = [
105 'action_after',
106 'action_apply',
107 'action_shootdown',
108 'post_calcdistance',
109 ]
111 def handle(self, evt_type, act):
112 if evt_type == 'action_shootdown' and isinstance(act, LaunchCard):
113 if act.bypass_check: return act
114 c = act.card
115 if not c.is_card(AttackCard): return act
117 src, tgt = act.source, act.target
118 if not src.tags['scarlet_mist'] == 'nerf': 118 ↛ 121line 118 didn't jump to line 121, because the condition on line 118 was never false
119 return act
121 dist = LaunchCard.calc_raw_distance(src, DummyCard())
122 if dist[tgt] > 1:
123 raise ScarletMistAttackLimit
125 elif evt_type == 'post_calcdistance':
126 src, c, dist = act
128 if not c.is_card(AttackCard):
129 return act
131 if src.tags['scarlet_mist'] != 'buff': 131 ↛ 134line 131 didn't jump to line 134, because the condition on line 131 was never false
132 return act
134 for k in dist:
135 dist[k] = 0
137 elif evt_type == 'action_after' and isinstance(act, Damage):
138 src = act.source
139 if not (src and src.tags['scarlet_mist'] == 'buff'): return act 139 ↛ 140line 139 didn't jump to line 140, because the condition on line 139 was never false
140 if src.life >= src.maxlife: return act
142 g = self.game
143 pact = g.action_stack[-1]
144 if not isinstance(pact, Attack): return act
145 if not pact.associated_card: return act
147 g.process_action(Heal(src, src, act.amount))
149 elif evt_type == 'action_apply' and isinstance(act, PrepareStage):
150 tgt = act.target
151 if not tgt.has_skill(ScarletMist): return act 151 ↛ 152line 151 didn't jump to line 152, because the condition on line 151 was never false
152 if not tgt.tags['scarlet_mist']: return act
153 g = self.game
154 g.process_action(ScarletMistEndAction(tgt, tgt))
156 return act
159class ScarletMistAction(UserAction):
160 def apply_action(self):
161 src, tl = self.source, self.target_list
162 src.tags['scarlet_mist_used'] = True
163 g = self.game
164 for p in g.players:
165 p.tags['scarlet_mist'] = 'nerf'
166 for p in tl:
167 p.tags['scarlet_mist'] = 'buff'
169 return True
171 def is_valid(self):
172 src = self.source
173 return not src.tags['scarlet_mist_used']
176class ScarletMistEndAction(GenericAction):
177 def apply_action(self):
178 g = self.game
179 for p in g.players:
180 p.tags['scarlet_mist'] = False
182 return True
185class ScarletMist(Skill):
186 associated_action = ScarletMistAction
187 skill_category = ['character', 'active', 'once', 'boss']
189 def check(self) -> bool:
190 return not len(self.associated_cards)
192 def target(self, src: Character, tl: Sequence[Character]):
193 g = self.game
194 from thb.thbrole import THBRoleRole
195 n = sum(i == THBRoleRole.ACCOMPLICE for i in g.roles.values())
196 n -= sum(ch.dead and g.roles[ch.player] == THBRoleRole.ACCOMPLICE for ch in g.players)
198 tl = [t for t in tl if not t.dead]
199 try:
200 tl.remove(src)
201 except ValueError:
202 pass
204 tl.insert(0, src)
205 return (tl[:n+1], bool(len(tl)))
208@register_character_to('common', 'boss')
209class Remilia(Character):
210 skills = [SpearTheGungnir, VampireKiss]
211 boss_skills = [ScarletMist]
212 eventhandlers = [SpearTheGungnirHandler, VampireKissHandler, ScarletMistHandler]
213 maxlife = 4