Coverage for thb/characters/reisen.py : 36%
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 game.base import ActionShootdown
8from thb.actions import ActionStageLaunchCard, Damage, FinalizeStage, LaunchCard, PlayerTurn
9from thb.actions import UserAction
10from thb.cards.base import Skill
11from thb.cards.classes import AttackCard, DuelCard, Heal, HealCard, PhysicalCard, t_None
12from thb.characters.base import Character, register_character_to
13from thb.inputlets import ChooseOptionInputlet
14from thb.mode import THBEventHandler
17# -- code --
18class Lunatic(Skill):
19 associated_action = None
20 skill_category = ['character', 'passive']
21 target = t_None()
24class Discarder(Skill):
25 associated_action = None
26 distance = 1
27 skill_category = ['character', 'passive']
28 target = t_None()
31class MahjongDrug(Skill):
32 associated_action = None
33 skill_category = ['character', 'passive']
34 target = t_None()
37class LunaticAction(UserAction):
38 def apply_action(self):
39 self.target.skills.append(Discarder)
40 self.target.tags['reisen_discarder'] = True # for tag
41 return True
44class LunaticHandler(THBEventHandler):
45 interested = ['action_after']
47 def handle(self, evt_type, act):
48 if evt_type == 'action_after' and isinstance(act, Damage):
49 src = act.source
50 if not src: return act
51 if not src.has_skill(Lunatic): return act
53 tgt = act.target
54 if tgt.dead or tgt.has_skill(Discarder): return act
56 g = self.game
57 for lc in reversed(g.action_stack):
58 if isinstance(lc, LaunchCard):
59 break
60 else:
61 return act
63 c = lc.card
64 if not (c.is_card(AttackCard) or c.is_card(DuelCard)):
65 return act
67 if g.user_input([src], ChooseOptionInputlet(self, (False, True))):
68 g.process_action(LunaticAction(src, tgt))
70 return act
73class DiscarderAttackOnly(ActionShootdown):
74 pass
77class DiscarderDistanceLimit(ActionShootdown):
78 pass
81class DiscarderHandler(THBEventHandler):
82 interested = ['action_after', 'action_shootdown']
84 def handle(self, evt_type, act):
85 if evt_type == 'action_shootdown' and isinstance(act, ActionStageLaunchCard):
86 src = act.source
87 if not src.has_skill(Discarder): return act
88 g = self.game
89 current = PlayerTurn.get_current(g).target
90 if src is not current: return act
92 self.card = c = act.card
93 if not c.is_card(AttackCard):
94 if c.is_card(PhysicalCard) or 'treat_as' in c.category:
95 raise DiscarderAttackOnly
96 else:
97 return act
99 dist = LaunchCard.calc_distance(g, src, Discarder(src))
100 dist.pop(src, '')
101 nearest = max(min(dist.values()), 0)
102 avail = {p for p in dist if dist[p] <= nearest}
104 if not set(act.target_list) <= avail:
105 raise DiscarderDistanceLimit
107 elif evt_type == 'action_after' and isinstance(act, FinalizeStage):
108 tgt = act.target
109 if tgt.has_skill(Discarder):
110 tgt.skills.remove(Discarder)
111 tgt.tags['reisen_discarder'] = False # for tag
113 return act
116class MahjongDrugAction(UserAction):
117 def apply_action(self):
118 self.target.tags['wine'] = True
119 return True
121 def is_valid(self):
122 return not self.target.dead
125class MahjongDrugHandler(THBEventHandler):
126 interested = ['action_after']
128 def handle(self, evt_type, act):
129 if evt_type == 'action_after' and isinstance(act, Heal):
130 tgt = act.target
131 if not tgt.has_skill(MahjongDrug): return act
132 card = getattr(act, 'associated_card', None)
133 if not card or not card.is_card(HealCard): return act
135 g = self.game
136 if g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):
137 g.process_action(MahjongDrugAction(tgt, tgt))
139 return act
142@register_character_to('common', '-kof')
143class Reisen(Character):
144 skills = [Lunatic, MahjongDrug]
145 eventhandlers = [DiscarderHandler, LunaticHandler, MahjongDrugHandler]
146 maxlife = 4
149@register_character_to('kof')
150class ReisenKOF(Character):
151 skills = [Lunatic]
152 eventhandlers = [DiscarderHandler, LunaticHandler]
153 maxlife = 4