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 DrawCards, DropCardStage, DummyAction, FinalizeStage, GenericAction 

8from thb.actions import LifeLost, MaxLifeChange, Pindian, PlayerDeath, PlayerTurn, TryRevive 

9from thb.actions import UserAction, ttags 

10from thb.cards.base import Skill, t_None, t_OtherOne 

11from thb.cards.basic import Heal 

12from thb.characters.base import Character, register_character_to 

13from thb.inputlets import ChooseOptionInputlet 

14from thb.mode import THBEventHandler 

15 

16 

17# -- code -- 

18class GuidedDeathLifeLost(LifeLost): 

19 pass 

20 

21 

22class GuidedDeathEffect(GenericAction): 

23 def __init__(self, source, target_list): 

24 self.source = source 

25 self.target = source 

26 self.target_list = target_list 

27 

28 def apply_action(self): 

29 g = self.game 

30 

31 for p in self.target_list: 

32 g.process_action(GuidedDeathLifeLost(p, p)) 

33 

34 return True 

35 

36 

37class GuidedDeathAction(UserAction): 

38 def apply_action(self): 

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

40 if tgt.dead: return True 

41 g = self.game 

42 ttags(src)['guided_death_active_use'] = tgt 

43 g.process_action(GuidedDeathLifeLost(src, tgt, 1)) 

44 return True 

45 

46 

47class GuidedDeath(Skill): 

48 associated_action = GuidedDeathAction 

49 skill_category = ('character', 'active') 

50 target = t_OtherOne() 

51 usage = 'drop' 

52 

53 def check(self): 

54 cl = self.associated_cards 

55 return len(cl) == 0 

56 

57 

58class GuidedDeathHandler(THBEventHandler): 

59 interested = ('action_apply',) 

60 execute_before = ('SoulDrainHandler',) 

61 

62 def handle(self, evt_type, act): 

63 if evt_type == 'action_apply' and isinstance(act, FinalizeStage): 

64 g = self.game 

65 

66 src = act.target 

67 if not src.has_skill(GuidedDeath) or src.dead: 

68 return act 

69 

70 p = ttags(src)['guided_death_active_use'] 

71 if p: 71 ↛ 72line 71 didn't jump to line 72, because the condition on line 71 was never true

72 g.process_action(Heal(p, p, 1)) 

73 else: 

74 tl = [p for p in g.players.rotate_to(src) if p.life == 1 and p is not src] 

75 if not tl: 

76 return act 

77 

78 g.process_action(GuidedDeathEffect(src, tl)) 

79 

80 return act 

81 

82 

83class SoulDrain(Skill): 

84 associated_action = None 

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

86 target = t_None() 

87 

88 

89class SoulDrainEffect(GenericAction): 

90 def apply_action(self): 

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

92 g = self.game 

93 

94 assert tgt.life <= 0 

95 

96 g.process_action(DrawCards(src, 1)) 

97 

98 if not tgt.cards and not tgt.showncards: 

99 return True 

100 

101 if src is tgt: 101 ↛ 102line 101 didn't jump to line 102, because the condition on line 101 was never true

102 return True 

103 

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

105 if g.process_action(Pindian(src, tgt)): 

106 g.process_action(MaxLifeChange(src, tgt, -tgt.maxlife + 1)) 

107 else: 

108 g.process_action(Heal(src, tgt, -tgt.life + 1)) 

109 

110 return True 

111 

112 

113class SoulDrainHandler(THBEventHandler): 

114 interested = ['action_before'] 

115 

116 def handle(self, evt_type, act): 

117 if evt_type == 'action_before' and isinstance(act, TryRevive): 

118 g = self.game 

119 tgt = act.target 

120 

121 for p in g.players: 

122 if p.has_skill(SoulDrain) and not p.dead and p is not tgt: 

123 break 

124 else: 

125 return act 

126 

127 for a in reversed(g.action_stack): 127 ↛ 134line 127 didn't jump to line 134, because the loop on line 127 didn't complete

128 if isinstance(a, PlayerTurn): 

129 if a.target is not p: 

130 return act 

131 else: 

132 break 

133 else: 

134 return act 

135 

136 g.process_action(SoulDrainEffect(p, tgt)) 

137 

138 if tgt.life > 0: 

139 # Abort TryRevive process 

140 return DummyAction(p, act.target) 

141 else: 

142 return act 

143 

144 return act 

145 

146 

147class PerfectCherryBlossomHandler(THBEventHandler): 

148 interested = ['action_apply', 'action_before'] 

149 

150 def handle(self, evt_type, act): 

151 if evt_type == 'action_apply' and isinstance(act, PlayerDeath): 

152 g = self.game 

153 

154 for p in g.players: 

155 if p.has_skill(PerfectCherryBlossom) and not p.dead and p is not act.target: 

156 break 

157 else: 

158 return act 

159 

160 g.process_action(PerfectCherryBlossomExtractAction(p, act.target)) 

161 

162 elif evt_type == 'action_before' and isinstance(act, DropCardStage): 

163 g = self.game 

164 tgt = act.target 

165 if not tgt.has_skill(PerfectCherryBlossom): 

166 return act 

167 

168 act.dropn -= tgt.maxlife - tgt.life 

169 

170 return act 

171 

172 

173class PerfectCherryBlossomExtractAction(UserAction): 

174 def apply_action(self): 

175 g = self.game 

176 src = self.source 

177 if src.life >= src.maxlife: 

178 choice = 'maxlife' 

179 elif g.user_input([src], ChooseOptionInputlet(self, ('life', 'maxlife'))) == 'maxlife': 

180 choice = 'maxlife' 

181 else: 

182 choice = 'life' 

183 

184 if choice == 'life': 

185 g.process_action(Heal(src, src, 1)) 

186 else: 

187 g.process_action(MaxLifeChange(src, src, 1)) 

188 

189 return True 

190 

191 

192class PerfectCherryBlossom(Skill): 

193 associated_action = None 

194 skill_category = ('character', 'passive') 

195 target = t_None() 

196 

197 

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

199class Yuyuko(Character): 

200 skills = [GuidedDeath, SoulDrain, PerfectCherryBlossom] 

201 eventhandlers = [GuidedDeathHandler, SoulDrainHandler, PerfectCherryBlossomHandler] 

202 maxlife = 3