actor.py 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. # coding: utf-8
  2. import typing
  3. import time
  4. import pyglet
  5. from PIL import Image
  6. from synergine2.config import Config
  7. from synergine2.simulation import Subject
  8. from synergine2_cocos2d.actor import Actor
  9. from synergine2_xyz.exception import UnknownAnimationIndex
  10. from opencombat.exception import UnknownWeapon
  11. from opencombat.exception import WrongMode
  12. from opencombat.exception import UnknownFiringAnimation
  13. from opencombat.gui.animation import ANIMATION_CRAWL
  14. from opencombat.gui.animation import ANIMATION_WALK
  15. from opencombat.gui.const import MODE_MAN_STAND_UP
  16. from opencombat.gui.const import MODE_MAN_CRAWLING
  17. from opencombat.gui.image import TileImageCacheManager
  18. from opencombat.gui.weapon import RIFFLE
  19. from opencombat.gui.weapon import WeaponImageApplier
  20. from opencombat.user_action import UserAction
  21. if typing.TYPE_CHECKING:
  22. from opencombat.gui.fire import GuiFiringEvent
  23. MODE_DEFAULT = 'MODE_DEFAULT'
  24. class BaseActor(Actor):
  25. position_matching = {
  26. ANIMATION_WALK: MODE_MAN_STAND_UP,
  27. ANIMATION_CRAWL: MODE_MAN_CRAWLING,
  28. }
  29. mode_image_paths = {
  30. MODE_DEFAULT: 'unknown.png',
  31. }
  32. modes = [
  33. MODE_DEFAULT,
  34. ]
  35. weapons_firing_image_scheme = {}
  36. weapon_image_scheme = {}
  37. move_for_gui_actions = {}
  38. def __init__(
  39. self,
  40. image_path: str,
  41. config: Config,
  42. subject: Subject,
  43. ) -> None:
  44. self._mode = MODE_MAN_STAND_UP
  45. self.weapon_image_applier = WeaponImageApplier(config, self)
  46. super().__init__(image_path, subject=subject, config=config)
  47. # Firing
  48. self.last_firing_time = 0
  49. self.firing_change_image_gap = 0.05 # seconds
  50. def get_image_cache_manager(self) -> TileImageCacheManager:
  51. return TileImageCacheManager(self, self.config)
  52. def get_default_mode(self) -> str:
  53. return MODE_DEFAULT
  54. def get_mode_image_path(self, mode: str) -> str:
  55. return self.mode_image_paths[mode]
  56. def get_modes(self) -> typing.List[str]:
  57. return self.modes
  58. @property
  59. def mode(self) -> str:
  60. return self._mode
  61. @mode.setter
  62. def mode(self, value) -> None:
  63. if value not in self.get_modes():
  64. raise WrongMode('Actor "{}" has no mode "{}" ({})'.format(
  65. self.__class__.__name__,
  66. value,
  67. ', '.join(self.get_modes()),
  68. ))
  69. self._mode = value
  70. def get_mode_for_gui_action(self, gui_action: str) -> str:
  71. try:
  72. return self.move_for_gui_actions[gui_action]
  73. except KeyError:
  74. return self.get_default_mode()
  75. @property
  76. def weapons(self) -> typing.List[str]:
  77. return []
  78. def get_default_appliable_images(self) -> typing.List[Image.Image]:
  79. if not self.weapons:
  80. return []
  81. return [
  82. self.weapon_image_applier.get_image_for_weapon(
  83. self.mode,
  84. self.weapons[0], # FIXME
  85. )
  86. ]
  87. def get_animation_appliable_images(
  88. self,
  89. animation_name: str,
  90. animation_position: int,
  91. ) -> typing.List[Image.Image]:
  92. if not self.weapons:
  93. return []
  94. position = self.position_matching[animation_name]
  95. try:
  96. return [
  97. self.weapon_image_applier.get_animation_image_for_weapon(
  98. position,
  99. self.weapons[0],
  100. animation_position,
  101. )
  102. ]
  103. except UnknownWeapon:
  104. return []
  105. def firing(self, firing: 'GuiFiringEvent') -> None:
  106. # FIXME: move some code ?
  107. now = time.time()
  108. if now - self.last_firing_time >= self.firing_change_image_gap:
  109. self.last_firing_time = now
  110. firing.increment_animation_index()
  111. try:
  112. image = self.image_cache_manager.firing_cache.get(
  113. mode=self.mode,
  114. weapon=firing.weapon,
  115. position=firing.animation_index,
  116. )
  117. except UnknownAnimationIndex:
  118. image = self.image_cache_manager.firing_cache.get(
  119. mode=self.mode,
  120. weapon=firing.weapon,
  121. position=0,
  122. )
  123. firing.reset_animation_index()
  124. except UnknownFiringAnimation as exc:
  125. self.logger.error(
  126. 'No firing animation for actor {}({}): {}'.format(
  127. self.__class__.__name__,
  128. str(self.subject.id),
  129. str(exc),
  130. )
  131. )
  132. return # There is no firing animation defined
  133. # FIXME cache: prepare before firing
  134. import uuid
  135. tmp_path = '/tmp/{}.png'.format(str(uuid.uuid4()))
  136. image.save(tmp_path)
  137. pyglet_image = pyglet.image.load(tmp_path)
  138. self.update_image(pyglet_image.get_texture())
  139. class Man(BaseActor):
  140. animation_image_paths = {
  141. ANIMATION_WALK: [
  142. 'actors/man.png',
  143. 'actors/man_w1.png',
  144. 'actors/man_w2.png',
  145. 'actors/man_w3.png',
  146. 'actors/man_w4.png',
  147. 'actors/man_w5.png',
  148. 'actors/man_w6.png',
  149. 'actors/man_w7.png',
  150. ],
  151. ANIMATION_CRAWL: [
  152. 'actors/man_c1.png',
  153. 'actors/man_c2.png',
  154. 'actors/man_c3.png',
  155. 'actors/man_c4.png',
  156. ]
  157. }
  158. modes = [
  159. MODE_MAN_STAND_UP,
  160. MODE_MAN_CRAWLING,
  161. ]
  162. mode_image_paths = {
  163. MODE_MAN_STAND_UP: 'actors/man.png',
  164. MODE_MAN_CRAWLING: 'actors/man_c1.png',
  165. }
  166. weapon_image_scheme = {
  167. MODE_MAN_STAND_UP: {
  168. RIFFLE: [
  169. 'actors/man_weap1.png'
  170. ],
  171. },
  172. MODE_MAN_CRAWLING: {
  173. RIFFLE: [
  174. 'actors/man_c1_weap1.png',
  175. 'actors/man_c2_weap1.png',
  176. 'actors/man_c3_weap1.png',
  177. 'actors/man_c4_weap1.png',
  178. ],
  179. }
  180. }
  181. weapons_firing_image_scheme = {
  182. MODE_MAN_STAND_UP: {
  183. RIFFLE: [
  184. 'actors/man_weap1_firing1.png',
  185. 'actors/man_weap1_firing2.png',
  186. 'actors/man_weap1_firing3.png',
  187. ],
  188. },
  189. MODE_MAN_CRAWLING: {
  190. RIFFLE: [
  191. 'actors/man_weap1_firing1.png',
  192. 'actors/man_weap1_firing2.png',
  193. 'actors/man_weap1_firing3.png',
  194. ]
  195. }
  196. }
  197. move_for_gui_actions = {
  198. UserAction.ORDER_MOVE: MODE_MAN_STAND_UP,
  199. UserAction.ORDER_MOVE_FAST: MODE_MAN_STAND_UP,
  200. UserAction.ORDER_MOVE_CRAWL: MODE_MAN_CRAWLING,
  201. }
  202. def __init__(
  203. self,
  204. config: Config,
  205. subject: Subject,
  206. ) -> None:
  207. super().__init__('actors/man.png', subject=subject, config=config)
  208. @property
  209. def weapons(self) -> typing.List[str]:
  210. # TODO BS 2018-01-26: Will be managed by complex part of code
  211. return [RIFFLE]
  212. def get_default_mode(self) -> str:
  213. return MODE_MAN_STAND_UP
  214. class HeavyVehicle(BaseActor):
  215. animation_image_paths = {
  216. ANIMATION_WALK: [
  217. 'actors/tank1.png',
  218. ],
  219. ANIMATION_CRAWL: [
  220. 'actors/tank1.png',
  221. ]
  222. }
  223. mode_image_paths = {
  224. MODE_DEFAULT: 'actors/tank1.png',
  225. }
  226. def __init__(
  227. self,
  228. config: Config,
  229. subject: Subject,
  230. ) -> None:
  231. super().__init__('actors/tank1.png', subject=subject, config=config)
  232. @property
  233. def weapons(self) -> typing.List[str]:
  234. # TODO BS 2018-01-26: Will be managed by complex part of code
  235. return [RIFFLE]