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 

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

48 raise GameEnded([pl[1]]) 

49 

50 if g.is_dropped(pl[1]): 

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 assert choice.char_cls 

87 new = choice.char_cls(p) 

88 g.players.replace(old, new) 

89 g.refresh_dispatcher() 

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

91 return new 

92 

93 

94class THBKOFRole(Enum): 

95 HIDDEN = 0 

96 HAKUREI = 1 

97 MORIYA = 2 

98 

99 

100class THBattleKOFBootstrap(BootstrapAction): 

101 game: 'THBattleKOF' 

102 

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

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

105 players: BatchList[Player]): 

106 self.source = self.target = None 

107 self.params = params 

108 self.items = items 

109 self.players = players 

110 

111 def apply_action(self) -> bool: 

112 g = self.game 

113 

114 g.deck = Deck(g, kof_card_definition) 

115 pl = self.players 

116 A, B = pl 

117 g.roles = { 

118 A: PlayerRole(THBKOFRole), 

119 B: PlayerRole(THBKOFRole), 

120 } 

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

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

123 

124 # choose girls --> 

125 from thb.characters import get_characters 

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

127 

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

129 order = [A, B, B, A, A, B, B, A, A, B] 

130 

131 choices, imperial_choices = build_choices_shared( 

132 g, pl, self.items, 

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

134 ) 

135 

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

137 

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

139 for p, c in imperial_choices.items(): 

140 c.chosen = p 

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

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

143 order.remove(p) 

144 

145 for p in order: 

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

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

148 c = c or next(c for c in choices if not c.chosen) 

149 

150 c.chosen = p 

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

152 

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

154 

155 # reveal akaris for themselves 

156 for p in [A, B]: 

157 for c in g.chosen[p]: 

158 c.akari = False 

159 p.reveal(c) 

160 del c.chosen 

161 

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

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

164 

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

166 ilet = ChooseGirlInputlet(g, g.chosen) 

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

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

169 

170 def s(p): 

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

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

173 

174 c.akari = False 

175 pl.reveal(c) 

176 cls = c.char_cls 

177 assert cls 

178 ch = cls(p) 

179 g.refresh_dispatcher() 

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

181 

182 return ch 

183 

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

185 

186 for p in pl: 

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

188 

189 g.refresh_dispatcher() 

190 g.emit_event('game_begin', g) 

191 

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

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

194 

195 for ch in g.players: 

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

197 

198 for i in cycle([0, 1]): 

199 ch = g.players[i] 

200 if i >= 6000: break 

201 if ch.dead: 

202 handler = g.dispatcher.find_by_cls(KOFCharacterSwitchHandler) 

203 assert handler, 'WTF?!' 

204 handler.do_switch_dead() 

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

206 

207 assert not ch.dead 

208 

209 try: 

210 g.process_action(PlayerTurn(ch)) 

211 except InterruptActionFlow: 

212 pass 

213 

214 return True 

215 

216 

217class THBattleKOF(THBattle): 

218 n_persons = 2 

219 game_ehs = [ 

220 DeathHandler, 

221 KOFCharacterSwitchHandler, 

222 ] 

223 bootstrap = THBattleKOFBootstrap 

224 

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

226 

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

228 a, b = g.players 

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

230 

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

232 return False