Coverage for thb/characters/meirin.py : 85%
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 DropCards, GenericAction, MaxLifeChange, PlayerTurn, random_choose_card
7from thb.cards.base import DummyCard, Skill, t_None
8from thb.cards.classes import AttackCard, BaseAttack, GrazeCard, LaunchGraze, TreatAs
9from thb.characters.base import Character, register_character_to
10from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet
11from thb.mode import THBEventHandler
14# -- code --
15class LoongPunch(Skill):
16 associated_action = None
17 skill_category = ['character', 'passive']
18 target = t_None()
21class Taichi(TreatAs, Skill):
22 skill_category = ['character', 'active']
24 @property
25 def treat_as(self):
26 cl = self.associated_cards
27 if not cl: return DummyCard
28 c = cl[0]
29 if c.is_card(GrazeCard):
30 return AttackCard
31 if c.is_card(AttackCard):
32 return GrazeCard
33 return DummyCard
35 def check(self):
36 cl = self.associated_cards
37 if not cl or len(cl) != 1: return False
38 c = cl[0]
39 if not (c.is_card(AttackCard) or c.is_card(GrazeCard)): return False 39 ↛ exitline 39 didn't return from function 'check', because the return on line 39 wasn't executed
40 return c.resides_in is not None and c.resides_in.type in (
41 'cards', 'showncards',
42 )
45class RiverBehind(Skill):
46 associated_action = None
47 skill_category = ['character', 'passive', 'awake']
48 target = t_None()
51class LoongPunchAction(GenericAction):
52 def __init__(self, source, target, _type):
53 self.source = source
54 self.target = target
55 self.type = _type
57 def apply_action(self):
58 g = self.game
59 src, tgt = self.source, self.target
60 c = g.user_input([src], ChoosePeerCardInputlet(self, tgt, ('cards', 'showncards')))
61 c = c or random_choose_card(g, [tgt.cards, tgt.showncards])
62 if not c: return False
63 g.players.exclude(tgt).reveal(c)
64 g.process_action(DropCards(src, tgt, [c]))
65 return True
68class LoongPunchHandler(THBEventHandler):
69 interested = ['action_after']
70 execute_after = ['DeathSickleHandler']
72 def handle(self, evt_type, act):
73 if evt_type == 'action_after' and isinstance(act, LaunchGraze):
74 if not act.succeeded: return act
75 g = self.game
76 pact = g.action_stack[-1]
77 if not isinstance(pact, BaseAttack): return act 77 ↛ exitline 77 didn't return from function 'handle', because the return on line 77 wasn't executed
78 self.do_effect(pact.source, pact.target, 'attack')
79 self.do_effect(pact.target, pact.source, 'graze')
81 return act
83 def do_effect(self, src, tgt, _type):
84 if not src.has_skill(LoongPunch): return
85 if not (tgt.cards or tgt.showncards): return 85 ↛ exitline 85 didn't return from function 'do_effect', because the return on line 85 wasn't executed
86 g = self.game
87 if not g.user_input([src], ChooseOptionInputlet(self, (False, True))): return 87 ↛ 89line 87 didn't jump to line 89, because the condition on line 87 was never false
89 g.process_action(LoongPunchAction(src, tgt, _type))
92class RiverBehindAwake(GenericAction):
93 def apply_action(self):
94 tgt = self.target
95 assert tgt.has_skill(RiverBehind)
96 tgt.skills.remove(RiverBehind)
97 tgt.skills.append(Taichi)
98 g = self.game
99 g.process_action(MaxLifeChange(tgt, tgt, -1))
100 return True
103class RiverBehindHandler(THBEventHandler):
104 interested = ['action_apply']
106 def handle(self, evt_type, act):
107 if evt_type == 'action_apply' and isinstance(act, PlayerTurn):
108 tgt = act.target
109 if not tgt.has_skill(RiverBehind): return act
110 g = self.game
111 if tgt.life <= 2 and tgt.life <= min(p.life for p in g.players if not p.dead):
112 g.process_action(RiverBehindAwake(tgt, tgt))
113 return act
116@register_character_to('common')
117class Meirin(Character):
118 skills = [LoongPunch, RiverBehind]
119 eventhandlers = [RiverBehindHandler, LoongPunchHandler]
120 maxlife = 4