# Coverage for thb/characters/chen.py : 25%

## 111 statements 39 run 72 missing 0 excluded 0 partial

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 -*-

2 from __future__ import annotations

3

4# -- stdlib --

5# -- third party --

6# -- own --

7 from thb.actions import DrawCards, ForEach, LaunchCard, PlayerTurn, UserAction

8 from thb.cards.base import Skill

9 from thb.cards.classes import AttackCard, DollControlCard, Heal, InstantSpellCardAction, RejectCard

10 from thb.cards.classes import t_OtherOne

11 from thb.characters.base import Character, register_character_to

12 from thb.inputlets import ChooseOptionInputlet

13 from thb.mode import THBEventHandler

14

15

16# -- code --

17 class 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

39 class 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

86 class 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

96 class 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

120 class 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

129 class 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')

158 class Chen(Character):

159 skills = [FlyingSkanda, Shikigami]

160 eventhandlers = [FlyingSkandaHandler, ShikigamiHandler]

161 maxlife = 4