Browse Source

Fire and kill

Bastien Sevajol 6 years ago
parent
commit
45cf4f9c11

+ 5 - 1
sandbox/tile/config.yaml View File

1
 core:
1
 core:
2
     cycle_duration: 0.25
2
     cycle_duration: 0.25
3
-    use_x_cores: 1
3
+    use_x_cores: 2
4
 terminals:
4
 terminals:
5
     sync: True
5
     sync: True
6
 game:
6
 game:
7
+    look_around:
8
+        frequency: 1
9
+    engage:
10
+        frequency: 2
7
     move:
11
     move:
8
         walk_ref_time: 3
12
         walk_ref_time: 3
9
         run_ref_time: 1
13
         run_ref_time: 1

+ 2 - 0
sandbox/tile/const.py View File

7
 FLAG_URSS = 'URSS'
7
 FLAG_URSS = 'URSS'
8
 
8
 
9
 SIDE = 'SIDE'
9
 SIDE = 'SIDE'
10
+COMBAT_MODE = 'COMBAT_MODE'
11
+COMBAT_MODE_DEFENSE = 'COMBAT_MODE_DEFENSE'
10
 
12
 
11
 DE_COLOR = (0, 81, 211)
13
 DE_COLOR = (0, 81, 211)
12
 URSS_COLOR = (204, 0, 0)
14
 URSS_COLOR = (204, 0, 0)

+ 79 - 4
sandbox/tile/gui/base.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
+import os
3
+import random
2
 import typing
4
 import typing
3
 
5
 
6
+import pyglet
4
 from pyglet.window import key
7
 from pyglet.window import key
5
 
8
 
6
 from cocos.actions import MoveTo as BaseMoveTo
9
 from cocos.actions import MoveTo as BaseMoveTo
10
+from cocos.audio.pygame.mixer import Sound
7
 from sandbox.tile.user_action import UserAction
11
 from sandbox.tile.user_action import UserAction
8
 from synergine2.config import Config
12
 from synergine2.config import Config
9
 from synergine2.log import SynergineLogger
13
 from synergine2.log import SynergineLogger
22
 from synergine2_xyz.utils import get_angle
26
 from synergine2_xyz.utils import get_angle
23
 from sandbox.tile.simulation.event import NewVisibleOpponent
27
 from sandbox.tile.simulation.event import NewVisibleOpponent
24
 from sandbox.tile.simulation.event import NoLongerVisibleOpponent
28
 from sandbox.tile.simulation.event import NoLongerVisibleOpponent
29
+from sandbox.tile.simulation.event import FireEvent
30
+from sandbox.tile.simulation.event import DieEvent
25
 
31
 
26
 
32
 
27
 class EditLayer(BaseEditLayer):
33
 class EditLayer(BaseEditLayer):
44
     edit_layer_class = EditLayer
50
     edit_layer_class = EditLayer
45
 
51
 
46
 
52
 
53
+# TODO: Move into synergine2cocos2d
54
+class AudioLibrary(object):
55
+    sound_file_paths = {
56
+        'gunshot_default': '204010__duckduckpony__homemade-gunshot-2.ogg',
57
+    }
58
+
59
+    def __init__(self, sound_dir_path: str) -> None:
60
+        self._sound_dir_path = sound_dir_path
61
+        self._sounds = {}
62
+
63
+    def get_sound(self, name: str) -> Sound:
64
+        if name not in self._sounds:
65
+            sound_file_name = self.sound_file_paths[name]
66
+            self._sounds[name] = Sound(os.path.join(self._sound_dir_path, sound_file_name))
67
+        return self._sounds[name]
68
+
69
+
47
 class Game(TMXGui):
70
 class Game(TMXGui):
48
     layer_manager_class = TileLayerManager
71
     layer_manager_class = TileLayerManager
49
 
72
 
64
             read_queue_interval=read_queue_interval,
87
             read_queue_interval=read_queue_interval,
65
             map_dir_path=map_dir_path,
88
             map_dir_path=map_dir_path,
66
         )
89
         )
90
+        self.sound_lib = AudioLibrary('sandbox/tile/sounds/')
67
 
91
 
68
         self.terminal.register_event_handler(
92
         self.terminal.register_event_handler(
69
             FinishMoveEvent,
93
             FinishMoveEvent,
85
             self.no_longer_visible_opponent,
109
             self.no_longer_visible_opponent,
86
         )
110
         )
87
 
111
 
112
+        self.terminal.register_event_handler(
113
+            FireEvent,
114
+            self.fire_happen,
115
+        )
116
+
117
+        self.terminal.register_event_handler(
118
+            DieEvent,
119
+            self.subject_die,
120
+        )
121
+
88
         # configs
122
         # configs
89
         self.move_duration_ref = float(self.config.resolve('game.move.walk_ref_time'))
123
         self.move_duration_ref = float(self.config.resolve('game.move.walk_ref_time'))
90
         self.move_fast_duration_ref = float(self.config.resolve('game.move.run_ref_time'))
124
         self.move_fast_duration_ref = float(self.config.resolve('game.move.run_ref_time'))
147
             observer_actor = self.layer_manager.subject_layer.subjects_index[event.observer_subject_id]
181
             observer_actor = self.layer_manager.subject_layer.subjects_index[event.observer_subject_id]
148
             observed_actor = self.layer_manager.subject_layer.subjects_index[event.observed_subject_id]
182
             observed_actor = self.layer_manager.subject_layer.subjects_index[event.observed_subject_id]
149
 
183
 
150
-            observer_pixel_position = self.layer_manager.grid_manager.get_pixel_position_of_grid_position(
151
-                observer_actor.subject.position,
184
+            observer_pixel_position = self.layer_manager.scrolling_manager.world_to_screen(
185
+                *self.layer_manager.grid_manager.get_pixel_position_of_grid_position(
186
+                    observer_actor.subject.position,
187
+                )
152
             )
188
             )
153
-            observed_pixel_position = self.layer_manager.grid_manager.get_pixel_position_of_grid_position(
154
-                observed_actor.subject.position,
189
+            observed_pixel_position = self.layer_manager.scrolling_manager.world_to_screen(
190
+                *self.layer_manager.grid_manager.get_pixel_position_of_grid_position(
191
+                    observed_actor.subject.position,
192
+                )
155
             )
193
             )
156
 
194
 
157
             def draw_visible_opponent():
195
             def draw_visible_opponent():
161
                     line_color,
199
                     line_color,
162
                 )
200
                 )
163
 
201
 
202
+            # TODO: Not in edit layer !
164
             self.layer_manager.edit_layer.append_callback(draw_visible_opponent, 1.0)
203
             self.layer_manager.edit_layer.append_callback(draw_visible_opponent, 1.0)
204
+
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_pixel_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_pixel_position_of_grid_position(
215
+                event.target_position,
216
+            )
217
+        )
218
+
219
+        def gunshot_trace():
220
+            draw_line(
221
+                shooter_pixel_position,
222
+                fire_to_pixel_position,
223
+                color=(255, 0, 0),
224
+            )
225
+
226
+        def gunshot_sound():
227
+            self.sound_lib.get_sound('gunshot_default').play()
228
+
229
+        # To avoid all in same time
230
+        delay = random.uniform(0.0, 0.6)
231
+
232
+        self.layer_manager.edit_layer.append_callback(gunshot_trace, duration=0.1, delay=delay)
233
+        self.layer_manager.edit_layer.append_callback(gunshot_sound, duration=0.0, delay=delay)
234
+
235
+    def subject_die(self, event: DieEvent) -> None:
236
+        killed_actor = self.layer_manager.subject_layer.subjects_index[event.shoot_subject_id]
237
+        dead_image = pyglet.resource.image('maps/003/actors/man_d1.png')
238
+        killed_actor.update_image(dead_image)
239
+        killed_actor.freeze()

BIN
sandbox/tile/maps/003/actors/man_d1.png View File


+ 4 - 3
sandbox/tile/run.py View File

26
 from synergine2.terminals import TerminalManager
26
 from synergine2.terminals import TerminalManager
27
 
27
 
28
 
28
 
29
-def main(map_dir_path: str, seed_value: int=42):
30
-    seed(seed_value)
29
+def main(map_dir_path: str, seed_value: int=None):
30
+    if seed_value is not None:
31
+        seed(seed_value)
31
 
32
 
32
     config = Config()
33
     config = Config()
33
     config.load_yaml('sandbox/tile/config.yaml')
34
     config.load_yaml('sandbox/tile/config.yaml')
92
 if __name__ == '__main__':
93
 if __name__ == '__main__':
93
     parser = argparse.ArgumentParser(description='Run TileStrategy')
94
     parser = argparse.ArgumentParser(description='Run TileStrategy')
94
     parser.add_argument('map_dir_path', help='map directory path')
95
     parser.add_argument('map_dir_path', help='map directory path')
95
-    parser.add_argument('--seed', dest='seed', default=42)
96
+    parser.add_argument('--seed', dest='seed', default=None)
96
 
97
 
97
     args = parser.parse_args()
98
     args = parser.parse_args()
98
 
99
 

+ 7 - 1
sandbox/tile/simulation/base.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
-
2
+from sandbox.tile.const import COLLECTION_ALIVE
3
 from sandbox.tile.simulation.physics import TilePhysics
3
 from sandbox.tile.simulation.physics import TilePhysics
4
 from synergine2.config import Config
4
 from synergine2.config import Config
5
+from synergine2.simulation import SubjectBehaviour
5
 from synergine2_xyz.physics import Physics
6
 from synergine2_xyz.physics import Physics
6
 from synergine2_xyz.simulation import XYZSimulation
7
 from synergine2_xyz.simulation import XYZSimulation
7
 from synergine2_xyz.subjects import XYZSubject
8
 from synergine2_xyz.subjects import XYZSubject
34
 
35
 
35
 class BaseSubject(XYZSubject):
36
 class BaseSubject(XYZSubject):
36
     pass
37
     pass
38
+
39
+
40
+class AliveSubjectBehaviour(SubjectBehaviour):
41
+    def is_terminated(self) -> bool:
42
+        return COLLECTION_ALIVE not in self.subject.collections

+ 74 - 3
sandbox/tile/simulation/behaviour.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
+import random
2
 import time
3
 import time
4
+import typing
3
 
5
 
6
+from sandbox.tile.const import COLLECTION_ALIVE
7
+from sandbox.tile.simulation.base import AliveSubjectBehaviour
4
 from sandbox.tile.simulation.event import NoLongerVisibleOpponent
8
 from sandbox.tile.simulation.event import NoLongerVisibleOpponent
9
+from sandbox.tile.simulation.event import FireEvent
10
+from sandbox.tile.simulation.event import DieEvent
5
 from sandbox.tile.simulation.event import NewVisibleOpponent
11
 from sandbox.tile.simulation.event import NewVisibleOpponent
6
 from sandbox.tile.simulation.mechanism import OpponentVisibleMechanism
12
 from sandbox.tile.simulation.mechanism import OpponentVisibleMechanism
7
 from sandbox.tile.user_action import UserAction
13
 from sandbox.tile.user_action import UserAction
8
 from synergine2.config import Config
14
 from synergine2.config import Config
9
 from synergine2.simulation import Simulation
15
 from synergine2.simulation import Simulation
10
-from synergine2.simulation import SubjectBehaviour
11
 from synergine2.simulation import Event
16
 from synergine2.simulation import Event
12
 from synergine2.simulation import Subject
17
 from synergine2.simulation import Subject
13
 from synergine2_xyz.move.simulation import MoveToBehaviour as BaseMoveToBehaviour
18
 from synergine2_xyz.move.simulation import MoveToBehaviour as BaseMoveToBehaviour
25
         self._run_duration = float(self.config.resolve('game.move.run_ref_time'))
30
         self._run_duration = float(self.config.resolve('game.move.run_ref_time'))
26
         self._crawl_duration = float(self.config.resolve('game.move.crawl_ref_time'))
31
         self._crawl_duration = float(self.config.resolve('game.move.crawl_ref_time'))
27
 
32
 
33
+    def is_terminated(self) -> bool:
34
+        return COLLECTION_ALIVE not in self.subject.collections
35
+
28
     def _can_move_to_next_step(self, move_to_data: dict) -> bool:
36
     def _can_move_to_next_step(self, move_to_data: dict) -> bool:
29
         if move_to_data['gui_action'] == UserAction.ORDER_MOVE:
37
         if move_to_data['gui_action'] == UserAction.ORDER_MOVE:
30
             return time.time() - move_to_data['last_intention_time'] >= self._walk_duration
38
             return time.time() - move_to_data['last_intention_time'] >= self._walk_duration
34
             return time.time() - move_to_data['last_intention_time'] >= self._crawl_duration
42
             return time.time() - move_to_data['last_intention_time'] >= self._crawl_duration
35
 
43
 
36
 
44
 
37
-class LookAroundBehaviour(SubjectBehaviour):
45
+class LookAroundBehaviour(AliveSubjectBehaviour):
38
     """
46
     """
39
     Behaviour who permit to reference visible things like enemies
47
     Behaviour who permit to reference visible things like enemies
40
     """
48
     """
41
     visible_mechanism = OpponentVisibleMechanism
49
     visible_mechanism = OpponentVisibleMechanism
42
     use = [visible_mechanism]
50
     use = [visible_mechanism]
43
-    force_action = True
51
+
52
+    def __init__(self, *args, **kwargs) -> None:
53
+        super().__init__(*args, **kwargs)
54
+        self._seconds_frequency = float(self.config.resolve('game.look_around.frequency'))
55
+
56
+    @property
57
+    def seconds_frequency(self) -> typing.Optional[float]:
58
+        return self._seconds_frequency
44
 
59
 
45
     def action(self, data) -> [Event]:
60
     def action(self, data) -> [Event]:
46
         new_visible_subject_events = []
61
         new_visible_subject_events = []
80
             'new_visible_subject_ids': new_visible_subject_ids,
95
             'new_visible_subject_ids': new_visible_subject_ids,
81
             'no_longer_visible_subject_ids': no_longer_visible_subject_ids,
96
             'no_longer_visible_subject_ids': no_longer_visible_subject_ids,
82
         }
97
         }
98
+
99
+
100
+class EngageOpponent(AliveSubjectBehaviour):
101
+    visible_mechanism = OpponentVisibleMechanism
102
+    use = [visible_mechanism]
103
+
104
+    def __init__(self, *args, **kwargs) -> None:
105
+        super().__init__(*args, **kwargs)
106
+        self._seconds_frequency = float(self.config.resolve('game.engage.frequency'))
107
+
108
+    @property
109
+    def seconds_frequency(self) -> typing.Optional[float]:
110
+        return self._seconds_frequency
111
+
112
+    def action(self, data) -> [Event]:
113
+        kill = data['kill']
114
+        target_subject_id = data['target_subject_id']
115
+        target_subject = self.simulation.subjects.index[target_subject_id]
116
+        target_position = data['target_position']
117
+
118
+        events = list()
119
+        events.append(FireEvent(shooter_subject_id=self.subject.id, target_position=target_position))
120
+
121
+        if kill:
122
+            target_subject.collections.remove(COLLECTION_ALIVE)
123
+            # FIXME: Must be automatic when manipulate subject collections !
124
+            self.simulation.collections[COLLECTION_ALIVE].remove(target_subject_id)
125
+            self.simulation.collections[COLLECTION_ALIVE] = self.simulation.collections[COLLECTION_ALIVE]
126
+            events.append(DieEvent(shooter_subject_id=self.subject.id, shoot_subject_id=target_subject_id))
127
+
128
+        return events
129
+
130
+    def run(self, data):
131
+        visible_subjects = data[self.visible_mechanism]['visible_subjects']
132
+        if not visible_subjects:
133
+            return
134
+        # Manage selected target (can change, better visibility, etc ...)
135
+        # Manage weapon munition to be able to fire
136
+        # Manage fear/under fire ...
137
+        # Manage weapon reload time
138
+
139
+        # For dev fun, don't fire at random
140
+        if random.randint(1, 3) == -1:
141
+            # Executed but decided to fail
142
+            self.last_execution_time = time.time()
143
+            return False
144
+
145
+        target_subject = random.choice(visible_subjects)
146
+        kill = random.randint(0, 100) >= 75
147
+
148
+        # Manage fire miss or touch (visibility, fear, opponent hiding, etc ...)
149
+        return {
150
+            'kill': kill,
151
+            'target_subject_id': target_subject.id,
152
+            'target_position': target_subject.position,
153
+        }

+ 22 - 0
sandbox/tile/simulation/event.py View File

2
 
2
 
3
 
3
 
4
 # TODO: Reprendre les events Move, pour les lister tous ici
4
 # TODO: Reprendre les events Move, pour les lister tous ici
5
+import typing
6
+
5
 from synergine2.simulation import Event
7
 from synergine2.simulation import Event
6
 
8
 
7
 
9
 
23
     ) -> None:
25
     ) -> None:
24
         self.observer_subject_id = observer_subject_id
26
         self.observer_subject_id = observer_subject_id
25
         self.observed_subject_id = observed_subject_id
27
         self.observed_subject_id = observed_subject_id
28
+
29
+
30
+class FireEvent(Event):
31
+    def __init__(
32
+        self,
33
+        shooter_subject_id: int,
34
+        target_position: typing.Tuple[int, int],
35
+    ) -> None:
36
+        self.shooter_subject_id = shooter_subject_id
37
+        self.target_position = target_position
38
+
39
+
40
+class DieEvent(Event):
41
+    def __init__(
42
+        self,
43
+        shooter_subject_id: int,
44
+        shoot_subject_id: int,
45
+    ) -> None:
46
+        self.shooter_subject_id = shooter_subject_id
47
+        self.shoot_subject_id = shoot_subject_id

+ 5 - 3
sandbox/tile/simulation/mechanism.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
 import typing
2
 import typing
3
 
3
 
4
-from sandbox.tile.const import SIDE
4
+from sandbox.tile.const import SIDE, COLLECTION_ALIVE
5
 from synergine2_xyz.subjects import XYZSubject
5
 from synergine2_xyz.subjects import XYZSubject
6
 from synergine2_xyz.visible.simulation import VisibleMechanism
6
 from synergine2_xyz.visible.simulation import VisibleMechanism
7
 
7
 
8
 
8
 
9
 class OpponentVisibleMechanism(VisibleMechanism):
9
 class OpponentVisibleMechanism(VisibleMechanism):
10
+    from_collection = COLLECTION_ALIVE
11
+
10
     def reduce_subjects(self, subjects: typing.List[XYZSubject]) -> typing.Iterator[XYZSubject]:
12
     def reduce_subjects(self, subjects: typing.List[XYZSubject]) -> typing.Iterator[XYZSubject]:
11
-        def is_opponent(subject: XYZSubject) -> bool:
13
+        def filter_subject(subject: XYZSubject) -> bool:
12
             return self.subject.properties[SIDE] != subject.properties[SIDE]
14
             return self.subject.properties[SIDE] != subject.properties[SIDE]
13
 
15
 
14
-        return filter(is_opponent, subjects)
16
+        return filter(filter_subject, subjects)

+ 4 - 1
sandbox/tile/simulation/subject.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
-from sandbox.tile.const import COLLECTION_ALIVE
2
+from sandbox.tile.const import COLLECTION_ALIVE, COMBAT_MODE_DEFENSE
3
 from sandbox.tile.simulation.base import BaseSubject
3
 from sandbox.tile.simulation.base import BaseSubject
4
 from sandbox.tile.simulation.behaviour import MoveToBehaviour
4
 from sandbox.tile.simulation.behaviour import MoveToBehaviour
5
+from sandbox.tile.simulation.behaviour import EngageOpponent
5
 from sandbox.tile.simulation.behaviour import LookAroundBehaviour
6
 from sandbox.tile.simulation.behaviour import LookAroundBehaviour
6
 from synergine2.share import shared
7
 from synergine2.share import shared
7
 
8
 
13
     behaviours_classes = [
14
     behaviours_classes = [
14
         MoveToBehaviour,
15
         MoveToBehaviour,
15
         LookAroundBehaviour,
16
         LookAroundBehaviour,
17
+        EngageOpponent,
16
     ]
18
     ]
17
     visible_opponent_ids = shared.create_self('visible_opponent_ids', lambda: [])
19
     visible_opponent_ids = shared.create_self('visible_opponent_ids', lambda: [])
20
+    combat_mode = shared.create_self('combat_mode', COMBAT_MODE_DEFENSE)
18
     # TODO: implement (copied from engulf)
21
     # TODO: implement (copied from engulf)
19
     # behaviour_selector_class = CellBehaviourSelector
22
     # behaviour_selector_class = CellBehaviourSelector

BIN
sandbox/tile/sounds/204010__duckduckpony__homemade-gunshot-2.ogg View File


+ 4 - 0
sandbox/tile/terminal/base.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
 from sandbox.tile.simulation.event import NewVisibleOpponent
2
 from sandbox.tile.simulation.event import NewVisibleOpponent
3
+from sandbox.tile.simulation.event import FireEvent
4
+from sandbox.tile.simulation.event import DieEvent
3
 from sandbox.tile.simulation.event import NoLongerVisibleOpponent
5
 from sandbox.tile.simulation.event import NoLongerVisibleOpponent
4
 from sandbox.tile.simulation.physics import TilePhysics
6
 from sandbox.tile.simulation.physics import TilePhysics
5
 from sandbox.tile.simulation.subject import TileSubject as ManSubject
7
 from sandbox.tile.simulation.subject import TileSubject as ManSubject
16
         StartMoveEvent,
18
         StartMoveEvent,
17
         NewVisibleOpponent,
19
         NewVisibleOpponent,
18
         NoLongerVisibleOpponent,
20
         NoLongerVisibleOpponent,
21
+        FireEvent,
22
+        DieEvent,
19
     ]
23
     ]
20
 
24
 
21
     def __init__(self, *args, asynchronous: bool, map_dir_path: str, **kwargs):
25
     def __init__(self, *args, asynchronous: bool, map_dir_path: str, **kwargs):

+ 21 - 9
synergine2/cycle.py View File

2
 import multiprocessing
2
 import multiprocessing
3
 import typing
3
 import typing
4
 
4
 
5
+import time
6
+
5
 from synergine2.base import BaseObject
7
 from synergine2.base import BaseObject
6
 from synergine2.config import Config
8
 from synergine2.config import Config
7
 from synergine2.exceptions import SynergineException
9
 from synergine2.exceptions import SynergineException
291
                 str(len(subject_behaviours)),
293
                 str(len(subject_behaviours)),
292
             ))
294
             ))
293
 
295
 
294
-            for behaviour in subject_behaviours.values():
296
+            for behaviour_key, behaviour in list(subject_behaviours.items()):
295
                 self.logger.info('Subject {}: run {} behaviour'.format(
297
                 self.logger.info('Subject {}: run {} behaviour'.format(
296
                     str(subject.id),
298
                     str(subject.id),
297
                     str(type(behaviour)),
299
                     str(type(behaviour)),
298
                 ))
300
                 ))
299
 
301
 
300
-                # We identify behaviour data with it's class to be able to intersect it after subprocess data collect
301
-                with time_it() as elapsed_time:
302
-                    behaviour_data = behaviour.run(mechanisms_data)  # TODO: Behaviours dependencies
302
+                if behaviour.is_terminated():
303
+                    del subject.behaviours[behaviour_key]
303
 
304
 
304
-                if self.logger.is_debug:
305
-                    self.logger.debug('Subject {}: behaviour {} produce data: {} in {}s'.format(
306
-                        str(type(behaviour)),
305
+                # We identify behaviour data with it's class to be able to intersect it after subprocess data collect
306
+                if behaviour.is_skip(self.current_cycle):
307
+                    behaviour_data = False
308
+                    self.logger.debug('Subject {}: behaviour {} skip'.format(
307
                         str(subject.id),
309
                         str(subject.id),
308
-                        str(behaviour_data),
309
-                        elapsed_time.get_final_time(),
310
+                        str(type(behaviour)),
310
                     ))
311
                     ))
312
+                else:
313
+                    with time_it() as elapsed_time:
314
+                        behaviour.last_execution_time = time.time()
315
+                        behaviour_data = behaviour.run(mechanisms_data)  # TODO: Behaviours dependencies
316
+                        if self.logger.is_debug:
317
+                            self.logger.debug('Subject {}: behaviour {} produce data: {} in {}s'.format(
318
+                                str(type(behaviour)),
319
+                                str(subject.id),
320
+                                str(behaviour_data),
321
+                                elapsed_time.get_final_time(),
322
+                            ))
311
 
323
 
312
                 if behaviour_data:
324
                 if behaviour_data:
313
                     behaviours_data[behaviour.__class__] = behaviour_data
325
                     behaviours_data[behaviour.__class__] = behaviour_data

+ 6 - 2
synergine2/processing.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
+import random
2
 import typing
3
 import typing
3
 from multiprocessing import Process
4
 from multiprocessing import Process
4
 from multiprocessing.connection import Connection
5
 from multiprocessing.connection import Connection
41
             args=(
42
             args=(
42
                 self.local_write_pipe,
43
                 self.local_write_pipe,
43
                 self.process_read_pipe,
44
                 self.process_read_pipe,
44
-            )
45
+            ),
46
+            kwargs={'seed': random.random()}
45
         )
47
         )
46
-        self.db = None  # type: RedisDatabase
48
+        self.db = None  # TODO delete
47
         self.process.start()
49
         self.process.start()
48
 
50
 
49
     def work(self, *args, **kwargs):
51
     def work(self, *args, **kwargs):
52
+        seed_value = kwargs.pop('seed')
53
+        random.seed(seed_value)
50
         while True:
54
         while True:
51
             args = self.process_read_pipe.recv()
55
             args = self.process_read_pipe.recv()
52
             if args == STOP:
56
             if args == STOP:

+ 4 - 0
synergine2/share.py View File

102
         super().remove(object_)
102
         super().remove(object_)
103
         self.shared.set(self.shared_data.get_final_key(self.instance), list(self))
103
         self.shared.set(self.shared_data.get_final_key(self.instance), list(self))
104
 
104
 
105
+    def extend(self, iterable) -> None:
106
+        super().extend(iterable)
107
+        self.shared.set(self.shared_data.get_final_key(self.instance), list(self))
108
+
105
     # TODO: Cover all methods
109
     # TODO: Cover all methods
106
 
110
 
107
 
111
 

+ 32 - 3
synergine2/simulation.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
 import typing
2
 import typing
3
 
3
 
4
+import time
5
+
4
 from synergine2.base import BaseObject
6
 from synergine2.base import BaseObject
5
 from synergine2.base import IdentifiedObject
7
 from synergine2.base import IdentifiedObject
6
 from synergine2.config import Config
8
 from synergine2.config import Config
35
     behaviours_classes = []
37
     behaviours_classes = []
36
     behaviour_selector_class = None  # type: typing.Type[SubjectBehaviourSelector]
38
     behaviour_selector_class = None  # type: typing.Type[SubjectBehaviourSelector]
37
     intention_manager_class = None  # type: typing.Type[IntentionManager]
39
     intention_manager_class = None  # type: typing.Type[IntentionManager]
40
+    collections = shared.create_self('collections', lambda: [])
38
 
41
 
39
     def __init__(
42
     def __init__(
40
         self,
43
         self,
50
         """
53
         """
51
         super().__init__()
54
         super().__init__()
52
         # FIXME: use shared data to permit dynamic start_collections
55
         # FIXME: use shared data to permit dynamic start_collections
53
-        self.collections = self.start_collections[:]
56
+        self.collections.extend(self.start_collections[:])
54
 
57
 
55
         self.config = config
58
         self.config = config
56
         self._id = id(self)  # We store object id because it's lost between process
59
         self._id = id(self)  # We store object id because it's lost between process
183
     behaviours_classes = []
186
     behaviours_classes = []
184
 
187
 
185
     subject_classes = shared.create('subject_classes', {})
188
     subject_classes = shared.create('subject_classes', {})
186
-    collections = shared.create('start_collections', {})
189
+    collections = shared.create('collections', {})
187
 
190
 
188
     def __init__(
191
     def __init__(
189
         self,
192
         self,
285
 
288
 
286
 
289
 
287
 class SubjectBehaviour(Behaviour):
290
 class SubjectBehaviour(Behaviour):
288
-    frequency = 1
289
     use = []  # type: typing.List[typing.Type[SubjectMechanism]]
291
     use = []  # type: typing.List[typing.Type[SubjectMechanism]]
290
 
292
 
291
     def __init__(
293
     def __init__(
297
         self.config = config
299
         self.config = config
298
         self.simulation = simulation
300
         self.simulation = simulation
299
         self.subject = subject
301
         self.subject = subject
302
+        self.last_execution_time = 0
303
+
304
+    @property
305
+    def cycle_frequency(self) -> typing.Optional[float]:
306
+        return None
307
+
308
+    @property
309
+    def seconds_frequency(self) -> typing.Optional[float]:
310
+        return None
311
+
312
+    def is_terminated(self) -> bool:
313
+        """
314
+        :return: True if behaviour will no longer exist (can be removed from simulation)
315
+        """
316
+        return False
317
+
318
+    def is_skip(self, cycle_number: int) -> bool:
319
+        """
320
+        :return: True if behaviour have to be skip this time
321
+        """
322
+        if self.cycle_frequency is not None:
323
+            return not bool(cycle_number % self.cycle_frequency)
324
+
325
+        if self.seconds_frequency is not None:
326
+            return time.time() - self.last_execution_time <= self.seconds_frequency
327
+
328
+        return False
300
 
329
 
301
     def run(self, data):
330
     def run(self, data):
302
         """
331
         """

+ 13 - 0
synergine2_cocos2d/actor.py View File

44
         self.current_image = image
44
         self.current_image = image
45
         self.need_update_cshape = False
45
         self.need_update_cshape = False
46
         self.properties = properties or {}
46
         self.properties = properties or {}
47
+        self._freeze = False
48
+
49
+    def freeze(self) -> None:
50
+        """
51
+        Set object to freeze mode: No visual modification can be done anymore
52
+        """
53
+        self._freeze = True
47
 
54
 
48
     def stop_actions(self, action_types: typing.Tuple[typing.Type[cocos.actions.Action], ...]) -> None:
55
     def stop_actions(self, action_types: typing.Tuple[typing.Type[cocos.actions.Action], ...]) -> None:
49
         for action in self.actions:
56
         for action in self.actions:
59
         self.need_update_cshape = False
66
         self.need_update_cshape = False
60
 
67
 
61
     def update_position(self, new_position: euclid.Vector2) -> None:
68
     def update_position(self, new_position: euclid.Vector2) -> None:
69
+        if self._freeze:
70
+            return
71
+
62
         self.position = new_position
72
         self.position = new_position
63
         self.cshape.center = new_position  # Note: if remove: strange behaviour: drag change actor position with anomaly
73
         self.cshape.center = new_position  # Note: if remove: strange behaviour: drag change actor position with anomaly
64
 
74
 
79
         return self.current_image
89
         return self.current_image
80
 
90
 
81
     def update_image(self, new_image: pyglet.image.TextureRegion):
91
     def update_image(self, new_image: pyglet.image.TextureRegion):
92
+        if self._freeze:
93
+            return
94
+
82
         self.image = new_image
95
         self.image = new_image
83
         self.image_anchor = new_image.width // 2, new_image.height // 2
96
         self.image_anchor = new_image.width // 2, new_image.height // 2

+ 22 - 2
synergine2_cocos2d/gui.py View File

10
 import cocos
10
 import cocos
11
 from cocos import collision_model
11
 from cocos import collision_model
12
 from cocos import euclid
12
 from cocos import euclid
13
+from cocos.audio.pygame import mixer
13
 from cocos.layer import ScrollableLayer
14
 from cocos.layer import ScrollableLayer
14
 from synergine2.config import Config
15
 from synergine2.config import Config
15
 from synergine2.log import SynergineLogger
16
 from synergine2.log import SynergineLogger
122
         self,
123
         self,
123
         func: typing.Callable[[], None],
124
         func: typing.Callable[[], None],
124
         duration: float,
125
         duration: float,
126
+        delay: float=None,
125
     ) -> None:
127
     ) -> None:
126
         self.func = func
128
         self.func = func
127
         self.duration = duration
129
         self.duration = duration
128
         # Started timestamp
130
         # Started timestamp
129
         self.started = None  # type: float
131
         self.started = None  # type: float
132
+        self.require_delay = False
133
+        self.delay = delay
134
+        if delay is not None:
135
+            self.require_delay = True
130
 
136
 
131
     def execute(self) -> None:
137
     def execute(self) -> None:
138
+        if self.require_delay and not self.started:
139
+            self.started = time.time()
140
+            return
141
+        elif self.require_delay and time.time() - self.started < self.delay:
142
+            return
143
+        elif self.require_delay:
144
+            self.started = None
145
+            self.require_delay = False
146
+
132
         if self.started is None:
147
         if self.started is None:
133
             self.started = time.time()
148
             self.started = time.time()
134
 
149
 
135
-        if time.time() - self.started < self.duration:
150
+        if time.time() - self.started <= self.duration:
151
+            self.func()
152
+        elif not self.duration:
136
             self.func()
153
             self.func()
154
+            raise FinishedCallback()
137
         else:
155
         else:
138
             raise FinishedCallback()
156
             raise FinishedCallback()
139
 
157
 
231
         # TODO: In top level class: to be available in all layers
249
         # TODO: In top level class: to be available in all layers
232
         self.callbacks = []  # type: typing.List[Callback]
250
         self.callbacks = []  # type: typing.List[Callback]
233
 
251
 
234
-    def append_callback(self, callback: typing.Callable[[], None], duration: float) -> None:
252
+    def append_callback(self, callback: typing.Callable[[], None], duration: float, delay: float=None) -> None:
235
         self.callbacks.append(Callback(
253
         self.callbacks.append(Callback(
236
             callback,
254
             callback,
237
             duration,
255
             duration,
256
+            delay=delay,
238
         ))
257
         ))
239
 
258
 
240
     def set_selectable(self, actor: Actor) -> None:
259
     def set_selectable(self, actor: Actor) -> None:
719
             vsync=True,
738
             vsync=True,
720
             resizable=True,
739
             resizable=True,
721
         )
740
         )
741
+        mixer.init()
722
 
742
 
723
         self.interaction_manager = InteractionManager(
743
         self.interaction_manager = InteractionManager(
724
             config=self.config,
744
             config=self.config,

+ 12 - 1
synergine2_xyz/visible/simulation.py View File

8
 
8
 
9
 
9
 
10
 class VisibleMechanism(SubjectMechanism):
10
 class VisibleMechanism(SubjectMechanism):
11
+    from_collection = None
12
+
11
     def __init__(
13
     def __init__(
12
         self,
14
         self,
13
         config: Config,
15
         config: Config,
24
     def is_visible(self, observed: XYZSubject) -> bool:
26
     def is_visible(self, observed: XYZSubject) -> bool:
25
         return self.simulation.physics.subject_see_subject(self.subject, observed)
27
         return self.simulation.physics.subject_see_subject(self.subject, observed)
26
 
28
 
29
+    def _get_subject_iterable_from_collection(self, collection_name: str) -> typing.Iterator[XYZSubject]:
30
+        for subject_id in self.simulation.collections[collection_name]:
31
+            yield self.simulation.subjects.index[subject_id]
32
+
27
     def run(self) -> dict:
33
     def run(self) -> dict:
28
-        subjects_to_parse = self.reduce_subjects(self.simulation.subjects)
34
+        if self.from_collection is None:
35
+            subjects = self.simulation.subjects
36
+        else:
37
+            subjects = self._get_subject_iterable_from_collection(self.from_collection)
38
+
39
+        subjects_to_parse = self.reduce_subjects(subjects)
29
         subjects_visible = list(filter(self.is_visible, subjects_to_parse))
40
         subjects_visible = list(filter(self.is_visible, subjects_to_parse))
30
         return {
41
         return {
31
             'visible_subjects': subjects_visible,
42
             'visible_subjects': subjects_visible,