Hide keyboard shortcuts

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 

3 

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 

14 

15 

16# -- code -- 

17class FlyingSkandaAction(ForEach): 

18 

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 

24 

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 

32 

33 def get_actual_action(self, act): 

34 return None 

35 

36 

37class FlyingSkanda(Skill): 

38 associated_action = FlyingSkandaAction 

39 skill_category = ['character', 'active'] 

40 usage = 'launch' 

41 

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] 

56 

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 

62 

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 

68 

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

71 

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 

75 

76 return True 

77 

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) 

82 

83 

84class FlyingSkandaHandler(THBEventHandler): 

85 interested = ['action_after'] 

86 

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 

92 

93 

94class ShikigamiAction(UserAction): 

95 def apply_action(self): 

96 tgt = self.target 

97 src = self.source 

98 

99 g = self.game 

100 

101 if tgt.life < tgt.maxlife and g.user_input( 

102 [tgt], ChooseOptionInputlet(self, (False, True)) 

103 ): 

104 g.process_action(Heal(src, tgt)) 

105 else: 

106 g.process_action(DrawCards(tgt, 2)) 

107 

108 tgt.tags['shikigami_target'] = src 

109 src.tags['shikigami_target'] = tgt 

110 src.tags['shikigami_tag'] = src.tags['turn_count'] 

111 

112 return True 

113 

114 def is_valid(self): 

115 return 'shikigami_tag' not in self.source.tags 

116 

117 

118class Shikigami(Skill): 

119 associated_action = ShikigamiAction 

120 skill_category = ['character', 'active', 'once'] 

121 target = t_OtherOne() 

122 

123 def check(self): 

124 return not self.associated_cards 

125 

126 

127class ShikigamiHandler(THBEventHandler): 

128 interested = ['post_calcdistance'] 

129 

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 

134 

135 tgt = src.tags.get('shikigami_target') 

136 if not tgt or tgt.dead: return arg 

137 

138 g = self.game 

139 current = PlayerTurn.get_current(g).target 

140 if current is not src: return arg 

141 

142 origin = src if 'shikigami_tag' in src.tags else tgt 

143 if origin.tags['shikigami_tag'] != origin.tags['turn_count']: 

144 return arg 

145 

146 dist2 = LaunchCard.calc_raw_distance(g, tgt, AttackCard()) 

147 

148 for k in dist2: 

149 if dist[k] > 0 and dist2[k] <= 1: 

150 dist[k] = 0 

151 

152 return arg 

153 

154 

155@register_character_to('common', '-kof') 

156class Chen(Character): 

157 skills = [FlyingSkanda, Shikigami] 

158 eventhandlers = [FlyingSkandaHandler, ShikigamiHandler] 

159 maxlife = 4