Coverage for thb/characters/yuyuko.py : 28%
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 DrawCards, DropCardStage, DummyAction, FinalizeStage, GenericAction
8from thb.actions import LifeLost, MaxLifeChange, Pindian, PlayerDeath, PlayerTurn, TryRevive
9from thb.actions import UserAction, ttags
10from thb.cards.base import Skill, t_None, t_OtherOne
11from thb.cards.basic import Heal
12from thb.characters.base import Character, register_character_to
13from thb.inputlets import ChooseOptionInputlet
14from thb.mode import THBEventHandler
17# -- code --
18class GuidedDeathLifeLost(LifeLost):
19 pass
22class GuidedDeathEffect(GenericAction):
23 def __init__(self, source, target_list):
24 self.source = source
25 self.target = source
26 self.target_list = target_list
28 def apply_action(self):
29 g = self.game
31 for p in self.target_list:
32 g.process_action(GuidedDeathLifeLost(p, p))
34 return True
37class GuidedDeathAction(UserAction):
38 def apply_action(self):
39 src, tgt = self.source, self.target
40 if tgt.dead: return True
41 g = self.game
42 ttags(src)['guided_death_active_use'] = tgt
43 g.process_action(GuidedDeathLifeLost(src, tgt, 1))
44 return True
47class GuidedDeath(Skill):
48 associated_action = GuidedDeathAction
49 skill_category = ('character', 'active')
50 target = t_OtherOne()
51 usage = 'drop'
53 def check(self):
54 cl = self.associated_cards
55 return len(cl) == 0
58class GuidedDeathHandler(THBEventHandler):
59 interested = ['action_apply']
60 execute_before = ['SoulDrainHandler']
62 def handle(self, evt_type, act):
63 if evt_type == 'action_apply' and isinstance(act, FinalizeStage):
64 g = self.game
66 src = act.target
67 if not src.has_skill(GuidedDeath) or src.dead:
68 return act
70 p = ttags(src)['guided_death_active_use']
71 if p:
72 g.process_action(Heal(p, p, 1))
73 else:
74 tl = [p for p in g.players.rotate_to(src) if p.life == 1 and p is not src]
75 if not tl:
76 return act
78 g.process_action(GuidedDeathEffect(src, tl))
80 return act
83class SoulDrain(Skill):
84 associated_action = None
85 skill_category = ['character', 'passive']
86 target = t_None()
89class SoulDrainEffect(GenericAction):
90 def apply_action(self):
91 src, tgt = self.source, self.target
92 g = self.game
94 assert tgt.life <= 0
96 g.process_action(DrawCards(src, 1))
98 if not tgt.cards and not tgt.showncards:
99 return True
101 if src is tgt:
102 return True
104 if g.user_input([src], ChooseOptionInputlet(self, (False, True))):
105 if g.process_action(Pindian(src, tgt)):
106 g.process_action(MaxLifeChange(src, tgt, -tgt.maxlife + 1))
107 else:
108 g.process_action(Heal(src, tgt, -tgt.life + 1))
110 return True
113class SoulDrainHandler(THBEventHandler):
114 interested = ['action_before']
116 def handle(self, evt_type, act):
117 if evt_type == 'action_before' and isinstance(act, TryRevive):
118 g = self.game
119 tgt = act.target
121 for p in g.players:
122 if p.has_skill(SoulDrain) and not p.dead and p is not tgt:
123 break
124 else:
125 return act
127 for a in reversed(g.action_stack):
128 if isinstance(a, PlayerTurn):
129 if a.target is not p:
130 return act
131 else:
132 break
133 else:
134 return act
136 g.process_action(SoulDrainEffect(p, tgt))
138 if tgt.life > 0:
139 # Abort TryRevive process
140 return DummyAction(p, act.target)
141 else:
142 return act
144 return act
147class PerfectCherryBlossomHandler(THBEventHandler):
148 interested = ['action_apply', 'action_before']
149 execute_after = ['DeathHandler']
151 def handle(self, evt_type, act):
152 if evt_type == 'action_apply' and isinstance(act, PlayerDeath):
153 g = self.game
155 for p in g.players:
156 if p.has_skill(PerfectCherryBlossom) and not p.dead and p is not act.target:
157 break
158 else:
159 return act
161 g.process_action(PerfectCherryBlossomExtractAction(p, act.target))
163 elif evt_type == 'action_before' and isinstance(act, DropCardStage):
164 g = self.game
165 tgt = act.target
166 if not tgt.has_skill(PerfectCherryBlossom):
167 return act
169 act.dropn -= tgt.maxlife - tgt.life
171 return act
174class PerfectCherryBlossomExtractAction(UserAction):
175 def apply_action(self):
176 g = self.game
177 src = self.source
178 if src.life >= src.maxlife:
179 choice = 'maxlife'
180 elif g.user_input([src], ChooseOptionInputlet(self, ('life', 'maxlife'))) == 'maxlife':
181 choice = 'maxlife'
182 else:
183 choice = 'life'
185 if choice == 'life':
186 g.process_action(Heal(src, src, 1))
187 else:
188 g.process_action(MaxLifeChange(src, src, 1))
190 return True
193class PerfectCherryBlossom(Skill):
194 associated_action = None
195 skill_category = ('character', 'passive')
196 target = t_None()
199@register_character_to('common', '-kof')
200class Yuyuko(Character):
201 skills = [GuidedDeath, SoulDrain, PerfectCherryBlossom]
202 eventhandlers = [GuidedDeathHandler, SoulDrainHandler, PerfectCherryBlossomHandler]
203 maxlife = 3