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 thb.actions import Damage, DrawCards, LaunchCard, LifeLost, PlayerTurn, UserAction 

8from thb.actions import migrate_cards, skill_check, skill_wrap, user_choose_cards 

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

10from thb.cards.classes import Heal, SealingArrayCard, TreatAs 

11from thb.cards.definition import BasicCard 

12from thb.characters.base import Character, register_character_to 

13from thb.inputlets import ChooseOptionInputlet 

14from thb.mode import THBEventHandler 

15 

16 

17# -- code -- 

18class Dilemma(Skill): 

19 associated_action = None 

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

21 target = t_None() 

22 

23 

24class DilemmaDamageAction(UserAction): 

25 card_usage = 'handover' 

26 

27 def apply_action(self): 

28 src = self.source 

29 tgt = self.target 

30 

31 cards = user_choose_cards(self, tgt, ('cards', 'showncards', 'equips')) 

32 g = self.game 

33 if cards: 

34 self.peer_action = 'card' 

35 g.players.exclude(tgt).reveal(cards) 

36 migrate_cards(cards, src.cards) 

37 else: 

38 self.peer_action = 'life' 

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

40 

41 return True 

42 

43 def cond(self, cards): 

44 if len(cards) != 1: return False 

45 card = cards[0] 

46 if card.is_card(Skill): 

47 return False 

48 

49 if card.resides_in.type not in ( 49 ↛ exitline 49 didn't jump to the function exit

50 'cards', 'showncards', 'equips' 

51 ): return False 

52 

53 return card.suit == Card.DIAMOND 

54 

55 

56class DilemmaHealAction(DrawCards): 

57 def __init__(self, source, target, amount=2): 

58 self.source = source 

59 self.target = target 

60 self.amount = amount 

61 self.back = False 

62 

63 

64class DilemmaHandler(THBEventHandler): 

65 interested = ('action_after',) 

66 execute_after = ( 

67 'DyingHandler', 

68 'AyaRoundfanHandler', 

69 'NenshaPhoneHandler', 

70 ) 

71 

72 def handle(self, evt_type, act): 

73 if evt_type != 'action_after': return act 73 ↛ exitline 73 didn't return from function 'handle', because the return on line 73 wasn't executed

74 if not isinstance(act, (Damage, Heal)): return act 

75 

76 src = act.source 

77 tgt = act.target 

78 if tgt.dead: return act 

79 if not tgt.has_skill(Dilemma): return act 

80 if not src: return act 

81 

82 self.dilemma_type = 'negative' if isinstance(act, Damage) else 'positive' 

83 g = self.game 

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

85 return act 

86 

87 if isinstance(act, Damage): 

88 g.process_action(DilemmaDamageAction(tgt, src)) 

89 else: # Heal 

90 g.process_action(DilemmaHealAction(tgt, src, 1)) 

91 

92 return act 

93 

94 

95class ImperishableNight(TreatAs, Skill): 

96 treat_as = SealingArrayCard 

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

98 

99 def check(self): 

100 g = self.game 

101 current = PlayerTurn.get_current(g).target 

102 return current is not self.character 

103 

104 

105class ImperishableNightHandler(THBEventHandler): 

106 interested = ['action_after'] 

107 card_usage = 'launch' 

108 

109 def handle(self, evt_type, act): 

110 if evt_type != 'action_after': return act 110 ↛ exitline 110 didn't return from function 'handle', because the return on line 110 wasn't executed

111 if not isinstance(act, LaunchCard): return act 

112 

113 g = self.game 

114 

115 card = act.card 

116 if not card: return act 116 ↛ exitline 116 didn't return from function 'handle', because the return on line 116 wasn't executed

117 if not isinstance(card, BasicCard): return act 

118 if card.color != Card.RED: return act 

119 

120 if card.is_card(VirtualCard): 120 ↛ 121line 120 didn't jump to line 121, because the condition on line 120 was never true

121 rawcards = VirtualCard.unwrap([card]) 

122 else: 

123 rawcards = [card] 

124 

125 if not all( 125 ↛ exitline 125 didn't jump to the function exit

126 c.resides_in is None or c.resides_in.type == 'droppedcard' 

127 for c in rawcards 

128 ): return act 

129 

130 tgt = act.source 

131 self.target = tgt # for ui 

132 

133 if tgt.dead: return act 

134 

135 try: 

136 current = PlayerTurn.get_current(g).target 

137 except IndexError: 

138 current = None 

139 

140 for p in g.players: 

141 if p.dead or p is tgt: continue 

142 if not p.has_skill(ImperishableNight): continue 

143 if p is current: continue 

144 

145 if not g.user_input([p], ChooseOptionInputlet(self, (False, True))): 

146 continue 

147 

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

149 

150 if cards: 

151 g.players.reveal(cards) 

152 skill = skill_wrap(p, [ImperishableNight], cards, {}) 

153 assert skill_check(skill) # should not fail 

154 g.deck.register_vcard(skill) 

155 rst = g.process_action(LaunchCard(p, [tgt], skill)) 

156 assert rst 

157 

158 return act 

159 

160 def cond(self, cards): 

161 if len(cards) != 1: return False 

162 card = cards[0] 

163 if card.resides_in.type not in ( 163 ↛ exitline 163 didn't jump to the function exit

164 'cards', 'showncards', 'equips' 

165 ): return False 

166 if 'skill' in card.category: return False 

167 if card.color != Card.RED: return False 

168 return bool(set(card.category) & {'basic', 'equipment'}) 

169 

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

171 tgt = self.target 

172 skill = skill_wrap(p, [ImperishableNight], cl, {}) 

173 return LaunchCard(p, [tgt], skill).can_fire() 

174 

175 

176@register_character_to('common') 

177class Kaguya(Character): 

178 skills = [Dilemma, ImperishableNight] 

179 eventhandlers = [DilemmaHandler, ImperishableNightHandler] 

180 maxlife = 3