# Coverage for thb/characters/yuuka.py : 98%

## 134 statements 133 run 1 missing 0 excluded 2 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 Damage, DrawCards, ForEach, LaunchCard, PlayerDeath, UserAction, PlayerTurn

7 from thb.actions import user_choose_players

8 from thb.cards.base import Skill

9 from thb.cards.classes import AttackCard, Duel, InstantSpellCardAction, Reject, TreatAs, 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 ReversedScales(TreatAs, Skill):

17 skill_category = ['character', 'active', 'compulsory']

18 treat_as = AttackCard

19

20 def check(self):

21 cl = self.associated_cards

22 if not cl or len(cl) != 1:

23 return False

24

25 g = self.game

26 if PlayerTurn.get_current(g).target is self.character:

27 return False

28

29 return cl[0].resides_in.type in ('cards', 'showncards')

30

31

32 class Sadist(Skill):

33 associated_action = None

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

35 target = t_None()

36

37

38 class SadistKOF(Skill):

39 associated_action = None

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

41 target = t_None()

42

43

44 class SadistKOFDrawCards(DrawCards):

45 pass

46

47

48 class SadistKOFDamageAction(UserAction):

49 def apply_action(self):

50 g = self.game

51 src, tgt = self.source, self.target

52 return g.process_action(Damage(src, tgt, 1))

53

54

55 class SadistKOFHandler(THBEventHandler):

56 interested = ['action_after', 'character_debut']

57 execute_after = ['DeathHandler']

58

59 def handle(self, evt_type, arg):

60 if evt_type == 'action_after' and isinstance(arg, PlayerDeath):

61 tgt = arg.target

62 g = self.game

63 op = g.get_opponent(tgt)

64 if arg.source is op and op.has_skill(SadistKOF):

65 g.process_action(SadistKOFDrawCards(op, 2))

66 op.tags['sadist_kof_fire'] = True

67

68 elif evt_type == 'character_debut':

69 old, new = arg

70 if not old: return arg

71

72 g = self.game

73 op = g.get_opponent(new)

74

75 if op.has_skill(SadistKOF) and op.tags['sadist_kof_fire']:

76 op.tags['sadist_kof_fire'] = False

77 g.process_action(SadistKOFDamageAction(op, new))

78

79 return arg

80

81

82 class ReversedScalesAction(UserAction):

83 def __init__(self, target, action):

84 self.source = target

85 self.target = target

86 self.action = action

87

88 def apply_action(self):

89 self.action.__class__ = Duel

90 return True

91

92

93 class ReversedScalesHandler(THBEventHandler):

94 interested = ['action_before']

95 execute_before = ['MaidenCostumeHandler']

96

97 def handle(self, evt_type, act):

98 if evt_type != 'action_before': 98 ↛ 99 line 98 didn't jump to line 99, because the condition on line 98 was never true

99 return act

100

101 if not isinstance(act, InstantSpellCardAction):

102 return act

103

104 if isinstance(act, Reject):

105 # HACK

106 return act

107

108 if ForEach.is_group_effect(act):

109 return act

110

111 src = act.source

112 tgt = act.target

113

114 if not src or tgt is src:

115 return act

116

117 if not tgt.has_skill(ReversedScales):

118 return act

119

120 g = self.game

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

122 return act

123

124 g.process_action(ReversedScalesAction(tgt, act))

125

126 return act

127

128

129 class SadistAction(UserAction):

130 def __init__(self, source, target):

131 self.source = source

132 self.target = target

133

134 def apply_action(self):

135 g = self.game

136 src, tgt = self.source, self.target

137 tgt.tags['sadist_target'] = False

138 g.process_action(Damage(src, tgt, 1))

139 return True

140

141

142 class SadistHandler(THBEventHandler):

143 interested = ['action_after', 'action_before']

144 card_usage = 'drop'

145 execute_before = ['WineHandler']

146 execute_after = ['DeathHandler']

147

148 def handle(self, evt_type, act):

149 if evt_type == 'action_after' and isinstance(act, PlayerDeath):

150 src = act.source

151 if not src or not src.has_skill(Sadist):

152 return act

153

154 g = self.game

155 dist = LaunchCard.calc_distance(g, src, AttackCard())

156 candidates = [k for k, v in dist.items() if v <= 0 and k is not src]

157

158 if not candidates:

159 return act

160

161 pl = user_choose_players(self, src, candidates)

162 if pl: 162 ↛ 178 line 162 didn't jump to line 178, because the condition on line 162 was never false

163 self.game.process_action(SadistAction(src, pl[0]))

164

165 elif evt_type == 'action_before' and isinstance(act, Damage):

166 src = act.source

167 tgt = act.target

168

169 if not src or src is tgt:

170 return act

171

172 if not src.has_skill(Sadist):

173 return act

174

175 if tgt.life == 1:

176 act.amount += 1

177

178 return act

179

180 def choose_player_target(self, pl):

181 return pl[-1:], len(pl)

182

183

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

185 class Yuuka(Character):

186 skills = [ReversedScales, Sadist]

187 eventhandlers = [ReversedScalesHandler, SadistHandler]

188 maxlife = 4

189

190

191@register_character_to('kof')

192 class YuukaKOF(Character):

193 skills = [ReversedScales, SadistKOF]

194 eventhandlers = [ReversedScalesHandler, SadistKOFHandler]

195 maxlife = 4