# Coverage for thb/characters/kyouko.py : 30%

## 105 statements 41 run 64 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

3# -- stdlib --

4# -- third party --

5# -- own --

6 from thb.actions import AskForCard, Damage, DrawCards, LaunchCard, UserAction, migrate_cards

7 from thb.actions import user_choose_players

8 from thb.cards.base import Skill, VirtualCard

9 from thb.cards.classes import Attack, AttackCard, t_None

10 from thb.characters.base import Character, register_character_to

11 from thb.inputlets import ChooseOptionInputlet

12 from thb.mode import THBEventHandler

13

14

15# -- code --

16 class Echo(Skill):

17 associated_action = None

18 skill_category = ['character', 'passive']

19 target = t_None()

20

21

22 class EchoAction(UserAction):

23 def __init__(self, source, target, card):

24 self.source, self.target, self.card = source, target, card

25

26 def apply_action(self):

27 src, tgt, c = self.target, self.target, self.card

28 g = self.game

29

30 assert c.detached

31

32 shadow = Echo(src)

33 for a in g.action_stack:

34 if isinstance(a, LaunchCard) and a.card is c:

35 a.card = shadow

36 elif getattr(a, 'associated_card', None) is c:

37 a.associated_card = shadow

38

39 migrate_cards([c], tgt.cards, unwrap=True)

40

41 return True

42

43

44 class EchoHandler(THBEventHandler):

45 interested = ['action_after']

46 execute_after = [

47 'DyingHandler',

48 'AyaRoundfanHandler',

49 'NenshaPhoneHandler',

50 ]

51

52 def handle(self, evt_type, act):

53 if evt_type == 'action_after' and isinstance(act, Damage):

54 tgt = act.target

55 if tgt.dead:

56 return act

57

58 if not tgt.has_skill(Echo):

59 return act

60

61 g = self.game

62 pact = g.action_stack[-1]

63 card = getattr(pact, 'associated_card', None)

64 if not card:

65 return act

66

67 if not card.detached or card.unwrapped:

68 return act

69

70 if not VirtualCard.unwrap([card]):

71 return act

72

73 if not g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):

74 return act

75

76 attack = card.is_card(AttackCard)

77 pl = attack and user_choose_players(self, tgt, [p for p in g.players if not p.dead])

78 p = pl[0] if pl else tgt

79

80 g.process_action(EchoAction(tgt, p, card))

81

82 return act

83

84 def choose_player_target(self, tl):

85 if not tl:

86 return (tl, False)

87

88 return (tl[-1:], True)

89

90

91 class Resonance(Skill):

92 associated_action = None

93 skill_category = ['character', 'passive']

94 target = t_None()

95

96

97 class ResonanceDrawAction(DrawCards):

98 pass

99

100

101 class ResonanceLaunchCard(LaunchCard):

102 pass

103

104

105 class ResonanceAction(AskForCard):

106 card_usage = 'launch'

107

108 def __init__(self, source, target, victim):

109 AskForCard.__init__(self, source, target, AttackCard)

110 self.source = source

111 self.target = target

112 self.victim = victim

113

114 def process_card(self, c):

115 g = self.game

116 g.process_action(ResonanceLaunchCard(self.target, [self.victim], c, bypass_check=True))

117 return True

118

119 def ask_for_action_verify(self, p, cl, tl):

120 return ResonanceLaunchCard(self.target, [self.victim], cl[0], bypass_check=True).can_fire()

121

122

123 class ResonanceHandler(THBEventHandler):

124 interested = ['action_done']

125

126 def handle(self, evt_type, act):

127 if evt_type == 'action_done' and isinstance(act, Attack):

128 src = act.source

129 tgt = act.target

130

131 if act.cancelled or src.dead or tgt.dead:

132 return act

133

134 if not src.has_skill(Resonance):

135 return act

136

137 g = self.game

138 pl = [p for p in g.players if not p.dead and p not in (src, tgt)]

139

140 if not pl:

141 return act

142

143 pl = user_choose_players(self, src, pl)

144

145 if not pl:

146 return act

147

148 g.process_action(ResonanceAction(src, pl[0], tgt))

149

150 return act

151

152 def choose_player_target(self, tl):

153 if not tl:

154 return (tl, False)

155

156 return (tl[-1:], True)

157

158

159@register_character_to('common')

160 class Kyouko(Character):

161 skills = [Echo, Resonance]

162 eventhandlers = [EchoHandler, ResonanceHandler]

163 maxlife = 4