Hide keyboard shortcuts

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

2from __future__ import annotations 

3 

4# -- stdlib -- 

5# -- third party -- 

6# -- own -- 

7from game.base import ActionShootdown 

8from thb.actions import Damage, DrawCards, DropCards, FatetellStage, GenericAction, LaunchCard 

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

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

11from thb.cards.classes import Wine, t_None 

12from thb.characters.base import Character, register_character_to 

13from thb.inputlets import ChooseOptionInputlet 

14from thb.mode import THBEventHandler 

15 

16 

17# -- code -- 

18class Ciguatera(Skill): 

19 associated_action = None 

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

21 target = t_None() 

22 

23 

24class CiguateraAction(UserAction): 

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

26 self.source = source 

27 self.target = target 

28 self.cards = cards 

29 

30 def apply_action(self): 

31 tgt = self.target 

32 src = self.source 

33 g = self.game 

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

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

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

37 

38 return True 

39 

40 

41class CiguateraHandler(THBEventHandler): 

42 interested = ['action_before'] 

43 card_usage = 'drop' 

44 

45 def handle(self, evt_type, act): 

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

47 if act.target.dead: return act 

48 g = self.game 

49 for p in g.players: 

50 if p.dead: 

51 continue 

52 

53 if not p.has_skill(Ciguatera): 

54 continue 

55 

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

57 if cards: 

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

59 

60 return act 

61 

62 def cond(self, cl): 

63 return \ 

64 len(cl) == 1 and \ 

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

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

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

68 

69 

70class Melancholy(Skill): 

71 associated_action = None 

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

73 target = t_None() 

74 

75 

76class MelancholyPoison(VirtualCard): 

77 associated_action = None 

78 target = t_None() 

79 

80 

81class MelancholyLimit(ActionShootdown): 

82 pass 

83 

84 

85class MelancholyAction(GenericAction): 

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

87 self.source = source 

88 self.target = target 

89 self.amount = amount 

90 

91 def apply_action(self): 

92 src = self.source 

93 tgt = self.target 

94 draw = DrawCards(src, self.amount) 

95 g = self.game 

96 g.process_action(draw) 

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

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

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

100 self.effective = True 

101 

102 else: 

103 self.effective = False 

104 

105 return True 

106 

107 

108class MelancholyHandler(THBEventHandler): 

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

110 execute_after = [ 

111 'DyingHandler', 

112 'AyaRoundfanHandler', 

113 'NenshaPhoneHandler', 

114 ] 

115 

116 def handle(self, evt_type, act): 

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

118 act = act 

119 tgt = act.target 

120 src = act.source 

121 

122 if not src: 

123 return act 

124 

125 if not tgt.has_skill(Melancholy): 

126 return act 

127 

128 g = self.game 

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

130 return act 

131 

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

133 

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

135 src = act.source 

136 g = self.game 

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

138 return act 

139 

140 def walk(c): 

141 if not c.is_card(VirtualCard): 

142 return [c] 

143 

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

145 return [] 

146 

147 cards = c.associated_cards 

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

149 

150 cards = walk(act.card) 

151 zone = src.cards, src.showncards 

152 for c in cards: 

153 if c.resides_in in zone: 

154 raise MelancholyLimit 

155 

156 return act 

157 

158 

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

160class Medicine(Character): 

161 skills = [Ciguatera, Melancholy] 

162 eventhandlers = [CiguateraHandler, MelancholyHandler] 

163 maxlife = 3