Coverage for thb/characters/chen.py : 92%
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, ForEach, LaunchCard, PlayerTurn, UserAction
8from thb.cards.base import Skill
9from thb.cards.classes import AttackCard, DollControlCard, Heal, InstantSpellCardAction, RejectCard
10from thb.cards.classes import t_OtherOne
11from thb.characters.base import Character, register_character_to
12from thb.inputlets import ChooseOptionInputlet
13from thb.mode import THBEventHandler
16# -- code --
17class FlyingSkandaAction(ForEach):
19 def prepare(self):
20 skill = self.associated_card
21 card = skill.associated_cards[0]
22 action = card.associated_action
23 self.action_cls = action
25 def is_valid(self):
26 p = self.source
27 if p.tags['turn_count'] <= p.tags['flying_skanda']:
28 return False
29 if any(t.dead for t in self.target_list): 29 ↛ 30line 29 didn't jump to line 30, because the condition on line 29 was never true
30 return False
31 return True
33 def get_actual_action(self, act):
34 return None
37class FlyingSkanda(Skill):
38 associated_action = FlyingSkandaAction
39 skill_category = ['character', 'active']
40 usage = 'launch'
42 def target(self, src, tl):
43 tl = [ch for ch in tl if not ch.dead]
44 cl = self.associated_cards
45 if not cl: return ([], False)
46 c = cl[0]
47 if len(tl) < 2:
48 return c.target(src, tl)
49 else:
50 rst = c.target(src, tl[:-1])
51 a = tl[-1]
52 if a is src:
53 return rst[0], False
54 else:
55 return rst[0] + [a], rst[1]
57 @property
58 def distance(self):
59 cl = self.associated_cards
60 if not cl: return 0 60 ↛ exitline 60 didn't return from function 'distance', because the return on line 60 wasn't executed
61 return cl[0].distance
63 def check(self):
64 cl = self.associated_cards
65 if len(cl) != 1: return False
66 c = cl[0]
67 if c.is_card(AttackCard): return True
69 if c.is_card(DollControlCard): return False 69 ↛ exitline 69 didn't return from function 'check', because the return on line 69 wasn't executed
70 if c.is_card(RejectCard): return False 70 ↛ exitline 70 didn't return from function 'check', because the return on line 70 wasn't executed
72 act = c.associated_action
73 if not act: return False 73 ↛ exitline 73 didn't return from function 'check', because the return on line 73 wasn't executed
74 if not issubclass(act, InstantSpellCardAction): return False 74 ↛ 76line 74 didn't jump to line 76, because the condition on line 74 was never false
76 return True
78 def is_card(self, cls):
79 cl = self.associated_cards
80 if cl and cl[0].is_card(cls): return True
81 return isinstance(self, cls)
84class FlyingSkandaHandler(THBEventHandler):
85 interested = ['action_after']
87 def handle(self, evt_type, act):
88 if evt_type == 'action_after' and isinstance(act, LaunchCard):
89 if not act.card.is_card(FlyingSkanda): return act
90 act.source.tags['flying_skanda'] = act.source.tags['turn_count']
91 return act
94class ShikigamiAction(UserAction):
95 def apply_action(self):
96 tgt = self.target
97 src = self.source
99 g = self.game
101 if tgt.life < tgt.maxlife and g.user_input( 101 ↛ 104line 101 didn't jump to line 104, because the condition on line 101 was never true
102 [tgt], ChooseOptionInputlet(self, (False, True))
103 ):
104 g.process_action(Heal(src, tgt))
105 else:
106 g.process_action(DrawCards(tgt, 2))
108 tgt.tags['shikigami_target'] = src
109 src.tags['shikigami_target'] = tgt
110 src.tags['shikigami_tag'] = src.tags['turn_count']
112 return True
114 def is_valid(self):
115 return 'shikigami_tag' not in self.source.tags
118class Shikigami(Skill):
119 associated_action = ShikigamiAction
120 skill_category = ['character', 'active', 'once']
121 target = t_OtherOne()
123 def check(self):
124 return not self.associated_cards
127class ShikigamiHandler(THBEventHandler):
128 interested = ['post_calcdistance']
130 def handle(self, evt_type, arg):
131 if evt_type == 'post_calcdistance': 131 ↛ 152line 131 didn't jump to line 152, because the condition on line 131 was never false
132 src, card, dist = arg
133 if not card.is_card(AttackCard): return arg
135 tgt = src.tags.get('shikigami_target')
136 if not tgt or tgt.dead: return arg
138 g = self.game
139 current = PlayerTurn.get_current(g).target
140 if current is not src: return arg 140 ↛ exitline 140 didn't return from function 'handle', because the return on line 140 wasn't executed
142 origin = src if 'shikigami_tag' in src.tags else tgt
143 if origin.tags['shikigami_tag'] != origin.tags['turn_count']:
144 return arg
146 dist2 = LaunchCard.calc_raw_distance(g, tgt, AttackCard())
148 for k in dist2:
149 if dist[k] > 0 and dist2[k] <= 1:
150 dist[k] = 0
152 return arg
155@register_character_to('common', '-kof')
156class Chen(Character):
157 skills = [FlyingSkanda, Shikigami]
158 eventhandlers = [FlyingSkandaHandler, ShikigamiHandler]
159 maxlife = 4