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

5from enum import Enum 

6from itertools import cycle 

7from typing import Any, Dict, List 

8import logging 

9 

10# -- third party -- 

11# -- own -- 

12from game.base import BootstrapAction, GameEnded, InputTransaction, InterruptActionFlow, Player 

13from game.base import list_shuffle 

14from thb.actions import DistributeCards, PlayerDeath, PlayerTurn, RevealRole 

15from thb.cards.base import Deck 

16from thb.cards.definition import kof_card_definition 

17from thb.characters.base import Character 

18from thb.common import CharChoice, PlayerRole, build_choices_shared, roll 

19from thb.inputlets import ChooseGirlInputlet 

20from thb.item import GameItem 

21from thb.mode import THBEventHandler, THBattle 

22from utils.misc import BatchList 

23 

24 

25# -- code -- 

26log = logging.getLogger('THBattleKOF') 

27 

28 

29class DeathHandler(THBEventHandler): 

30 interested = ['action_apply'] 

31 

32 game: 'THBattleKOF' 

33 

34 def handle(self, evt_type: str, act: PlayerDeath): 

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

36 if not isinstance(act, PlayerDeath): return act 

37 tgt = act.target 

38 p = tgt.player 

39 

40 g = self.game 

41 pl = g.players.player 

42 

43 if len(g.chosen[p]) <= 2: # 5(total chosen) - 3(available characters) = 2 

44 pl.remove(p) 

45 raise GameEnded(pl) 

46 

47 if g.is_dropped(pl[0]): 47 ↛ 48line 47 didn't jump to line 48, because the condition on line 47 was never true

48 raise GameEnded([pl[1]]) 

49 

50 if g.is_dropped(pl[1]): 50 ↛ 51line 50 didn't jump to line 51, because the condition on line 50 was never true

51 raise GameEnded([pl[0]]) 

52 

53 return act 

54 

55 

56class KOFCharacterSwitchHandler(THBEventHandler): 

57 interested = ['action_after', 'action_before', 'action_stage_action'] 

58 game: THBattleKOF 

59 

60 def handle(self, evt_type, act): 

61 cond = evt_type in ('action_before', 'action_after') 

62 cond = cond and isinstance(act, PlayerTurn) 

63 cond = cond or evt_type == 'action_stage_action' 

64 cond and self.do_switch_dead() 

65 return act 

66 

67 def do_switch_dead(self): 

68 g = self.game 

69 

70 # for p in [p for p in g.players if p.dead and p.chosen]: 

71 for ch in [ch for ch in g.players if ch.dead]: 

72 new = self.switch(ch) 

73 g.process_action(DistributeCards(new, 4)) 

74 g.emit_event('character_debut', (ch, new)) 

75 

76 def switch(self, old: Character): 

77 g = self.game 

78 p = old.player 

79 mapping = {p: g.chosen[p]} 

80 

81 with InputTransaction('ChooseGirl', [p], mapping=mapping) as trans: 

82 choice = g.user_input([p], ChooseGirlInputlet(g, mapping), timeout=30, trans=trans) 

83 choice = choice or g.chosen[p][0] 

84 

85 g.players.reveal(choice) 

86 new = choice.char_cls(p) 

87 g.players.replace(old, new) 

88 g.refresh_dispatcher() 

89 g.chosen[p].remove(choice) 

90 return new 

91 

92 

93class THBKOFRole(Enum): 

94 HIDDEN = 0 

95 HAKUREI = 1 

96 MORIYA = 2 

97 

98 

99class THBattleKOFBootstrap(BootstrapAction): 

100 game: 'THBattleKOF' 

101 

102 def __init__(self, params: Dict[str, Any], 

103 items: Dict[Player, List[GameItem]], 

104 players: BatchList[Player]): 

105 self.source = self.target = None 

106 self.params = params 

107 self.items = items 

108 self.players = players 

109 

110 def apply_action(self) -> bool: 

111 g = self.game 

112 

113 g.deck = Deck(g, kof_card_definition) 

114 pl = self.players 

115 A, B = pl 

116 g.roles = { 

117 A: PlayerRole(THBKOFRole), 

118 B: PlayerRole(THBKOFRole), 

119 } 

120 g.roles[A].set(THBKOFRole.HAKUREI) 

121 g.roles[B].set(THBKOFRole.MORIYA) 

122 

123 # choose girls --> 

124 from thb.characters import get_characters 

125 chars = get_characters('common', 'kof') 

126 

127 A, B = roll(g, pl, self.items) 

128 order = [A, B, B, A, A, B, B, A, A, B] 

129 

130 choices, imperial_choices = build_choices_shared( 

131 g, pl, self.items, 

132 candidates=chars, spec={'num': 10, 'akaris': 4}, 

133 ) 

134 

135 g.chosen = {A: [], B: []} 

136 

137 with InputTransaction('ChooseGirl', pl, mapping=choices) as trans: 

138 for p, c in imperial_choices.items(): 138 ↛ 139line 138 didn't jump to line 139, because the loop on line 138 never started

139 c.chosen = p 

140 g.chosen[p].append(c) 

141 trans.notify('girl_chosen', (p, c)) 

142 order.remove(p) 

143 

144 for p in order: 

145 c = g.user_input([p], ChooseGirlInputlet(g, {p: choices}), 10, 'single', trans) 

146 # c = c or next(choices[p], lambda c: not c.chosen, None) 

147 c = c or next(c for c in choices if not c.chosen) 147 ↛ exitline 147 didn't finish the generator expression on line 147

148 

149 c.chosen = p 

150 g.chosen[p].append(c) 

151 

152 trans.notify('girl_chosen', (p, c)) 

153 

154 # reveal akaris for themselves 

155 for p in [A, B]: 

156 for c in g.chosen[p]: 

157 c.akari = False 

158 p.reveal(c) 

159 del c.chosen 

160 

161 list_shuffle(g, g.chosen[A], A) 

162 list_shuffle(g, g.chosen[B], B) 

163 

164 with InputTransaction('ChooseGirl', pl, mapping=g.chosen) as trans: 

165 ilet = ChooseGirlInputlet(g, g.chosen) 

166 ilet.with_post_process(lambda p, rst: trans.notify('girl_chosen', (p, rst)) or rst) 

167 rst = g.user_input([A, B], ilet, type='all', trans=trans) 

168 

169 def s(p): 

170 c = rst[p] or g.chosen[p][0] 

171 g.chosen[p].remove(c) 

172 

173 c.akari = False 

174 pl.reveal(c) 

175 cls = c.char_cls 

176 assert cls 

177 ch = cls(p) 

178 g.refresh_dispatcher() 

179 g.emit_event('switch_character', (None, ch)) 

180 

181 return ch 

182 

183 cA, cB = g.players = BatchList([s(A), s(B)]) 

184 

185 for p in pl: 

186 g.process_action(RevealRole(g.roles[p], pl)) 

187 

188 g.refresh_dispatcher() 

189 g.emit_event('game_begin', g) 

190 

191 g.process_action(DistributeCards(cA, amount=4)) 

192 g.process_action(DistributeCards(cB, amount=3)) 

193 

194 for ch in g.players: 

195 g.emit_event('character_debut', (None, ch)) 

196 

197 for i in cycle([0, 1]): 197 ↛ 213line 197 didn't jump to line 213, because the loop on line 197 didn't complete

198 ch = g.players[i] 

199 if i >= 6000: break 199 ↛ 213line 199 didn't jump to line 213, because the break on line 199 wasn't executed

200 if ch.dead: 200 ↛ 201line 200 didn't jump to line 201, because the condition on line 200 was never true

201 handler = g.dispatcher.find_by_cls(KOFCharacterSwitchHandler) 

202 assert handler, 'WTF?!' 

203 handler.do_switch_dead() 

204 ch = g.players[i] # player changed 

205 

206 assert not ch.dead 

207 

208 try: 

209 g.process_action(PlayerTurn(ch)) 

210 except InterruptActionFlow: 

211 pass 

212 

213 return True 

214 

215 

216class THBattleKOF(THBattle): 

217 n_persons = 2 

218 game_ehs = [ 

219 DeathHandler, 

220 KOFCharacterSwitchHandler, 

221 ] 

222 bootstrap = THBattleKOFBootstrap 

223 

224 chosen: Dict[Player, List[CharChoice]] 

225 

226 def get_opponent(g: THBattleKOF, ch: Character): 

227 a, b = g.players 

228 return {a: b, b: a}[ch] 

229 

230 def can_leave(g: THBattle, p: Player) -> bool: 

231 return False