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 sk = self.associated_card 

21 assert isinstance(sk, FlyingSkanda) 

22 card = sk.associated_cards[0] 

23 action = card.associated_action 

24 assert action 

25 self.action_cls = action 

26 

27 def is_valid(self): 

28 p = self.source 

29 if p.tags['turn_count'] <= p.tags['flying_skanda']: 

30 return False 

31 if any(t.dead for t in self.target_list): 

32 return False 

33 return True 

34 

35 def get_actual_action(self, act): 

36 return None 

37 

38 

39class FlyingSkanda(Skill): 

40 associated_action = FlyingSkandaAction 

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

42 usage = 'launch' 

43 

44 def target(self, src, tl): 

45 tl = [ch for ch in tl if not ch.dead] 

46 cl = self.associated_cards 

47 if not cl: return ([], False) 

48 c = cl[0] 

49 if len(tl) < 2: 

50 return c.target(src, tl) 

51 else: 

52 rst = c.target(src, tl[:-1]) 

53 a = tl[-1] 

54 if a is src: 

55 return rst[0], False 

56 else: 

57 return rst[0] + [a], rst[1] 

58 

59 @property 

60 def distance(self): 

61 cl = self.associated_cards 

62 if not cl: return 0 

63 return getattr(cl[0], 'distance', 99999) 

64 

65 def check(self): 

66 cl = self.associated_cards 

67 if len(cl) != 1: return False 

68 c = cl[0] 

69 if c.is_card(AttackCard): return True 

70 

71 if c.is_card(DollControlCard): return False 

72 if c.is_card(RejectCard): return False 

73 

74 act = c.associated_action 

75 if not act: return False 

76 if not issubclass(act, InstantSpellCardAction): return False 

77 

78 return True 

79 

80 def is_card(self, cls): 

81 cl = self.associated_cards 

82 if cl and cl[0].is_card(cls): return True 

83 return isinstance(self, cls) 

84 

85 

86class FlyingSkandaHandler(THBEventHandler): 

87 interested = ['action_after'] 

88 

89 def handle(self, evt_type, act): 

90 if evt_type == 'action_after' and isinstance(act, LaunchCard): 

91 if not act.card.is_card(FlyingSkanda): return act 

92 act.source.tags['flying_skanda'] = act.source.tags['turn_count'] 

93 return act 

94 

95 

96class ShikigamiAction(UserAction): 

97 def apply_action(self): 

98 tgt = self.target 

99 src = self.source 

100 

101 g = self.game 

102 

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

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

105 ): 

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

107 else: 

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

109 

110 tgt.tags['shikigami_target'] = src 

111 src.tags['shikigami_target'] = tgt 

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

113 

114 return True 

115 

116 def is_valid(self): 

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

118 

119 

120class Shikigami(Skill): 

121 associated_action = ShikigamiAction 

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

123 target = t_OtherOne() 

124 

125 def check(self): 

126 return not self.associated_cards 

127 

128 

129class ShikigamiHandler(THBEventHandler): 

130 interested = ['post_calcdistance'] 

131 

132 def handle(self, evt_type, arg): 

133 if evt_type == 'post_calcdistance': 

134 src, card, dist = arg 

135 if not card.is_card(AttackCard): return arg 

136 

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

138 if not tgt or tgt.dead: return arg 

139 

140 g = self.game 

141 current = PlayerTurn.get_current(g).target 

142 if current is not src: return arg 

143 

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

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

146 return arg 

147 

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

149 

150 for k in dist2: 

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

152 dist[k] = 0 

153 

154 return arg 

155 

156 

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

158class Chen(Character): 

159 skills = [FlyingSkanda, Shikigami] 

160 eventhandlers = [FlyingSkandaHandler, ShikigamiHandler] 

161 maxlife = 4