Coverage for thb/thbkof.py : 89%
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
4# -- stdlib --
5from enum import Enum
6from itertools import cycle
7from typing import Any, Dict, List
8import logging
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
25# -- code --
26log = logging.getLogger('THBattleKOF')
29class DeathHandler(THBEventHandler):
30 interested = ['action_apply']
32 game: 'THBattleKOF'
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
40 g = self.game
41 pl = g.players.player
43 if len(g.chosen[p]) <= 2: # 5(total chosen) - 3(available characters) = 2
44 pl.remove(p)
45 raise GameEnded(pl)
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]])
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]])
53 return act
56class KOFCharacterSwitchHandler(THBEventHandler):
57 interested = ['action_after', 'action_before', 'action_stage_action']
58 game: THBattleKOF
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
67 def do_switch_dead(self):
68 g = self.game
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))
76 def switch(self, old: Character):
77 g = self.game
78 p = old.player
79 mapping = {p: g.chosen[p]}
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]
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
93class THBKOFRole(Enum):
94 HIDDEN = 0
95 HAKUREI = 1
96 MORIYA = 2
99class THBattleKOFBootstrap(BootstrapAction):
100 game: 'THBattleKOF'
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
110 def apply_action(self) -> bool:
111 g = self.game
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)
123 # choose girls -->
124 from thb.characters import get_characters
125 chars = get_characters('common', 'kof')
127 A, B = roll(g, pl, self.items)
128 order = [A, B, B, A, A, B, B, A, A, B]
130 choices, imperial_choices = build_choices_shared(
131 g, pl, self.items,
132 candidates=chars, spec={'num': 10, 'akaris': 4},
133 )
135 g.chosen = {A: [], B: []}
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)
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
149 c.chosen = p
150 g.chosen[p].append(c)
152 trans.notify('girl_chosen', (p, c))
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
161 list_shuffle(g, g.chosen[A], A)
162 list_shuffle(g, g.chosen[B], B)
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)
169 def s(p):
170 c = rst[p] or g.chosen[p][0]
171 g.chosen[p].remove(c)
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))
181 return ch
183 cA, cB = g.players = BatchList([s(A), s(B)])
185 for p in pl:
186 g.process_action(RevealRole(g.roles[p], pl))
188 g.refresh_dispatcher()
189 g.emit_event('game_begin', g)
191 g.process_action(DistributeCards(cA, amount=4))
192 g.process_action(DistributeCards(cB, amount=3))
194 for ch in g.players:
195 g.emit_event('character_debut', (None, ch))
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
206 assert not ch.dead
208 try:
209 g.process_action(PlayerTurn(ch))
210 except InterruptActionFlow:
211 pass
213 return True
216class THBattleKOF(THBattle):
217 n_persons = 2
218 game_ehs = [
219 DeathHandler,
220 KOFCharacterSwitchHandler,
221 ]
222 bootstrap = THBattleKOFBootstrap
224 chosen: Dict[Player, List[CharChoice]]
226 def get_opponent(g: THBattleKOF, ch: Character):
227 a, b = g.players
228 return {a: b, b: a}[ch]
230 def can_leave(g: THBattle, p: Player) -> bool:
231 return False