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: 

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: 

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): 

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 execute_after = ['DeathHandler'] 

150 

151 def handle(self, evt_type, act): 

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

153 g = self.game 

154 

155 for p in g.players: 

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

157 break 

158 else: 

159 return act 

160 

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

162 

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

164 g = self.game 

165 tgt = act.target 

166 if not tgt.has_skill(PerfectCherryBlossom): 

167 return act 

168 

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

170 

171 return act 

172 

173 

174class PerfectCherryBlossomExtractAction(UserAction): 

175 def apply_action(self): 

176 g = self.game 

177 src = self.source 

178 if src.life >= src.maxlife: 

179 choice = 'maxlife' 

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

181 choice = 'maxlife' 

182 else: 

183 choice = 'life' 

184 

185 if choice == 'life': 

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

187 else: 

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

189 

190 return True 

191 

192 

193class PerfectCherryBlossom(Skill): 

194 associated_action = None 

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

196 target = t_None() 

197 

198 

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

200class Yuyuko(Character): 

201 skills = [GuidedDeath, SoulDrain, PerfectCherryBlossom] 

202 eventhandlers = [GuidedDeathHandler, SoulDrainHandler, PerfectCherryBlossomHandler] 

203 maxlife = 3