# coding: utf-8 import io import os import random import typing import pyglet import time import tmx from PIL import Image from pyglet.window import key from cocos.actions import MoveTo as BaseMoveTo from cocos.audio.pygame.mixer import Sound from opencombat.simulation.interior import InteriorManager from opencombat.simulation.tmx import TileMap from opencombat.user_action import UserAction from synergine2.config import Config from synergine2.log import SynergineLogger from synergine2.terminals import Terminal from synergine2_cocos2d.actions import MoveTo from synergine2_cocos2d.animation import ANIMATION_CRAWL from synergine2_cocos2d.animation import ANIMATION_WALK from synergine2_cocos2d.animation import Animate from synergine2_cocos2d.gl import draw_line from synergine2_cocos2d.gui import EditLayer as BaseEditLayer from synergine2_cocos2d.gui import TMXGui from synergine2_cocos2d.layer import LayerManager from synergine2_xyz.move.simulation import FinishMoveEvent from synergine2_xyz.move.simulation import StartMoveEvent from synergine2_xyz.physics import Physics from synergine2_xyz.utils import get_angle from opencombat.simulation.event import NewVisibleOpponent from opencombat.simulation.event import NoLongerVisibleOpponent from opencombat.simulation.event import FireEvent from opencombat.simulation.event import DieEvent class EditLayer(BaseEditLayer): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) # TODO BS 20171213: Into other layer ! self.last_interior_draw = 0 # FIXME BS: hardcoded (move into other layer) self.interior_manager = InteriorManager(TileMap('opencombat/maps/003/003.tmx')) def _on_key_press(self, k, m): if self.selection: if k == key.M: self.user_action_pending = UserAction.ORDER_MOVE if k == key.R: self.user_action_pending = UserAction.ORDER_MOVE_FAST if k == key.C: self.user_action_pending = UserAction.ORDER_MOVE_CRAWL if k == key.F: self.user_action_pending = UserAction.ORDER_FIRE def draw(self) -> None: super().draw() self.draw_interiors() def draw_interiors(self): # TODO BS 20171213: Into other layer ! now = time.time() # FIXME: config if now - self.last_interior_draw > 2: self.last_interior_draw = now subject_grid_positions = [ a.subject.position for a in self.layer_manager.subject_layer.subjects_index.values() ] interiors = self.interior_manager.get_interiors(where_positions=subject_grid_positions) if interiors: # FIXME: hardcoded image = Image.open('opencombat/maps/003/background.png') image_fake_file = io.BytesIO() # FIXME: tile height/width ! self.interior_manager.update_image_for_interiors(image, interiors, 8, 8) image.save(image_fake_file, format='PNG') self.layer_manager.background_sprite.image = pyglet.image.load( 'new_background.png', file=image_fake_file, ) class TileLayerManager(LayerManager): edit_layer_class = EditLayer # TODO: Move into synergine2cocos2d class AudioLibrary(object): sound_file_paths = { 'gunshot_default': '204010__duckduckpony__homemade-gunshot-2.ogg', } def __init__(self, sound_dir_path: str) -> None: self._sound_dir_path = sound_dir_path self._sounds = {} def get_sound(self, name: str) -> Sound: if name not in self._sounds: sound_file_name = self.sound_file_paths[name] self._sounds[name] = Sound(os.path.join(self._sound_dir_path, sound_file_name)) return self._sounds[name] class Game(TMXGui): layer_manager_class = TileLayerManager def __init__( self, config: Config, logger: SynergineLogger, terminal: Terminal, physics: Physics, read_queue_interval: float = 1 / 60.0, map_dir_path: str=None, ): super().__init__( config, logger, terminal, physics=physics, read_queue_interval=read_queue_interval, map_dir_path=map_dir_path, ) self.sound_lib = AudioLibrary('opencombat/sounds/') self.terminal.register_event_handler( FinishMoveEvent, self.set_subject_position, ) self.terminal.register_event_handler( StartMoveEvent, self.start_move_subject, ) self.terminal.register_event_handler( NewVisibleOpponent, self.new_visible_opponent, ) self.terminal.register_event_handler( NoLongerVisibleOpponent, self.no_longer_visible_opponent, ) self.terminal.register_event_handler( FireEvent, self.fire_happen, ) self.terminal.register_event_handler( DieEvent, self.subject_die, ) # configs self.move_duration_ref = float(self.config.resolve('game.move.walk_ref_time')) self.move_fast_duration_ref = float(self.config.resolve('game.move.run_ref_time')) self.move_crawl_duration_ref = float(self.config.resolve('game.move.crawl_ref_time')) def before_run(self) -> None: from opencombat.gui.move import MoveActorInteraction from opencombat.gui.move import MoveFastActorInteraction from opencombat.gui.move import MoveCrawlActorInteraction from opencombat.gui.fire import FireActorInteraction self.layer_manager.interaction_manager.register(MoveActorInteraction, self.layer_manager) self.layer_manager.interaction_manager.register(MoveFastActorInteraction, self.layer_manager) self.layer_manager.interaction_manager.register(MoveCrawlActorInteraction, self.layer_manager) self.layer_manager.interaction_manager.register(FireActorInteraction, self.layer_manager) def set_subject_position(self, event: FinishMoveEvent): actor = self.layer_manager.subject_layer.subjects_index[event.subject_id] new_world_position = self.layer_manager.grid_manager.get_world_position_of_grid_position(event.to_position) actor.stop_actions((BaseMoveTo,)) actor.set_position(*new_world_position) def start_move_subject(self, event: StartMoveEvent): actor = self.layer_manager.subject_layer.subjects_index[event.subject_id] new_world_position = self.layer_manager.grid_manager.get_world_position_of_grid_position(event.to_position) if event.gui_action == UserAction.ORDER_MOVE: animation = ANIMATION_WALK cycle_duration = 2 move_duration = self.move_duration_ref elif event.gui_action == UserAction.ORDER_MOVE_FAST: animation = ANIMATION_WALK cycle_duration = 0.5 move_duration = self.move_fast_duration_ref elif event.gui_action == UserAction.ORDER_MOVE_CRAWL: animation = ANIMATION_CRAWL cycle_duration = 2 move_duration = self.move_crawl_duration_ref else: raise NotImplementedError() move_action = MoveTo(new_world_position, move_duration) actor.do(move_action) actor.do(Animate(animation, duration=move_duration, cycle_duration=cycle_duration)) actor.rotation = get_angle(event.from_position, event.to_position) def new_visible_opponent(self, event: NewVisibleOpponent): self.visible_or_no_longer_visible_opponent(event, (153, 0, 153)) def no_longer_visible_opponent(self, event: NoLongerVisibleOpponent): self.visible_or_no_longer_visible_opponent(event, (255, 102, 0)) def visible_or_no_longer_visible_opponent( self, event: typing.Union[NoLongerVisibleOpponent, NewVisibleOpponent], line_color, ) -> None: if self.layer_manager.debug: observer_actor = self.layer_manager.subject_layer.subjects_index[event.observer_subject_id] observed_actor = self.layer_manager.subject_layer.subjects_index[event.observed_subject_id] observer_pixel_position = self.layer_manager.scrolling_manager.world_to_screen( *self.layer_manager.grid_manager.get_world_position_of_grid_position( observer_actor.subject.position, ) ) observed_pixel_position = self.layer_manager.scrolling_manager.world_to_screen( *self.layer_manager.grid_manager.get_world_position_of_grid_position( observed_actor.subject.position, ) ) def draw_visible_opponent(): draw_line( observer_pixel_position, observed_pixel_position, line_color, ) # TODO: Not in edit layer ! self.layer_manager.edit_layer.append_callback(draw_visible_opponent, 1.0) def fire_happen(self, event: FireEvent) -> None: # TODO: Not in edit layer ! shooter_actor = self.layer_manager.subject_layer.subjects_index[event.shooter_subject_id] shooter_pixel_position = self.layer_manager.scrolling_manager.world_to_screen( *self.layer_manager.grid_manager.get_world_position_of_grid_position( shooter_actor.subject.position, ) ) fire_to_pixel_position = self.layer_manager.scrolling_manager.world_to_screen( *self.layer_manager.grid_manager.get_world_position_of_grid_position( event.target_position, ) ) def gunshot_trace(): draw_line( shooter_pixel_position, fire_to_pixel_position, color=(255, 0, 0), ) def gunshot_sound(): self.sound_lib.get_sound('gunshot_default').play() # To avoid all in same time delay = random.uniform(0.0, 0.6) self.layer_manager.edit_layer.append_callback(gunshot_trace, duration=0.1, delay=delay) self.layer_manager.edit_layer.append_callback(gunshot_sound, duration=0.0, delay=delay) def subject_die(self, event: DieEvent) -> None: killed_actor = self.layer_manager.subject_layer.subjects_index[event.shoot_subject_id] dead_image = pyglet.resource.image('opencombat/maps/003/actors/man_d1.png') killed_actor.update_image(dead_image) killed_actor.freeze()