base.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. # coding: utf-8
  2. import io
  3. import os
  4. import random
  5. import typing
  6. import pyglet
  7. import time
  8. import tmx
  9. from PIL import Image
  10. from pyglet.window import key
  11. from cocos.actions import MoveTo as BaseMoveTo
  12. from cocos.audio.pygame.mixer import Sound
  13. from opencombat.simulation.interior import InteriorManager
  14. from opencombat.simulation.tmx import TileMap
  15. from opencombat.user_action import UserAction
  16. from synergine2.config import Config
  17. from synergine2.log import SynergineLogger
  18. from synergine2.terminals import Terminal
  19. from synergine2_cocos2d.actions import MoveTo
  20. from synergine2_cocos2d.animation import ANIMATION_CRAWL
  21. from synergine2_cocos2d.animation import ANIMATION_WALK
  22. from synergine2_cocos2d.animation import Animate
  23. from synergine2_cocos2d.gl import draw_line
  24. from synergine2_cocos2d.gui import EditLayer as BaseEditLayer
  25. from synergine2_cocos2d.gui import TMXGui
  26. from synergine2_cocos2d.layer import LayerManager
  27. from synergine2_xyz.move.simulation import FinishMoveEvent
  28. from synergine2_xyz.move.simulation import StartMoveEvent
  29. from synergine2_xyz.physics import Physics
  30. from synergine2_xyz.utils import get_angle
  31. from opencombat.simulation.event import NewVisibleOpponent
  32. from opencombat.simulation.event import NoLongerVisibleOpponent
  33. from opencombat.simulation.event import FireEvent
  34. from opencombat.simulation.event import DieEvent
  35. class EditLayer(BaseEditLayer):
  36. def __init__(self, *args, **kwargs) -> None:
  37. super().__init__(*args, **kwargs)
  38. # TODO BS 20171213: Into other layer !
  39. self.last_interior_draw = 0
  40. # FIXME BS: hardcoded (move into other layer)
  41. self.interior_manager = InteriorManager(TileMap('opencombat/maps/003/003.tmx'))
  42. def _on_key_press(self, k, m):
  43. if self.selection:
  44. if k == key.M:
  45. self.user_action_pending = UserAction.ORDER_MOVE
  46. if k == key.R:
  47. self.user_action_pending = UserAction.ORDER_MOVE_FAST
  48. if k == key.C:
  49. self.user_action_pending = UserAction.ORDER_MOVE_CRAWL
  50. if k == key.F:
  51. self.user_action_pending = UserAction.ORDER_FIRE
  52. def draw(self) -> None:
  53. super().draw()
  54. self.draw_interiors()
  55. def draw_interiors(self):
  56. # TODO BS 20171213: Into other layer !
  57. now = time.time()
  58. # FIXME: config
  59. if now - self.last_interior_draw > 2:
  60. self.last_interior_draw = now
  61. subject_grid_positions = [
  62. a.subject.position for a
  63. in self.layer_manager.subject_layer.subjects_index.values()
  64. ]
  65. interiors = self.interior_manager.get_interiors(where_positions=subject_grid_positions)
  66. if interiors:
  67. # FIXME: hardcoded
  68. image = Image.open('opencombat/maps/003/background.png')
  69. image_fake_file = io.BytesIO()
  70. # FIXME: tile height/width !
  71. self.interior_manager.update_image_for_interiors(image, interiors, 8, 8)
  72. image.save(image_fake_file, format='PNG')
  73. self.layer_manager.background_sprite.image = pyglet.image.load(
  74. 'new_background.png',
  75. file=image_fake_file,
  76. )
  77. class TileLayerManager(LayerManager):
  78. edit_layer_class = EditLayer
  79. # TODO: Move into synergine2cocos2d
  80. class AudioLibrary(object):
  81. sound_file_paths = {
  82. 'gunshot_default': '204010__duckduckpony__homemade-gunshot-2.ogg',
  83. }
  84. def __init__(self, sound_dir_path: str) -> None:
  85. self._sound_dir_path = sound_dir_path
  86. self._sounds = {}
  87. def get_sound(self, name: str) -> Sound:
  88. if name not in self._sounds:
  89. sound_file_name = self.sound_file_paths[name]
  90. self._sounds[name] = Sound(os.path.join(self._sound_dir_path, sound_file_name))
  91. return self._sounds[name]
  92. class Game(TMXGui):
  93. layer_manager_class = TileLayerManager
  94. def __init__(
  95. self,
  96. config: Config,
  97. logger: SynergineLogger,
  98. terminal: Terminal,
  99. physics: Physics,
  100. read_queue_interval: float = 1 / 60.0,
  101. map_dir_path: str=None,
  102. ):
  103. super().__init__(
  104. config,
  105. logger,
  106. terminal,
  107. physics=physics,
  108. read_queue_interval=read_queue_interval,
  109. map_dir_path=map_dir_path,
  110. )
  111. self.sound_lib = AudioLibrary('opencombat/sounds/')
  112. self.terminal.register_event_handler(
  113. FinishMoveEvent,
  114. self.set_subject_position,
  115. )
  116. self.terminal.register_event_handler(
  117. StartMoveEvent,
  118. self.start_move_subject,
  119. )
  120. self.terminal.register_event_handler(
  121. NewVisibleOpponent,
  122. self.new_visible_opponent,
  123. )
  124. self.terminal.register_event_handler(
  125. NoLongerVisibleOpponent,
  126. self.no_longer_visible_opponent,
  127. )
  128. self.terminal.register_event_handler(
  129. FireEvent,
  130. self.fire_happen,
  131. )
  132. self.terminal.register_event_handler(
  133. DieEvent,
  134. self.subject_die,
  135. )
  136. # configs
  137. self.move_duration_ref = float(self.config.resolve('game.move.walk_ref_time'))
  138. self.move_fast_duration_ref = float(self.config.resolve('game.move.run_ref_time'))
  139. self.move_crawl_duration_ref = float(self.config.resolve('game.move.crawl_ref_time'))
  140. def before_run(self) -> None:
  141. from opencombat.gui.move import MoveActorInteraction
  142. from opencombat.gui.move import MoveFastActorInteraction
  143. from opencombat.gui.move import MoveCrawlActorInteraction
  144. from opencombat.gui.fire import FireActorInteraction
  145. self.layer_manager.interaction_manager.register(MoveActorInteraction, self.layer_manager)
  146. self.layer_manager.interaction_manager.register(MoveFastActorInteraction, self.layer_manager)
  147. self.layer_manager.interaction_manager.register(MoveCrawlActorInteraction, self.layer_manager)
  148. self.layer_manager.interaction_manager.register(FireActorInteraction, self.layer_manager)
  149. def set_subject_position(self, event: FinishMoveEvent):
  150. actor = self.layer_manager.subject_layer.subjects_index[event.subject_id]
  151. new_world_position = self.layer_manager.grid_manager.get_world_position_of_grid_position(event.to_position)
  152. actor.stop_actions((BaseMoveTo,))
  153. actor.set_position(*new_world_position)
  154. def start_move_subject(self, event: StartMoveEvent):
  155. actor = self.layer_manager.subject_layer.subjects_index[event.subject_id]
  156. new_world_position = self.layer_manager.grid_manager.get_world_position_of_grid_position(event.to_position)
  157. if event.gui_action == UserAction.ORDER_MOVE:
  158. animation = ANIMATION_WALK
  159. cycle_duration = 2
  160. move_duration = self.move_duration_ref
  161. elif event.gui_action == UserAction.ORDER_MOVE_FAST:
  162. animation = ANIMATION_WALK
  163. cycle_duration = 0.5
  164. move_duration = self.move_fast_duration_ref
  165. elif event.gui_action == UserAction.ORDER_MOVE_CRAWL:
  166. animation = ANIMATION_CRAWL
  167. cycle_duration = 2
  168. move_duration = self.move_crawl_duration_ref
  169. else:
  170. raise NotImplementedError()
  171. move_action = MoveTo(new_world_position, move_duration)
  172. actor.do(move_action)
  173. actor.do(Animate(animation, duration=move_duration, cycle_duration=cycle_duration))
  174. actor.rotation = get_angle(event.from_position, event.to_position)
  175. def new_visible_opponent(self, event: NewVisibleOpponent):
  176. self.visible_or_no_longer_visible_opponent(event, (153, 0, 153))
  177. def no_longer_visible_opponent(self, event: NoLongerVisibleOpponent):
  178. self.visible_or_no_longer_visible_opponent(event, (255, 102, 0))
  179. def visible_or_no_longer_visible_opponent(
  180. self,
  181. event: typing.Union[NoLongerVisibleOpponent, NewVisibleOpponent],
  182. line_color,
  183. ) -> None:
  184. if self.layer_manager.debug:
  185. observer_actor = self.layer_manager.subject_layer.subjects_index[event.observer_subject_id]
  186. observed_actor = self.layer_manager.subject_layer.subjects_index[event.observed_subject_id]
  187. observer_pixel_position = self.layer_manager.scrolling_manager.world_to_screen(
  188. *self.layer_manager.grid_manager.get_world_position_of_grid_position(
  189. observer_actor.subject.position,
  190. )
  191. )
  192. observed_pixel_position = self.layer_manager.scrolling_manager.world_to_screen(
  193. *self.layer_manager.grid_manager.get_world_position_of_grid_position(
  194. observed_actor.subject.position,
  195. )
  196. )
  197. def draw_visible_opponent():
  198. draw_line(
  199. observer_pixel_position,
  200. observed_pixel_position,
  201. line_color,
  202. )
  203. # TODO: Not in edit layer !
  204. self.layer_manager.edit_layer.append_callback(draw_visible_opponent, 1.0)
  205. def fire_happen(self, event: FireEvent) -> None:
  206. # TODO: Not in edit layer !
  207. shooter_actor = self.layer_manager.subject_layer.subjects_index[event.shooter_subject_id]
  208. shooter_pixel_position = self.layer_manager.scrolling_manager.world_to_screen(
  209. *self.layer_manager.grid_manager.get_world_position_of_grid_position(
  210. shooter_actor.subject.position,
  211. )
  212. )
  213. fire_to_pixel_position = self.layer_manager.scrolling_manager.world_to_screen(
  214. *self.layer_manager.grid_manager.get_world_position_of_grid_position(
  215. event.target_position,
  216. )
  217. )
  218. def gunshot_trace():
  219. draw_line(
  220. shooter_pixel_position,
  221. fire_to_pixel_position,
  222. color=(255, 0, 0),
  223. )
  224. def gunshot_sound():
  225. self.sound_lib.get_sound('gunshot_default').play()
  226. # To avoid all in same time
  227. delay = random.uniform(0.0, 0.6)
  228. self.layer_manager.edit_layer.append_callback(gunshot_trace, duration=0.1, delay=delay)
  229. self.layer_manager.edit_layer.append_callback(gunshot_sound, duration=0.0, delay=delay)
  230. def subject_die(self, event: DieEvent) -> None:
  231. killed_actor = self.layer_manager.subject_layer.subjects_index[event.shoot_subject_id]
  232. dead_image = pyglet.resource.image('opencombat/maps/003/actors/man_d1.png')
  233. killed_actor.update_image(dead_image)
  234. killed_actor.freeze()