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 ActionStage, ForEach, GenericAction, LaunchCard, LifeLost, PlayerDeath 

8from thb.actions import PlayerTurn, UserAction 

9from thb.cards.base import Skill 

10from thb.cards.classes import AttackCard, AttackCardVitalityHandler, t_None, t_Self 

11from thb.characters.base import Character, register_character_to 

12from thb.common import CharChoice 

13from thb.inputlets import ChooseOptionInputlet 

14from thb.mode import THBEventHandler 

15from thb.thbkof import KOFCharacterSwitchHandler 

16 

17 

18# -- code -- 

19class HeterodoxySkipAction(GenericAction): 

20 def apply_action(self): 

21 return True 

22 

23 

24class HeterodoxyHandler(THBEventHandler): 

25 interested = ['action_before'] 

26 execute_before = ['MaidenCostumeHandler'] 

27 

28 def handle(self, evt_type, act): 

29 if evt_type == 'action_before' and ForEach.is_group_effect(act): 

30 tgt = act.target 

31 if not tgt.has_skill(Heterodoxy): return act 

32 

33 g = self.game 

34 for a in reversed(g.action_stack): 

35 if isinstance(a, HeterodoxyAction): 35 ↛ 36line 35 didn't jump to line 36, because the condition on line 35 was never true

36 break 

37 else: 

38 return act 

39 

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

41 return act 

42 

43 act.cancelled = True 

44 g.process_action(HeterodoxySkipAction(tgt, tgt)) 

45 

46 return act 

47 

48 

49class HeterodoxyAction(UserAction): 

50 def apply_action(self): 

51 g = self.game 

52 card = self.associated_card.associated_cards[0] 

53 src = self.source 

54 victim = self.target 

55 tgts = self.target_list[1:] 

56 

57 g.players.reveal(card) 

58 

59 if card.is_card(AttackCard): 

60 src.tags['vitality'] -= 1 

61 

62 # XXX: Use card owned by other 

63 lc = LaunchCard(victim, tgts, card) 

64 

65 g = self.game 

66 g.process_action(lc) 

67 

68 return True 

69 

70 def is_valid(self): 

71 src = self.source 

72 card = self.associated_card.associated_cards[0] 

73 if card.is_card(AttackCard) and src.tags['vitality'] < 1: 

74 if not AttackCardVitalityHandler.is_disabled(src): 74 ↛ 77line 74 didn't jump to line 77, because the condition on line 74 was never false

75 return False 

76 

77 if card.usage != 'launch': 77 ↛ 78line 77 didn't jump to line 78, because the condition on line 77 was never true

78 return False 

79 

80 victim = self.target 

81 tgts = self.target_list[1:] 

82 lc = LaunchCard(victim, tgts, card) 

83 return lc.can_fire() 

84 

85 

86class Heterodoxy(Skill): 

87 no_drop = True 

88 associated_action = HeterodoxyAction 

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

90 usage = 'handover' 

91 

92 def check(self): 

93 cl = self.associated_cards 

94 return ( 

95 cl and len(cl) == 1 and 

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

97 not cl[0].is_card(Skill) and 

98 getattr(cl[0], 'associated_action', None) 

99 ) 

100 

101 def target(self, src, tl): 

102 cl = self.associated_cards 

103 if not cl: return ([], False) 

104 c = cl[0] 

105 tname = c.target.__name__ 

106 

107 tl = [t for t in tl if not t.dead] 

108 

109 if not tl: return [], False 

110 if tl[0] is self.character: return [], False 

111 

112 if tname in ('t_Self', 't_All', 't_AllInclusive'): 112 ↛ 113line 112 didn't jump to line 113, because the condition on line 112 was never true

113 return tl[-1:], True 

114 else: 

115 _tl, valid = c.target(tl[0], tl[1:]) 

116 return [tl[0]] + _tl, valid 

117 

118 

119class Summon(Skill): 

120 associated_action = None 

121 skill_category = ['character', 'passive', 'once'] 

122 target = t_None() 

123 

124 

125class SummonAction(UserAction): 

126 

127 def apply_action(self): 

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

129 

130 skills = tgt.skills 

131 skills = [s for s in skills if 'character' in s.skill_category] 

132 skills = [s for s in skills if 'once' not in s.skill_category] 

133 skills = [s for s in skills if 'awake' not in s.skill_category] 

134 

135 if not skills: return False 135 ↛ exitline 135 didn't return from function 'apply_action', because the return on line 135 wasn't executed

136 

137 mapping = self.mapping = {s.__name__: s for s in skills} 

138 names = list(sorted(mapping.keys())) 

139 

140 g = self.game 

141 choice = g.user_input([src], ChooseOptionInputlet(self, names)) or names[0] 

142 

143 src.tags['summon_used'] = True 

144 src.skills.append(mapping[choice]) 

145 self.choice = mapping[choice] 

146 

147 return True 

148 

149 

150class SummonHandler(THBEventHandler): 

151 interested = ['action_before'] 

152 

153 def handle(self, evt_type, act): 

154 if evt_type == 'action_before' and isinstance(act, PlayerDeath): 

155 g = self.game 

156 p = PlayerTurn.get_current(g).target 

157 if p is act.target: return act 

158 if not p.has_skill(Summon): return act 

159 if p.tags['summon_used']: return act 

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

161 

162 g.process_action(SummonAction(p, act.target)) 

163 

164 return act 

165 

166 

167class SummonKOFAction(UserAction): 

168 def apply_action(self): 

169 # WHOLE BUNCH OF MEGA HACK 

170 old = self.target 

171 g = self.game 

172 old_life, maxlife_delta = old.life, old.maxlife - old.__class__.maxlife 

173 

174 ActionStage.force_break(g) 

175 

176 current = PlayerTurn.get_current(g).target 

177 assert current is old 

178 

179 handler = g.dispatcher.find_by_cls(KOFCharacterSwitchHandler) 

180 tgt = handler.switch(old) 

181 

182 tgt.life = old_life 

183 tgt.maxlife += maxlife_delta 

184 

185 for l in ('cards', 'showncards', 'equips', 'fatetell', 'special'): 

186 s, t = getattr(old, l), getattr(tgt, l) 

187 for i in list(s): 

188 i.move_to(t) 

189 

190 for s in old.skills: 

191 if 'character' not in s.skill_category: 

192 tgt.skills.append(s) 

193 

194 for act in g.action_stack: 

195 # Meh... good enough 

196 if act.source is old: 

197 act.source = tgt 

198 

199 if act.target is old: 

200 act.target = tgt 

201 

202 if isinstance(act, LaunchCard): 

203 act.target_list = [ 

204 tgt if p is old else p 

205 for p in act.target_list 

206 ] 

207 

208 tgt.tags = old.tags 

209 

210 g.chosen[tgt.player].append(CharChoice(old.__class__)) 

211 

212 if tgt.life > tgt.maxlife: 

213 g.process_action(LifeLost(tgt, tgt, tgt.life - tgt.maxlife)) 

214 

215 self.transition = [old, tgt] 

216 

217 g.emit_event('character_debut', (old, tgt)) 

218 

219 return True 

220 

221 

222class SummonKOFCollect(UserAction): 

223 def apply_action(self): 

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

225 g = self.game 

226 g.chosen[src.player].append(CharChoice(tgt.__class__)) 

227 return True 

228 

229 

230class SummonKOFHandler(THBEventHandler): 

231 interested = ['action_apply'] 

232 

233 def handle(self, evt_type, act): 

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

235 g = self.game 

236 src, tgt = act.source, act.target 

237 p = g.get_opponent(tgt) 

238 

239 if not (src is p and p.has_skill(SummonKOF)): 

240 return act 

241 

242 g.process_action(SummonKOFCollect(p, tgt)) 

243 

244 return act 

245 

246 

247class SummonKOF(Skill): 

248 associated_action = SummonKOFAction 

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

250 target = t_Self() 

251 

252 def check(self): 

253 cl = self.associated_cards 

254 return len(cl) == 0 

255 

256 

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

258class Seiga(Character): 

259 skills = [Heterodoxy, Summon] 

260 eventhandlers = [HeterodoxyHandler, SummonHandler] 

261 maxlife = 4 

262 

263 

264@register_character_to('kof') 

265class SeigaKOF(Character): 

266 skills = [SummonKOF] 

267 eventhandlers = [SummonKOFHandler] 

268 maxlife = 4