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

2 

3# -- stdlib -- 

4# -- third party -- 

5# -- own -- 

6from typing import cast 

7from thb.actions import ActionStage, AskForCard, DistributeCards, DrawCardStage, DrawCards, MigrateCardsTransaction 

8from thb.actions import GenericAction, PlayerTurn, Reforge, UserAction, migrate_cards 

9from thb.actions import random_choose_card, ttags, user_choose_cards, user_choose_players 

10from thb.cards.base import Skill 

11from thb.cards.classes import Heal, PhysicalCard, t_None, t_Self 

12from thb.characters.base import Character, register_character_to 

13from thb.mode import THBEventHandler 

14 

15 

16# -- code -- 

17class MiracleHeal(Heal): 

18 pass 

19 

20 

21class MiracleAction(UserAction): 

22 def apply_action(self): 

23 tgt = self.target 

24 g = self.game 

25 g.process_action(DrawCards(tgt, 1)) 

26 

27 ttags(tgt)['miracle_times'] += 1 

28 

29 if ttags(tgt)['miracle_times'] == 3: 

30 candidates = [p for p in g.players if not p.dead and p.life < p.maxlife] 

31 if candidates: 

32 beneficiery, = user_choose_players(self, tgt, candidates) or (None,) 

33 if beneficiery: 33 ↛ 36line 33 didn't jump to line 36, because the condition on line 33 was never false

34 g.process_action(MiracleHeal(tgt, beneficiery)) 

35 

36 return True 

37 

38 def choose_player_target(self, tl): 

39 return tl[-1:], bool(tl) 

40 

41 def cond(self, cl): 

42 return True 

43 

44 def is_valid(self): 

45 tgt = self.target 

46 return len(self.associated_card.associated_cards) == ttags(tgt)['miracle_times'] + 1 

47 

48 

49class Miracle(Skill): 

50 associated_action = MiracleAction 

51 skill_category = ['character', 'active'] 

52 target = t_Self() 

53 usage = 'drop' 

54 

55 def check(self): 

56 cl = self.associated_cards 

57 return cl and all( 

58 c.resides_in is not None and 

59 c.resides_in.type in ( 

60 'cards', 'showncards', 'equips' 

61 ) for c in self.associated_cards 

62 ) 

63 

64 

65class SanaeFaithCollectCardAction(GenericAction): 

66 card_usage = 'handover' 

67 no_reveal = True 

68 

69 def apply_action(self) -> bool: 

70 g = self.game 

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

72 cards = user_choose_cards(self, tgt, ('cards', 'showncards')) 

73 c = cards[0] if cards else random_choose_card(g, [tgt.cards, tgt.showncards]) 

74 src.player.reveal(c) 

75 migrate_cards([c], src.cards) 

76 

77 return True 

78 

79 def cond(self, cl): 

80 return len(cl) == 1 and \ 

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

82 not cl[0].is_card(Skill) 

83 

84 

85class SanaeFaithReturnCardAction(GenericAction): 

86 card_usage = 'handover' 

87 no_reveal = True 

88 

89 def apply_action(self) -> bool: 

90 g = self.game 

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

92 cards = user_choose_cards(self, src, ('cards', 'showncards', 'equips')) 

93 c = cards[0] if cards else random_choose_card(g, [src.cards, src.showncards, src.equips]) 

94 if not c: 94 ↛ 95line 94 didn't jump to line 95, because the condition on line 94 was never true

95 return False 

96 

97 tgt.player.reveal(c) 

98 migrate_cards([c], tgt.cards) 

99 

100 return True 

101 

102 def cond(self, cl): 

103 return len(cl) == 1 and not cl[0].is_card(Skill) 

104 

105 

106class SanaeFaithAction(UserAction): 

107 def apply_action(self): 

108 src = self.source 

109 tl = self.target_list 

110 g = self.game 

111 

112 for p in tl: 

113 g.process_action(SanaeFaithCollectCardAction(src, p)) 

114 

115 g.deck.shuffle(src.cards) 

116 

117 for p in tl: 

118 g.process_action(SanaeFaithReturnCardAction(src, p)) 

119 

120 ttags(src)['faith'] = True 

121 

122 return True 

123 

124 def is_valid(self): 

125 src = self.source 

126 return not ttags(src)['faith'] 

127 

128 

129class SanaeFaith(Skill): 

130 associated_action = SanaeFaithAction 

131 skill_category = ['character', 'active'] 

132 usage = 'launch' 

133 

134 def target(self, src, tl): 

135 tl = [t for t in tl if not t.dead and (t.cards or t.showncards)] 

136 try: 

137 tl.remove(src) 

138 except ValueError: 

139 pass 

140 

141 return (tl[:2], bool(len(tl))) 

142 

143 def check(self): 

144 return not self.associated_cards 

145 

146 

147class SanaeFaithKOF(Skill): 

148 associated_action = None 

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

150 target = t_None() 

151 

152 

153class SanaeFaithKOFDrawCards(DrawCards): 

154 pass 

155 

156 

157class SanaeFaithKOFHandler(THBEventHandler): 

158 interested = ['post_card_migration'] 

159 

160 def handle(self, evt_type, arg): 

161 if evt_type == 'post_card_migration': 161 ↛ 195line 161 didn't jump to line 195, because the condition on line 161 was never false

162 trans = cast(MigrateCardsTransaction, arg) 

163 if isinstance(trans.action, (DistributeCards, DrawCardStage)): 

164 return arg 

165 

166 g = self.game 

167 

168 for m in trans.movements: 

169 if not m.to.owner: 

170 continue 

171 

172 if m.to.type not in ('cards', 'showncards', 'equips'): 

173 continue 

174 

175 if m.fr.owner is m.to.owner: 

176 continue 

177 

178 tgt = g.get_opponent(m.to.owner) 

179 

180 if not tgt.has_skill(SanaeFaithKOF): 

181 continue 

182 

183 turn = PlayerTurn.get_current(g) 

184 if not turn: 184 ↛ 185line 184 didn't jump to line 185, because the condition on line 184 was never true

185 continue 

186 

187 stage = turn.current_stage 

188 if stage.target is not m.to.owner or not isinstance(stage, ActionStage): 

189 continue 

190 

191 g.process_action(SanaeFaithKOFDrawCards(tgt, 1)) 

192 

193 break 

194 

195 return arg 

196 

197 

198class GodDescendant(Skill): 

199 associated_action = None 

200 target = t_None() 

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

202 

203 

204class GodDescendantEffect(UserAction): 

205 def __init__(self, source, target, target_list, card): 

206 self.source = source 

207 self.target = target 

208 self.target_list = target_list 

209 self.card = card 

210 

211 def apply_action(self): 

212 g = self.game 

213 src, tgt, tl = self.source, self.target, self.target_list 

214 g.process_action(Reforge(src, tgt, self.card)) 

215 assert tgt in tl 

216 tl.remove(tgt) 

217 return True 

218 

219 

220class GodDescendantAction(AskForCard): 

221 card_usage = 'reforge' 

222 

223 def __init__(self, target, target_list): 

224 AskForCard.__init__(self, target, target, PhysicalCard, ('cards', 'showncards', 'equips')) 

225 self.target_list = target_list 

226 

227 def process_card(self, c): 

228 g = self.game 

229 return g.process_action(GodDescendantEffect(self.source, self.target, self.target_list, c)) 

230 

231 

232class GodDescendantHandler(THBEventHandler): 

233 interested = ['choose_target'] 

234 execute_before = ['MaidenCostumeHandler'] 

235 

236 def handle(self, evt_type, arg): 

237 if evt_type == 'choose_target': 237 ↛ 249line 237 didn't jump to line 249, because the condition on line 237 was never false

238 act, tl = arg 

239 if 'group_effect' not in act.card.category: 

240 return arg 

241 

242 g = self.game 

243 for tgt in tl: 

244 if not tgt.has_skill(GodDescendant): 

245 continue 

246 

247 g.process_action(GodDescendantAction(tgt, tl)) 

248 

249 return arg 

250 

251 def cond(self, cl): 

252 return len(cl) == 1 

253 

254 

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

256class Sanae(Character): 

257 skills = [Miracle, SanaeFaith, GodDescendant] 

258 eventhandlers = [GodDescendantHandler] 

259 maxlife = 3 

260 

261 

262@register_character_to('kof') 

263class SanaeKOF(Character): 

264 skills = [Miracle, SanaeFaithKOF, GodDescendant] 

265 eventhandlers = [SanaeFaithKOFHandler, GodDescendantHandler] 

266 maxlife = 3