class GameOverMode(Mode): col_spacing = 0.25 row_spacing = 0.04 stat_font_size = 0.04 left_most = -0.33 top_most = 0.4 keymap = { action.quit: False, } def enter(self): self.mouse.show() self.mouse.absolute() self.task_action('escape', action.quit, True) self.render_game_over_screen() def exit(self): self.game_over_menu.hide() for text in self.text.values(): text.hide() del self.game_over_menu del self.text def render_game_over_screen(self): self.game_over_menu = GenericMenu( title=f"Game over! {self.game.winner.name} wins!", frame_color=(0, 0, 0, 0.5), title_pos=(0, 0, 0.55), ) self.game_over_menu.show() self.text = { 'stat_names': OnscreenText( text='Points\n', style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 0.5), pos=(self.left_most, self.top_most - self.row_spacing), scale=self.stat_font_size, ), } for i, player in enumerate(self.game.players): self.text[player.name] = OnscreenText( text=f"{player.name}\n{player.points}", style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 0.5), pos=(self.left_most + self.col_spacing * (i + 1), self.top_most), scale=self.stat_font_size, )
class ShotViewer(Interface): is_game = False def __init__(self, *args, **kwargs): Interface.__init__(self, *args, **kwargs) self.create_standby_screen() self.create_instructions() self.create_title('') self.stop() def create_title(self, title): self.title_node = OnscreenText( text = title, pos = (-1.55, -0.93), scale = ani.menu_text_scale*0.7, fg = (1,1,1,1), align = TextNode.ALeft, parent = aspect2d, ) self.title_node.hide() def create_instructions(self): self.instructions = OnscreenText( text = "Press <escape> to exit", pos = (-1.55, 0.93), scale = ani.menu_text_scale*0.7, fg = (1,1,1,1), align = TextNode.ALeft, parent = aspect2d, ) self.instructions.hide() def create_standby_screen(self): self.standby_screen = GenericMenu(frame_color=(0.3,0.3,0.3,1)) self.standby_screen.add_image(ani.logo_paths['default'], pos=(0,0,0), scale=(0.5, 1, 0.44)) text = OnscreenText( text = 'GUI standing by...', style = 1, fg = (1, 1, 1, 1), parent = self.standby_screen.titleMenu, align = TextNode.ALeft, pos = (-1.55,0.93), scale = 0.8*ani.menu_text_scale, ) def show(self, shot_or_shots=None, title=''): if shot_or_shots is None: # No passed shots. This is ok if self.shots has already been defined, but will complain # otherwise if not len(self.shots): raise ConfigError("ShotViewer.show :: No shots passed and no shots set.") else: # Create a new SystemCollection based on type of shot_or_shots if issubclass(type(shot_or_shots), System): self.shots = SystemCollection() self.shots.append(shot_or_shots) elif issubclass(type(shot_or_shots), SystemCollection): self.shots = shot_or_shots if self.shots.active is None: self.shots.set_active(0) self.standby_screen.hide() self.instructions.show() self.create_title(title) self.title_node.show() self.init_help_page() self.help_hint.hide() self.mouse = Mouse() self.init_system_nodes() self.init_hud() params = dict( init_animations = True, single_instance = True, ) self.change_mode('shot', enter_kwargs=params) self.player_cam.load_state('last_scene', ok_if_not_exists=True) self.taskMgr.run() def stop(self): self.standby_screen.show() self.instructions.hide() self.title_node.hide() base.graphicsEngine.renderFrame() base.graphicsEngine.renderFrame() self.taskMgr.stop() def finalizeExit(self): self.stop()
class CalculateMode(Mode): keymap = { action.move: False, action.quit: False, action.zoom: False, action.show_help: False, } def enter(self): self.mouse.hide() self.mouse.relative() self.mouse.track() self.shot_sim_overlay = GenericMenu( title='Calculating shot...', frame_color=(0, 0, 0, 0.4), title_pos=(0, 0, -0.2), ) self.add_task(self.run_simulation, 'run_simulation', taskChain='simulation') self.task_action('escape', action.quit, True) self.task_action('mouse1', action.zoom, True) self.task_action('mouse1-up', action.zoom, False) self.task_action('a', action.aim, True) self.task_action('v', action.move, True) self.task_action('v-up', action.move, False) self.task_action('h', action.show_help, True) self.add_task(self.calculate_view_task, 'calculate_view_task') def exit(self): self.remove_task('calculate_view_task') self.shot_sim_overlay.hide() def calculate_view_task(self, task): if not 'run_simulation' in self.tasks: # simulation calculation is finished self.change_mode('shot', enter_kwargs=dict(init_animations=True)) elif self.keymap[action.zoom]: self.zoom_camera_calculate() elif self.keymap[action.move]: self.move_camera_calculate() else: if task.time > ani.rotate_downtime: # Prevents shot follow through from moving camera self.rotate_camera_calculate() else: # Update mouse positions so there is not a big jump self.mouse.touch() if task.time > 0.25: self.shot_sim_overlay.show() return task.cont def run_simulation(self, task): """Run a pool simulation""" self.shots.active.simulate(continuize=False, quiet=False) self.game.process_shot(self.shots.active) self.remove_task('run_simulation') return task.done def zoom_camera_calculate(self): with self.mouse: s = -self.mouse.get_dy() * ani.zoom_sensitivity self.player_cam.node.setPos( pt.autils.multiply_cw(self.player_cam.node.getPos(), 1 - s)) def move_camera_calculate(self): with self.mouse: dxp, dyp = self.mouse.get_dx(), self.mouse.get_dy() h = self.player_cam.focus.getH() * np.pi / 180 + np.pi / 2 dx = dxp * np.cos(h) - dyp * np.sin(h) dy = dxp * np.sin(h) + dyp * np.cos(h) self.player_cam.focus.setX(self.player_cam.focus.getX() + dx * ani.move_sensitivity) self.player_cam.focus.setY(self.player_cam.focus.getY() + dy * ani.move_sensitivity) def rotate_camera_calculate(self): fx, fy = ani.rotate_sensitivity_x, ani.rotate_sensitivity_y with self.mouse: alpha_x = self.player_cam.focus.getH() - fx * self.mouse.get_dx() alpha_y = max( min(0, self.player_cam.focus.getR() + fy * self.mouse.get_dy()), -90) self.player_cam.focus.setH(alpha_x) # Move view laterally self.player_cam.focus.setR(alpha_y) # Move view vertically
class CamLoadMode(Mode): keymap = { action.quit: False, action.cam_load: True, } def enter(self): if self.last_mode == 'aim': self.last_mode = 'view' self.mouse.show() self.mouse.absolute() self.mouse.track() self.selection = None self.task_action('escape', action.quit, True) self.task_action('2', action.cam_load, True) self.task_action('2-up', action.cam_load, False) self.render_camera_load_buttons() self.add_task(self.cam_load_task, 'cam_load_task') def render_camera_load_buttons(self): self.cam_load_slots = GenericMenu( title="Release key with moused hovered over desired save slot", frame_color=(0, 0, 0, 0.2), title_pos=(0, 0, 0.45), ) pos = -1.2 for slot in range(1, 10): exists = True if f'save_{slot}' in self.player_cam.states else False button = self.cam_load_slots.add_button( text=(f'{slot}', f'{slot}', 'load' if exists else 'empty', f'{slot}'), command=lambda: None, scale=0.1, text_scale=0.6, frameSize=(-1.2, 1.2, -1.2, 1.2), frameColor=(0.3, 0.6, 0.6, 1.0) if exists else (0.8, 0.8, 0.8, 1.0), ) button.setPos((pos, 0, 0.25)) button.bind(DGG.WITHIN, self.update_load_selection, extraArgs=[slot]) button.bind(DGG.WITHOUT, self.update_load_selection, extraArgs=[None]) pos += 0.3 self.cam_load_slots.show() def update_load_selection(self, state, coords): self.selection = state def exit(self): if self.selection: self.player_cam.load_state(name=f'save_{self.selection}', ok_if_not_exists=True) self.remove_task('cam_load_task') self.mouse.touch() self.cam_load_slots.hide() def cam_load_task(self, task): if not self.keymap[action.cam_load]: enter_kwargs = dict( load_prev_cam=True) if self.last_mode == 'aim' else {} self.change_mode(self.last_mode, enter_kwargs=enter_kwargs) return task.cont
class Handler(object): def __init__(self): self.modes = { 'menu': { 'enter': self.menu_enter, 'exit': self.menu_exit, 'keymap': { action.exit: False, action.new_game: False, } }, 'aim': { 'enter': self.aim_enter, 'exit': self.aim_exit, 'keymap': { action.fine_control: False, action.quit: False, action.stroke: False, action.view: False, action.zoom: False, action.elevation: False, action.english: False, }, }, 'stroke': { 'enter': self.stroke_enter, 'exit': self.stroke_exit, 'keymap': { action.fine_control: False, action.stroke: True, }, }, 'view': { 'enter': self.view_enter, 'exit': self.view_exit, 'keymap': { action.aim: False, action.fine_control: False, action.move: True, action.quit: False, action.zoom: False, }, }, 'shot': { 'enter': self.shot_enter, 'exit': self.shot_exit, 'keymap': { action.aim: False, action.fine_control: False, action.move: False, action.toggle_pause: False, action.undo_shot: False, action.restart_ani: False, action.quit: False, action.zoom: False, action.rewind: False, action.fast_forward: False, }, }, } # Store the above as default states self.action_state_defaults = {} for mode in self.modes: self.action_state_defaults[mode] = {} for a, default_state in self.modes[mode]['keymap'].items(): self.action_state_defaults[mode][a] = default_state self.mode = None self.keymap = None def update_key_map(self, action_name, action_state): self.keymap[action_name] = action_state def task_action(self, keystroke, action_name, action_state): """Add action to keymap to be handled by tasks""" self.accept(keystroke, self.update_key_map, [action_name, action_state]) def change_mode(self, mode, exit_kwargs={}, enter_kwargs={}): assert mode in self.modes self.end_mode(**exit_kwargs) # Build up operations for the new mode self.mode = mode self.keymap = self.modes[mode]['keymap'] self.modes[mode]['enter'](**enter_kwargs) def end_mode(self, **kwargs): # Stop watching actions related to mode self.ignoreAll() # Tear down operations for the current mode if self.mode is not None: self.modes[self.mode]['exit'](**kwargs) self.reset_action_states() def menu_enter(self): self.mouse.show() self.mouse.absolute() self.show_menu('main') self.task_action('escape', action.exit, True) self.task_action('escape-up', action.exit, False) self.task_action('n', action.new_game, True) self.task_action('n-up', action.new_game, False) self.add_task(self.menu_task, 'menu_task') def menu_exit(self): self.hide_menus() self.remove_task('menu_task') def aim_enter(self): self.mouse.hide() self.mouse.relative() self.mouse.track() self.cue_stick.show_nodes() self.cue_stick.get_node('cue_stick').setX(0) self.cam.update_focus(self.balls['cue'].get_node('ball').getPos()) self.task_action('escape', action.quit, True) self.task_action('f', action.fine_control, True) self.task_action('f-up', action.fine_control, False) self.task_action('mouse1', action.zoom, True) self.task_action('mouse1-up', action.zoom, False) self.task_action('s', action.stroke, True) self.task_action('v', action.view, True) self.task_action('b', action.elevation, True) self.task_action('b-up', action.elevation, False) self.task_action('e', action.english, True) self.task_action('e-up', action.english, False) self.add_task(self.aim_task, 'aim_task') self.add_task(self.quit_task, 'quit_task') def aim_exit(self): self.remove_task('aim_task') self.remove_task('quit_task') self.cue_stick.hide_nodes() self.cam.store_state('aim', overwrite=True) def stroke_enter(self): self.mouse.hide() self.mouse.relative() self.mouse.track() self.cue_stick.track_stroke() self.cue_stick.show_nodes() self.task_action('f', action.fine_control, True) self.task_action('f-up', action.fine_control, False) self.task_action('s', action.stroke, True) self.task_action('s-up', action.stroke, False) self.add_task(self.stroke_task, 'stroke_task') def stroke_exit(self): self.remove_task('stroke_task') self.cam.store_state('stroke', overwrite=True) self.cam.load_state('aim') def view_enter(self): self.mouse.hide() self.mouse.relative() self.mouse.track() self.task_action('escape', action.quit, True) self.task_action('mouse1', action.zoom, True) self.task_action('mouse1-up', action.zoom, False) self.task_action('a', action.aim, True) self.task_action('v', action.move, True) self.task_action('v-up', action.move, False) self.add_task(self.view_task, 'view_task') self.add_task(self.quit_task, 'quit_task') def view_exit(self): self.remove_task('view_task') self.remove_task('quit_task') def shot_enter(self): self.mouse.hide() self.mouse.relative() self.mouse.track() self.shot_sim_overlay = GenericMenu( title='Calculating shot...', frame_color=(0, 0, 0, 0.4), title_pos=(0, 0, -0.2), ) self.shot_sim_overlay.show() self.cue_stick.set_object_state_as_render_state() self.add_task(self.run_simulation, 'run_simulation', taskChain='simulation') self.task_action('escape', action.quit, True) self.task_action('mouse1', action.zoom, True) self.task_action('mouse1-up', action.zoom, False) self.task_action('a', action.aim, True) self.task_action('v', action.move, True) self.task_action('v-up', action.move, False) self.task_action('r', action.restart_ani, True) self.task_action('r-up', action.restart_ani, False) self.task_action('z', action.undo_shot, True) self.task_action('z-up', action.undo_shot, False) self.task_action('f', action.fine_control, True) self.task_action('f-up', action.fine_control, False) self.task_action('arrow_left', action.rewind, True) self.task_action('arrow_left-up', action.rewind, False) self.task_action('arrow_right', action.fast_forward, True) self.task_action('arrow_right-up', action.fast_forward, False) self.add_task(self.quit_task, 'quit_task') def shot_exit(self, keep=True): """Exit shot mode Parameters ========== keep : bool, True If True, the system state will be set to the end state of the shot. Otherwise, the system state will be returned to the start state of the shot. """ self.shot.finish_animation() self.shot.ball_animations.finish() if keep: self.shot.cue.reset_state() self.shot.cue.set_render_state_as_object_state() for ball in self.shot.balls.values(): ball.reset_angular_integration() else: self.cam.load_state('stroke') for ball in self.shot.balls.values(): if ball.history.is_populated(): ball.set( rvw=ball.history.rvw[0], s=ball.history.s[0], t=0, ) ball.set_render_state_as_object_state() ball.history.reset_history() self.shot.cue.update_focus() self.remove_task('shot_view_task') self.remove_task('shot_animation_task') self.remove_task('quit_task') self.shot = None def reset_action_states(self): for key in self.keymap: self.keymap[key] = self.action_state_defaults[self.mode][key]