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

## 103 statements 102 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 game.base import ActionShootdown

7 from thb.actions import ActionStageLaunchCard, Damage, LaunchCard, PlayerTurn, UserAction

8 from thb.cards.base import Skill

9 from thb.cards.classes import AttackCard, DuelCard, Heal, HealCard, PhysicalCard, 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 Lunatic(Skill):

17 associated_action = None

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

19 target = t_None()

20

21

22 class Discarder(Skill):

23 associated_action = None

24 distance = 1

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

26 target = t_None()

27

28

29 class MahjongDrug(Skill):

30 associated_action = None

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

32 target = t_None()

33

34

35 class LunaticAction(UserAction):

36 def apply_action(self):

37 self.target.skills.append(Discarder)

38 self.target.tags['reisen_discarder'] = True # for tag

39 return True

40

41

42 class LunaticHandler(THBEventHandler):

43 interested = ['action_after']

44

45 def handle(self, evt_type, act):

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

47 src = act.source

48 if not src: return act

49 if not src.has_skill(Lunatic): return act

50

51 tgt = act.target

52 if tgt.dead or tgt.has_skill(Discarder): return act

53

54 g = self.game

55 for lc in reversed(g.action_stack): 55 ↛ 59 line 55 didn't jump to line 59, because the loop on line 55 didn't complete

56 if isinstance(lc, LaunchCard):

57 break

58 else:

59 return act

60

61 c = lc.card

62 if not (c.is_card(AttackCard) or c.is_card(DuelCard)):

63 return act

64

65 if g.user_input([src], ChooseOptionInputlet(self, (False, True))):

66 g.process_action(LunaticAction(src, tgt))

67

68 return act

69

70

71 class DiscarderAttackOnly(ActionShootdown):

72 pass

73

74

75 class DiscarderDistanceLimit(ActionShootdown):

76 pass

77

78

79 class DiscarderHandler(THBEventHandler):

80 interested = ['action_after', 'action_shootdown']

81

82 def handle(self, evt_type, act):

83 if evt_type == 'action_shootdown' and isinstance(act, ActionStageLaunchCard):

84 src = act.source

85 if not src.has_skill(Discarder): return act

86 g = self.game

87 current = PlayerTurn.get_current(g).target

88 if src is not current: return act 88 ↛ exit line 88 didn't return from function 'handle', because the return on line 88 wasn't executed

89

90 self.card = c = act.card

91 if not c.is_card(AttackCard):

92 if c.is_card(PhysicalCard) or 'treat_as' in c.category:

93 raise DiscarderAttackOnly

94 else:

95 return act

96

97 dist = LaunchCard.calc_distance(g, src, Discarder(src))

98 dist.pop(src, '')

99 nearest = max(min(dist.values()), 0)

100 avail = {p for p in dist if dist[p] <= nearest}

101

102 if not set(act.target_list) <= avail:

103 raise DiscarderDistanceLimit

104

105 elif evt_type == 'action_after' and isinstance(act, PlayerTurn):

106 tgt = act.target

107 if tgt.has_skill(Discarder):

108 tgt.skills.remove(Discarder)

109 tgt.tags['reisen_discarder'] = False # for tag

110

111 return act

112

113

114 class MahjongDrugAction(UserAction):

115 def apply_action(self):

116 self.target.tags['wine'] = True

117 return True

118

119 def is_valid(self):

120 return not self.target.dead

121

122

123 class MahjongDrugHandler(THBEventHandler):

124 interested = ['action_after']

125

126 def handle(self, evt_type, act):

127 if evt_type == 'action_after' and isinstance(act, Heal):

128 tgt = act.target

129 if not tgt.has_skill(MahjongDrug): return act

130 card = getattr(act, 'associated_card', None)

131 if not card or not card.is_card(HealCard): return act

132

133 g = self.game

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

135 g.process_action(MahjongDrugAction(tgt, tgt))

136

137 return act

138

139

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

141 class Reisen(Character):

142 skills = [Lunatic, MahjongDrug]

143 eventhandlers = [DiscarderHandler, LunaticHandler, MahjongDrugHandler]

144 maxlife = 4

145

146

147@register_character_to('kof')

148 class ReisenKOF(Character):

149 skills = [Lunatic]

150 eventhandlers = [DiscarderHandler, LunaticHandler]

151 maxlife = 4