move.py 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. # coding: utf-8
  2. import typing
  3. from random import randint
  4. import time
  5. from synergine2.config import Config
  6. from synergine2.simulation import SimulationBehaviour, Subject
  7. from synergine2.simulation import SubjectBehaviour
  8. from synergine2.simulation import SubjectMechanism
  9. from synergine2.simulation import Intention
  10. from synergine2.simulation import Simulation
  11. from synergine2.simulation import Event
  12. from synergine2_cocos2d.user_action import UserAction
  13. from synergine2_xyz.simulation import XYZSimulation
  14. class MoveToIntention(Intention):
  15. def __init__(
  16. self,
  17. move_to: typing.Tuple[int, int],
  18. start_time: float,
  19. gui_action: typing.Any,
  20. ) -> None:
  21. self.move_to = move_to
  22. self.path = [] # type: typing.List[typing.Tuple[int, int]]
  23. self.path_progression = -1 # type: int
  24. self.last_intention_time = start_time
  25. self.just_reach = False
  26. self.initial = True
  27. self.gui_action = gui_action
  28. class RequestMoveBehaviour(SimulationBehaviour):
  29. move_intention_class = MoveToIntention
  30. @classmethod
  31. def merge_data(cls, new_data, start_data=None):
  32. # TODO: behaviour/Thing dedicated to Gui -> Simulation ?
  33. pass # This behaviour is designed to be launch by terminal
  34. def __init__(
  35. self,
  36. config: Config,
  37. simulation: Simulation,
  38. ):
  39. super().__init__(config, simulation)
  40. self.simulation = typing.cast(XYZSimulation, self.simulation)
  41. def run(self, data):
  42. # TODO: behaviour/Thing dedicated to Gui -> Simulation ?
  43. pass # This behaviour is designed to be launch by terminal
  44. def action(self, data) -> typing.List[Event]:
  45. subject_id = data['subject_id']
  46. move_to = data['move_to']
  47. try:
  48. subject = self.simulation.subjects.index[subject_id]
  49. subject.intentions.set(self.move_intention_class(
  50. move_to,
  51. start_time=time.time(),
  52. gui_action=data['gui_action'],
  53. ))
  54. except KeyError:
  55. # TODO: log error here
  56. pass
  57. return []
  58. class MoveToMechanism(SubjectMechanism):
  59. def run(self):
  60. # TODO: Si move to: Si nouveau: a*, si bloque, a*, sinon rien
  61. # TODO: pourquoi un mechanism plutot que dans run du behaviour ? faire en sorte que lorsque on calcule,
  62. # si un subject est déjà passé par là et qu'il va au même endroit, ne pas recalculer.
  63. try:
  64. # TODO: MoveToIntention doit être configurable
  65. move = self.subject.intentions.get(MoveToIntention)
  66. move = typing.cast(MoveToIntention, move)
  67. new_path = None
  68. if not move.path:
  69. move.path = self.simulation.physics.found_path(
  70. start=self.subject.position,
  71. end=move.move_to,
  72. subject=self.subject,
  73. )
  74. # Note: We are in process, move change will be lost
  75. new_path = move.path
  76. # move.path = []
  77. # new_path = move.path
  78. # for i in range(20):
  79. # move.path.append((
  80. # self.subject.position[0],
  81. # self.subject.position[1] + i,
  82. # ))
  83. next_move = move.path[move.path_progression + 1]
  84. # TODO: fin de path
  85. if not self.simulation.is_possible_position(next_move):
  86. # TODO: refaire le path
  87. new_path = ['...']
  88. return {
  89. 'new_path': new_path,
  90. 'last_intention_time': move.last_intention_time,
  91. 'just_reach': move.just_reach,
  92. 'initial': move.initial,
  93. 'gui_action': move.gui_action,
  94. }
  95. except IndexError: # TODO: Specialize ? No movement left
  96. return None
  97. except KeyError: # TODO: Specialize ? No MoveIntention
  98. return None
  99. class FinishMoveEvent(Event):
  100. def __init__(
  101. self,
  102. subject_id: int,
  103. from_position: typing.Tuple[int, int],
  104. to_position: typing.Tuple[int, int],
  105. gui_action: typing.Any,
  106. *args,
  107. **kwargs
  108. ):
  109. super().__init__(*args, **kwargs)
  110. self.subject_id = subject_id
  111. self.from_position = from_position
  112. self.to_position = to_position
  113. self.gui_action = gui_action
  114. def repr_debug(self) -> str:
  115. return '{}: subject_id:{}, from_position:{} to_position: {}'.format(
  116. type(self).__name__,
  117. self.subject_id,
  118. self.from_position,
  119. self.to_position,
  120. )
  121. class StartMoveEvent(Event):
  122. def __init__(
  123. self,
  124. subject_id: int,
  125. from_position: typing.Tuple[int, int],
  126. to_position: typing.Tuple[int, int],
  127. gui_action: typing.Any,
  128. *args,
  129. **kwargs
  130. ):
  131. super().__init__(*args, **kwargs)
  132. self.subject_id = subject_id
  133. self.from_position = from_position
  134. self.to_position = to_position
  135. self.gui_action = gui_action
  136. def repr_debug(self) -> str:
  137. return '{}: subject_id:{}, from_position:{} to_position: {}'.format(
  138. type(self).__name__,
  139. self.subject_id,
  140. self.from_position,
  141. self.to_position,
  142. )
  143. class MoveToBehaviour(SubjectBehaviour):
  144. use = [MoveToMechanism]
  145. move_to_mechanism = MoveToMechanism
  146. def __init__(
  147. self,
  148. config: Config,
  149. simulation: Simulation,
  150. subject: Subject,
  151. ) -> None:
  152. super().__init__(config, simulation, subject)
  153. self._walk_duration = float(self.config.resolve('game.move.walk_ref_time'))
  154. self._run_duration = float(self.config.resolve('game.move.run_ref_time'))
  155. def run(self, data):
  156. move_to_data = data[self.move_to_mechanism]
  157. if move_to_data:
  158. if self._can_move_to_next_step(move_to_data):
  159. move_to_data['reach_next'] = True
  160. return move_to_data
  161. if self._is_fresh_new_step(move_to_data):
  162. move_to_data['reach_next'] = False
  163. return move_to_data
  164. return False
  165. def _can_move_to_next_step(self, move_to_data: dict) -> bool:
  166. # TODO: Relation vers cocos ! Deplacer UserAction ?
  167. if move_to_data['gui_action'] == UserAction.ORDER_MOVE:
  168. return time.time() - move_to_data['last_intention_time'] >= self._walk_duration
  169. if move_to_data['gui_action'] == UserAction.ORDER_MOVE_FAST:
  170. return time.time() - move_to_data['last_intention_time'] >= self._run_duration
  171. def _is_fresh_new_step(self, move_to_data: dict) -> bool:
  172. return move_to_data['just_reach'] or move_to_data['initial']
  173. def action(self, data) -> [Event]:
  174. new_path = data['new_path']
  175. # TODO: MoveToIntention doit être configurable
  176. move = self.subject.intentions.get(MoveToIntention)
  177. move = typing.cast(MoveToIntention, move)
  178. if new_path:
  179. move.path = new_path
  180. move.path_progression = -1
  181. previous_position = self.subject.position
  182. new_position = move.path[move.path_progression + 1]
  183. if data['reach_next']:
  184. # TODO: fin de path
  185. move.path_progression += 1
  186. self.subject.position = new_position
  187. move.last_intention_time = time.time()
  188. move.just_reach = True
  189. event = FinishMoveEvent(
  190. self.subject.id,
  191. previous_position,
  192. new_position,
  193. gui_action=move.gui_action,
  194. )
  195. else:
  196. move.just_reach = False
  197. event = StartMoveEvent(
  198. self.subject.id,
  199. previous_position,
  200. new_position,
  201. gui_action=move.gui_action,
  202. )
  203. move.initial = False
  204. # Note: Need to explicitly set to update shared data
  205. self.subject.intentions.set(move)
  206. return [event]