# Coverage for thb/characters/medicine.py : 99%

## 108 statements 108 run 0 missing 0 excluded 1 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 Damage, DrawCards, DropCards, FatetellStage, GenericAction, LaunchCard

8 from thb.actions import LifeLost, ShowCards, UseCard, UserAction, user_choose_cards

9 from thb.cards.base import Card, Skill, VirtualCard

10 from thb.cards.classes import Wine, t_None

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 Ciguatera(Skill):

18 associated_action = None

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

20 target = t_None()

21

22

23 class CiguateraAction(UserAction):

24 def __init__(self, source, target, cards):

25 self.source = source

26 self.target = target

27 self.cards = cards

28

29 def apply_action(self):

30 tgt = self.target

31 src = self.source

32 g = self.game

33 g.process_action(DropCards(src, src, self.cards))

34 g.process_action(LifeLost(src, tgt, 1))

35 g.process_action(Wine(src, tgt))

36

37 return True

38

39

40 class CiguateraHandler(THBEventHandler):

41 interested = ('action_before',)

42 card_usage = 'drop'

43

44 def handle(self, evt_type, act):

45 if evt_type == 'action_before' and isinstance(act, FatetellStage):

46 if act.target.dead: return act 46 ↛ exit line 46 didn't return from function 'handle', because the return on line 46 wasn't executed

47 g = self.game

48 for p in g.players:

49 if p.dead:

50 continue

51

52 if not p.has_skill(Ciguatera):

53 continue

54

55 cards = user_choose_cards(self, p, ('cards', 'showncards', 'equips'))

56 if cards:

57 g.process_action(CiguateraAction(p, act.target, cards))

58

59 return act

60

61 def cond(self, cl):

62 return \

63 len(cl) == 1 and \

64 not cl[0].is_card(Skill) and \

65 cl[0].resides_in.type in ('cards', 'showncards', 'equips') and \

66 cl[0].color == Card.BLACK

67

68

69 class Melancholy(Skill):

70 associated_action = None

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

72 target = t_None()

73

74

75 class MelancholyPoison(VirtualCard):

76 associated_action = None

77 target = t_None()

78

79

80 class MelancholyLimit(ActionShootdown):

81 pass

82

83

84 class MelancholyAction(GenericAction):

85 def __init__(self, source, target, amount):

86 self.source = source

87 self.target = target

88 self.amount = amount

89

90 def apply_action(self):

91 src = self.source

92 tgt = self.target

93 draw = DrawCards(src, self.amount)

94 g = self.game

95 g.process_action(draw)

96 g.process_action(ShowCards(src, draw.cards))

97 if [c for c in draw.cards if c.suit != Card.CLUB]: # any non-club

98 tgt.tags['melancholy_tag'] = g.turn_count

99 self.effective = True

100

101 else:

102 self.effective = False

103

104 return True

105

106

107 class MelancholyHandler(THBEventHandler):

108 interested = ('action_after', 'action_shootdown')

109 execute_after = (

110 'DyingHandler',

111 'AyaRoundfanHandler',

112 'NenshaPhoneHandler',

113 )

114

115 def handle(self, evt_type, act):

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

117 act = act

118 tgt = act.target

119 src = act.source

120

121 if not src:

122 return act

123

124 if not tgt.has_skill(Melancholy):

125 return act

126

127 g = self.game

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

129 return act

130

131 g.process_action(MelancholyAction(tgt, src, amount=1))

132

133 elif evt_type == 'action_shootdown' and isinstance(act, (LaunchCard, UseCard)):

134 src = act.source

135 g = self.game

136 if src.tags.get('melancholy_tag') != g.turn_count:

137 return act

138

139 def walk(c):

140 if not c.is_card(VirtualCard):

141 return [c]

142

143 if c.usage not in ('launch', 'use'):

144 return []

145

146 cards = c.associated_cards

147 return walk(cards[0]) if len(cards) == 1 else cards

148

149 cards = walk(act.card)

150 zone = src.cards, src.showncards

151 for c in cards:

152 if c.resides_in in zone:

153 raise MelancholyLimit

154

155 return act

156

157

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

159 class Medicine(Character):

160 skills = [Ciguatera, Melancholy]

161 eventhandlers = [CiguateraHandler, MelancholyHandler]

162 maxlife = 3