Browse Source

Merge branch 'feature/dump_state'

Bastien Sevajol 5 years ago
parent
commit
8a2ccf2c75

+ 1 - 0
README.md View File

39
 * C: crouch
39
 * C: crouch
40
 * M: move
40
 * M: move
41
 * F: fire (not implemented)
41
 * F: fire (not implemented)
42
+* S: Save current state into OpenCombat dir (or dir specified with `--state-save-dir`)

+ 2 - 0
config.yaml View File

22
 
22
 
23
 global:
23
 global:
24
     state_loader: "opencombat.state.StateLoader"
24
     state_loader: "opencombat.state.StateLoader"
25
+    state_dumper: "opencombat.state.StateDumper"
25
     state_schema: "opencombat/state.xsd"
26
     state_schema: "opencombat/state.xsd"
27
+    state_template: "opencombat/state_template.xml"
26
     cache_dir_path: 'cache'
28
     cache_dir_path: 'cache'
27
     include_path:
29
     include_path:
28
       maps:
30
       maps:

+ 84 - 6
maps/001/state1.xml View File

9
             <position>72,7</position>
9
             <position>72,7</position>
10
             <direction>90</direction>
10
             <direction>90</direction>
11
             <mode>standup</mode>
11
             <mode>standup</mode>
12
-            <side>URSS</side>
12
+            <properties>
13
+                <item>
14
+                    <key>SELECTION_COLOR_RGB</key>
15
+                    <value>204,0,0</value>
16
+                </item>
17
+                <item>
18
+                    <key>FLAG</key>
19
+                    <value>FLAG_URSS</value>
20
+                </item>
21
+                <item>
22
+                    <key>SIDE</key>
23
+                    <value>ALLIES</value>
24
+                </item>
25
+            </properties>
13
         </subject>
26
         </subject>
14
         <subject>
27
         <subject>
15
             <type>opencombat.simulation.subject.ManSubject</type>
28
             <type>opencombat.simulation.subject.ManSubject</type>
16
             <position>72,9</position>
29
             <position>72,9</position>
17
             <direction>90</direction>
30
             <direction>90</direction>
18
             <mode>standup</mode>
31
             <mode>standup</mode>
19
-            <side>URSS</side>
32
+            <properties>
33
+                <item>
34
+                    <key>SELECTION_COLOR_RGB</key>
35
+                    <value>204,0,0</value>
36
+                </item>
37
+                <item>
38
+                    <key>FLAG</key>
39
+                    <value>FLAG_URSS</value>
40
+                </item>
41
+                <item>
42
+                    <key>SIDE</key>
43
+                    <value>ALLIES</value>
44
+                </item>
45
+            </properties>
20
         </subject>
46
         </subject>
21
         <subject>
47
         <subject>
22
             <type>opencombat.simulation.subject.ManSubject</type>
48
             <type>opencombat.simulation.subject.ManSubject</type>
23
             <position>72,11</position>
49
             <position>72,11</position>
24
             <direction>90</direction>
50
             <direction>90</direction>
25
             <mode>standup</mode>
51
             <mode>standup</mode>
26
-            <side>URSS</side>
52
+            <properties>
53
+                <item>
54
+                    <key>SELECTION_COLOR_RGB</key>
55
+                    <value>204,0,0</value>
56
+                </item>
57
+                <item>
58
+                    <key>FLAG</key>
59
+                    <value>FLAG_URSS</value>
60
+                </item>
61
+                <item>
62
+                    <key>SIDE</key>
63
+                    <value>ALLIES</value>
64
+                </item>
65
+            </properties>
27
         </subject>
66
         </subject>
28
         <subject>
67
         <subject>
29
             <type>opencombat.simulation.subject.ManSubject</type>
68
             <type>opencombat.simulation.subject.ManSubject</type>
30
             <position>58,54</position>
69
             <position>58,54</position>
31
             <direction>270</direction>
70
             <direction>270</direction>
32
             <mode>hiding</mode>
71
             <mode>hiding</mode>
33
-            <side>DE</side>
72
+            <properties>
73
+                <item>
74
+                    <key>SELECTION_COLOR_RGB</key>
75
+                    <value>0,81,211</value>
76
+                </item>
77
+                <item>
78
+                    <key>FLAG</key>
79
+                    <value>FLAG_DE</value>
80
+                </item>
81
+                <item>
82
+                    <key>SIDE</key>
83
+                    <value>AXIS</value>
84
+                </item>
85
+            </properties>
34
         </subject>
86
         </subject>
35
         <subject>
87
         <subject>
36
             <type>opencombat.simulation.subject.ManSubject</type>
88
             <type>opencombat.simulation.subject.ManSubject</type>
37
             <position>58,56</position>
89
             <position>58,56</position>
38
             <direction>270</direction>
90
             <direction>270</direction>
39
             <mode>hiding</mode>
91
             <mode>hiding</mode>
40
-            <side>DE</side>
92
+            <properties>
93
+                <item>
94
+                    <key>SELECTION_COLOR_RGB</key>
95
+                    <value>0,81,211</value>
96
+                </item>
97
+                <item>
98
+                    <key>FLAG</key>
99
+                    <value>FLAG_DE</value>
100
+                </item>
101
+                <item>
102
+                    <key>SIDE</key>
103
+                    <value>AXIS</value>
104
+                </item>
105
+            </properties>
41
         </subject>
106
         </subject>
42
         <subject>
107
         <subject>
43
             <type>opencombat.simulation.subject.ManSubject</type>
108
             <type>opencombat.simulation.subject.ManSubject</type>
44
             <position>58,58</position>
109
             <position>58,58</position>
45
             <direction>270</direction>
110
             <direction>270</direction>
46
             <mode>hiding</mode>
111
             <mode>hiding</mode>
47
-            <side>DE</side>
112
+            <properties>
113
+                <item>
114
+                    <key>SELECTION_COLOR_RGB</key>
115
+                    <value>0,81,211</value>
116
+                </item>
117
+                <item>
118
+                    <key>FLAG</key>
119
+                    <value>FLAG_DE</value>
120
+                </item>
121
+                <item>
122
+                    <key>SIDE</key>
123
+                    <value>AXIS</value>
124
+                </item>
125
+            </properties>
48
         </subject>
126
         </subject>
49
     </subjects>
127
     </subjects>
50
 </state>
128
 </state>

+ 2 - 2
opencombat/const.py View File

3
 COLLECTION_ALIVE = 'ALIVE'
3
 COLLECTION_ALIVE = 'ALIVE'
4
 
4
 
5
 FLAG = 'FLAG'
5
 FLAG = 'FLAG'
6
-FLAG_DE = 'DE'
7
-FLAG_URSS = 'URSS'
6
+FLAG_DE = 'FLAG_DE'
7
+FLAG_URSS = 'FLAG_URSS'
8
 
8
 
9
 SIDE = 'SIDE'
9
 SIDE = 'SIDE'
10
 COMBAT_MODE = 'COMBAT_MODE'
10
 COMBAT_MODE = 'COMBAT_MODE'

+ 16 - 0
opencombat/gui/base.py View File

21
 from opencombat.gui.animation import ANIMATION_WALK
21
 from opencombat.gui.animation import ANIMATION_WALK
22
 from opencombat.gui.animation import ANIMATION_CRAWL
22
 from opencombat.gui.animation import ANIMATION_CRAWL
23
 from opencombat.gui.fire import GuiFiringEvent
23
 from opencombat.gui.fire import GuiFiringEvent
24
+from opencombat.gui.state import SaveStateInteraction
24
 from opencombat.simulation.interior import InteriorManager
25
 from opencombat.simulation.interior import InteriorManager
25
 from opencombat.simulation.tmx import TileMap
26
 from opencombat.simulation.tmx import TileMap
26
 from opencombat.user_action import UserAction
27
 from opencombat.user_action import UserAction
59
             if k == key.F:
60
             if k == key.F:
60
                 self.user_action_pending = UserAction.ORDER_FIRE
61
                 self.user_action_pending = UserAction.ORDER_FIRE
61
 
62
 
63
+        if k == key.S:
64
+            self.run_save_state()
65
+
62
     def draw(self) -> None:
66
     def draw(self) -> None:
63
         super().draw()
67
         super().draw()
64
 
68
 
69
+    def run_save_state(self) -> None:
70
+        interaction = self.layer_manager\
71
+            .interaction_manager\
72
+            .get_for_user_action(
73
+                UserAction.SAVE_STATE,
74
+            )
75
+        interaction.execute()
76
+
65
 
77
 
66
 class BackgroundLayer(cocos.layer.Layer):
78
 class BackgroundLayer(cocos.layer.Layer):
67
     def __init__(
79
     def __init__(
272
         self.layer_manager.interaction_manager.register(MoveFastActorInteraction, self.layer_manager)
284
         self.layer_manager.interaction_manager.register(MoveFastActorInteraction, self.layer_manager)
273
         self.layer_manager.interaction_manager.register(MoveCrawlActorInteraction, self.layer_manager)
285
         self.layer_manager.interaction_manager.register(MoveCrawlActorInteraction, self.layer_manager)
274
         self.layer_manager.interaction_manager.register(FireActorInteraction, self.layer_manager)
286
         self.layer_manager.interaction_manager.register(FireActorInteraction, self.layer_manager)
287
+        self.layer_manager.interaction_manager.register(
288
+            SaveStateInteraction,
289
+            self.layer_manager,
290
+        )
275
 
291
 
276
     def set_subject_position(
292
     def set_subject_position(
277
         self,
293
         self,

+ 18 - 0
opencombat/gui/state.py View File

1
+# coding: utf-8
2
+
3
+from synergine2.terminals import TerminalPackage
4
+from synergine2_cocos2d.interaction import Interaction
5
+
6
+from opencombat.simulation.state import SaveStateSimulationAction
7
+from opencombat.user_action import UserAction
8
+
9
+
10
+class SaveStateInteraction(Interaction):
11
+    gui_action = UserAction.SAVE_STATE
12
+
13
+    def get_package_for_terminal(self) -> TerminalPackage:
14
+        return TerminalPackage(
15
+            simulation_actions=[
16
+                (SaveStateSimulationAction, {}),
17
+            ]
18
+        )

+ 41 - 0
opencombat/simulation/state.py View File

1
+import os
2
+import time
3
+import typing
4
+
5
+from synergine2.config import Config
6
+from synergine2.simulation import SimulationBehaviour
7
+from synergine2.simulation import Event
8
+from synergine2.simulation import Simulation
9
+
10
+from opencombat.state import StateConstructorBuilder
11
+
12
+
13
+class SaveStateSimulationAction(SimulationBehaviour):
14
+    def __init__(
15
+        self,
16
+        config: Config,
17
+        simulation: Simulation,
18
+    ):
19
+        super().__init__(config, simulation)
20
+        self.state_dumper = StateConstructorBuilder(
21
+            config,
22
+            simulation,
23
+        ).get_state_dumper()
24
+        self.state_save_dir = self.config.resolve('_runtime.state_save_dir')
25
+
26
+    def run(self, data):
27
+        pass
28
+
29
+    def action(self, data) -> typing.List[Event]:
30
+        state_file_path = os.path.join(
31
+            self.state_save_dir,
32
+            'state_{}.xml'.format(time.time())
33
+        )
34
+        with open(state_file_path, 'w+') as file:
35
+            file.write(self.state_dumper.get_state_dump())
36
+
37
+        return []
38
+
39
+    @classmethod
40
+    def merge_data(cls, new_data, start_data=None):
41
+        pass

+ 120 - 25
opencombat/state.py View File

6
 
6
 
7
 from synergine2.config import Config
7
 from synergine2.config import Config
8
 from synergine2.log import get_logger
8
 from synergine2.log import get_logger
9
-from synergine2_cocos2d.const import SELECTION_COLOR_RGB
10
 
9
 
11
 from opencombat.exception import StateLoadError
10
 from opencombat.exception import StateLoadError
11
+from opencombat.exception import NotFoundError
12
 from opencombat.simulation.base import TileStrategySimulation
12
 from opencombat.simulation.base import TileStrategySimulation
13
 from opencombat.simulation.subject import TileSubject
13
 from opencombat.simulation.subject import TileSubject
14
 from opencombat.util import get_class_from_string_path
14
 from opencombat.util import get_class_from_string_path
15
+from opencombat.util import pretty_xml
15
 from opencombat.util import get_text_xml_element
16
 from opencombat.util import get_text_xml_element
16
-from opencombat.const import FLAG, SIDE
17
-from opencombat.const import FLAG_DE
18
-from opencombat.const import DE_COLOR
19
-from opencombat.const import URSS_COLOR
20
-from opencombat.const import FLAG_URSS
21
 
17
 
22
 
18
 
23
 class State(object):
19
 class State(object):
72
             get_text_xml_element(subject_element, 'direction'),
68
             get_text_xml_element(subject_element, 'direction'),
73
         )
69
         )
74
 
70
 
75
-        side = get_text_xml_element(subject_element, 'side')
76
-        if side == 'DE':
77
-            subject_properties.update({
78
-                SELECTION_COLOR_RGB: DE_COLOR,
79
-                FLAG: FLAG_DE,
80
-                SIDE: 'AXIS',
81
-            })
82
-        elif side == 'URSS':
83
-            subject_properties.update({
84
-                SELECTION_COLOR_RGB: URSS_COLOR,
85
-                FLAG: FLAG_URSS,
86
-                SIDE: 'ALLIES',
87
-            })
88
-        else:
89
-            raise NotImplementedError('Don\'t know "{}" side'.format(
90
-                side,
91
-            ))
71
+        properties_element = subject_element.find('properties')
72
+        decode_properties_map = self._get_decode_properties_map()
73
+
74
+        for item_element in properties_element.findall('item'):
75
+            key_text = item_element.find('key').text
76
+            value_text = item_element.find('value').text
77
+
78
+            try:
79
+                decoded_value = decode_properties_map[key_text](value_text)
80
+            except KeyError:
81
+                raise NotFoundError(
82
+                    'You try to load property "{}" but it is unknown'.format(
83
+                        key_text,
84
+                    )
85
+                )
86
+
87
+            subject_properties[key_text] = decoded_value
92
 
88
 
93
         subject.properties = subject_properties
89
         subject.properties = subject_properties
94
 
90
 
91
+    def _get_decode_properties_map(self) -> typing.Dict[str, typing.Callable[[str], typing.Any]]:  # nopep8
92
+        return {
93
+            'SELECTION_COLOR_RGB': lambda v: tuple(map(int, v.split(','))),
94
+            'FLAG': str,
95
+            'SIDE': str,
96
+        }
97
+
98
+
99
+class StateDumper(object):
100
+    def __init__(
101
+        self,
102
+        config: Config,
103
+        simulation: TileStrategySimulation,
104
+    ) -> None:
105
+        self._logger = get_logger('StateDumper', config)
106
+        self._config = config
107
+        self._simulation = simulation
108
+
109
+        state_template = self._config.resolve(
110
+           'global.state_template',
111
+           'opencombat/state_template.xml',
112
+        )
113
+        with open(state_template, 'r') as xml_file:
114
+            template_str = xml_file.read()
115
+
116
+        parser = etree.XMLParser(remove_blank_text=True)
117
+        self._state_root = etree.fromstring(
118
+            template_str.encode('utf-8'),
119
+            parser,
120
+        )
121
+        self._state_root_filled = False
122
+
123
+    def get_state_dump(self) -> str:
124
+        if not self._state_root_filled:
125
+            self._fill_state_root()
126
+
127
+        return pretty_xml(
128
+            etree.tostring(
129
+                self._state_root,
130
+            ).decode('utf-8'),
131
+        )
132
+
133
+    def _fill_state_root(self) -> None:
134
+        subjects_element = self._state_root.find('subjects')
135
+
136
+        for subject in self._simulation.subjects:
137
+            subject_element = etree.SubElement(subjects_element, 'subject')
138
+
139
+            position_element = etree.SubElement(subject_element, 'type')
140
+            position_element.text = '.'.join([
141
+                subject.__module__,
142
+                subject.__class__.__name__,
143
+            ])
144
+
145
+            position_element = etree.SubElement(subject_element, 'position')
146
+            position_element.text = ','.join(map(str, subject.position))
147
+
148
+            direction_element = etree.SubElement(subject_element, 'direction')
149
+            direction_element.text = str(subject.direction)
150
+
151
+            properties_element = etree.SubElement(
152
+                subject_element,
153
+                'properties',
154
+            )
155
+            encode_properties_map = self._get_encode_properties_map()
156
+
157
+            for key, value in subject.properties.items():
158
+                item_element = etree.SubElement(properties_element, 'item')
159
+                key_element = etree.SubElement(item_element, 'key')
160
+                value_element = etree.SubElement(item_element, 'value')
161
+
162
+                key_element.text = str(key)
163
+                value_element.text = encode_properties_map[key](value)
164
+
165
+        self._state_root_filled = True
166
+
167
+    def _get_encode_properties_map(self) -> typing.Dict[str, typing.Callable[[typing.Any], str]]:  # nopep8:
168
+        return {
169
+            'SELECTION_COLOR_RGB': lambda v: ','.join(map(str, v)),
170
+            'FLAG': str,
171
+            'SIDE': str,
172
+        }
173
+
95
 
174
 
96
 class StateLoader(object):
175
 class StateLoader(object):
97
     def __init__(
176
     def __init__(
178
         return doc
257
         return doc
179
 
258
 
180
 
259
 
181
-class StateLoaderBuilder(object):
260
+class StateConstructorBuilder(object):
182
     def __init__(
261
     def __init__(
183
         self,
262
         self,
184
         config: Config,
263
         config: Config,
185
         simulation: TileStrategySimulation,
264
         simulation: TileStrategySimulation,
186
     ) -> None:
265
     ) -> None:
187
-        self._logger = get_logger('StateLoader', config)
266
+        self._logger = get_logger('StateConstructorBuilder', config)
188
         self._config = config
267
         self._config = config
189
         self._simulation = simulation
268
         self._simulation = simulation
190
 
269
 
203
             self._config,
282
             self._config,
204
             self._simulation,
283
             self._simulation,
205
         )
284
         )
285
+
286
+    def get_state_dumper(
287
+        self,
288
+    ) -> StateDumper:
289
+        class_address = self._config.resolve(
290
+            'global.state_dumper',
291
+            'opencombat.state.StateDumper',
292
+        )
293
+        state_loader_class = get_class_from_string_path(
294
+            self._config,
295
+            class_address,
296
+        )
297
+        return state_loader_class(
298
+            self._config,
299
+            self._simulation,
300
+        )

+ 13 - 8
opencombat/state.xsd View File

10
         </xs:restriction>
10
         </xs:restriction>
11
     </xs:simpleType>
11
     </xs:simpleType>
12
 
12
 
13
-    <xs:simpleType name="sidetype" final="restriction">
14
-        <xs:restriction base="xs:string">
15
-            <xs:enumeration value="DE"/>
16
-            <xs:enumeration value="URSS"/>
17
-            <xs:enumeration value="US"/>
18
-        </xs:restriction>
19
-    </xs:simpleType>
13
+    <xs:complexType name="propertiestype">
14
+        <xs:sequence>
15
+            <xs:element maxOccurs="unbounded" name="item">
16
+                <xs:complexType>
17
+                    <xs:sequence>
18
+                        <xs:element name="key" type="xs:string"  />
19
+                        <xs:element name="value" type="xs:string" />
20
+                    </xs:sequence>
21
+                </xs:complexType>
22
+            </xs:element>
23
+        </xs:sequence>
24
+    </xs:complexType>
20
 
25
 
21
     <xs:simpleType name="modetype" final="restriction">
26
     <xs:simpleType name="modetype" final="restriction">
22
         <xs:restriction base="xs:string">
27
         <xs:restriction base="xs:string">
56
             <xs:element name="position" type="positiontype" minOccurs="1"/>
61
             <xs:element name="position" type="positiontype" minOccurs="1"/>
57
             <xs:element name="direction" type="directiontype" minOccurs="1"/>
62
             <xs:element name="direction" type="directiontype" minOccurs="1"/>
58
             <xs:element name="mode" type="modetype" minOccurs="1"/>
63
             <xs:element name="mode" type="modetype" minOccurs="1"/>
59
-            <xs:element name="side" type="sidetype" minOccurs="1"/>
64
+            <xs:element name="properties" type="propertiestype" minOccurs="1"/>
60
         </xs:sequence>
65
         </xs:sequence>
61
     </xs:complexType>
66
     </xs:complexType>
62
 
67
 

+ 7 - 0
opencombat/state_template.xml View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<state type="before_battle">
3
+    <map>
4
+    </map>
5
+    <subjects>
6
+    </subjects>
7
+</state>

+ 1 - 0
opencombat/user_action.py View File

3
 
3
 
4
 
4
 
5
 class UserAction(BaseUserAction):
5
 class UserAction(BaseUserAction):
6
+    SAVE_STATE = 'SAVE_STATE'
6
     ORDER_MOVE = 'ORDER_MOVE'
7
     ORDER_MOVE = 'ORDER_MOVE'
7
     ORDER_MOVE_FAST = 'ORDER_MOVE_FAST'
8
     ORDER_MOVE_FAST = 'ORDER_MOVE_FAST'
8
     ORDER_MOVE_CRAWL = 'ORDER_MOVE_CRAWL'
9
     ORDER_MOVE_CRAWL = 'ORDER_MOVE_CRAWL'

+ 17 - 0
opencombat/util.py View File

2
 import importlib
2
 import importlib
3
 
3
 
4
 from _elementtree import Element
4
 from _elementtree import Element
5
+import xml.dom.minidom as md
6
+from io import StringIO
5
 
7
 
6
 from opencombat.exception import NotFoundError
8
 from opencombat.exception import NotFoundError
7
 
9
 
38
         return default_value
40
         return default_value
39
 
41
 
40
     return found.text
42
     return found.text
43
+
44
+
45
+def pretty_xml(xml_str):
46
+    """
47
+    Return a pretty xmlstr of given xml str. Thank's to:
48
+        https://gist.github.com/eliask/d8517790b11edac75983d1e6fdab3cab
49
+    :param xml_str: ugly xml str
50
+    :return: pretty xml str
51
+    """
52
+    indent = ' ' * 4
53
+    return '\n'.join(
54
+        line for line in
55
+        md.parse(StringIO(xml_str)).toprettyxml(indent=indent).split('\n')
56
+        if line.strip()
57
+    )

+ 18 - 3
run.py View File

12
 
12
 
13
 from opencombat.simulation.base import TileStrategySimulation
13
 from opencombat.simulation.base import TileStrategySimulation
14
 from opencombat.simulation.base import TileStrategySubjects
14
 from opencombat.simulation.base import TileStrategySubjects
15
-from opencombat.state import StateLoaderBuilder
15
+from opencombat.state import StateConstructorBuilder
16
 from opencombat.terminal.base import CocosTerminal
16
 from opencombat.terminal.base import CocosTerminal
17
 
17
 
18
 
18
 
20
     map_dir_path: str,
20
     map_dir_path: str,
21
     seed_value: int=None,
21
     seed_value: int=None,
22
     state_file_path: str=None,
22
     state_file_path: str=None,
23
+    state_save_dir: str='.',
23
 ):
24
 ):
24
     if seed_value is not None:
25
     if seed_value is not None:
25
         seed(seed_value)
26
         seed(seed_value)
26
 
27
 
27
     config = Config()
28
     config = Config()
28
     config.load_yaml('config.yaml')
29
     config.load_yaml('config.yaml')
30
+
31
+    # Runtime config
32
+    config.setdefault('_runtime', {})['state_save_dir'] = state_save_dir
33
+
29
     level = logging.getLevelName(config.resolve('global.logging_level', 'ERROR'))
34
     level = logging.getLevelName(config.resolve('global.logging_level', 'ERROR'))
30
     logger = get_default_logger(level=level)
35
     logger = get_default_logger(level=level)
31
 
36
 
35
     subjects = TileStrategySubjects(simulation=simulation)
40
     subjects = TileStrategySubjects(simulation=simulation)
36
 
41
 
37
     if state_file_path:
42
     if state_file_path:
38
-        state_loader_builder = StateLoaderBuilder(config, simulation)
43
+        state_loader_builder = StateConstructorBuilder(config, simulation)
39
         state_loader = state_loader_builder.get_state_loader()
44
         state_loader = state_loader_builder.get_state_loader()
40
         state = state_loader.get_state(state_file_path)
45
         state = state_loader.get_state(state_file_path)
41
         subjects.extend(state.subjects)
46
         subjects.extend(state.subjects)
66
     parser.add_argument('map_dir_path', help='map directory path')
71
     parser.add_argument('map_dir_path', help='map directory path')
67
     parser.add_argument('--seed', dest='seed', default=None)
72
     parser.add_argument('--seed', dest='seed', default=None)
68
     parser.add_argument('--state', dest='state', default=None)
73
     parser.add_argument('--state', dest='state', default=None)
74
+    parser.add_argument(
75
+        '--state-save-dir',
76
+        dest='state_save_dir',
77
+        default='.',
78
+    )
69
 
79
 
70
     args = parser.parse_args()
80
     args = parser.parse_args()
71
 
81
 
72
-    main(args.map_dir_path, seed_value=args.seed, state_file_path=args.state)
82
+    main(
83
+        args.map_dir_path,
84
+        seed_value=args.seed,
85
+        state_file_path=args.state,
86
+        state_save_dir=args.state_save_dir,
87
+    )

+ 0 - 2
tests/fixtures/state_error_schema.xml View File

9
             <position>1,1</position>
9
             <position>1,1</position>
10
             <direction>90</direction>
10
             <direction>90</direction>
11
             <mode>standup</mode>
11
             <mode>standup</mode>
12
-            <side>URSS</side>
13
         </subject>
12
         </subject>
14
         <subjectFOO>
13
         <subjectFOO>
15
             <type>opencombat.simulation.subject.ManSubject</type>
14
             <type>opencombat.simulation.subject.ManSubject</type>
16
             <position>10,10</position>
15
             <position>10,10</position>
17
             <direction>270</direction>
16
             <direction>270</direction>
18
             <mode>hiding</mode>
17
             <mode>hiding</mode>
19
-            <side>DE</side>
20
         </subjectFOO>
18
         </subjectFOO>
21
     </subjects>
19
     </subjects>
22
 </state>
20
 </state>

+ 28 - 2
tests/fixtures/state_ok.xml View File

9
             <position>1,1</position>
9
             <position>1,1</position>
10
             <direction>90</direction>
10
             <direction>90</direction>
11
             <mode>standup</mode>
11
             <mode>standup</mode>
12
-            <side>URSS</side>
12
+            <properties>
13
+                <item>
14
+                    <key>SELECTION_COLOR_RGB</key>
15
+                    <value>204,0,0</value>
16
+                </item>
17
+                <item>
18
+                    <key>FLAG</key>
19
+                    <value>FLAG_URSS</value>
20
+                </item>
21
+                <item>
22
+                    <key>SIDE</key>
23
+                    <value>ALLIES</value>
24
+                </item>
25
+            </properties>
13
         </subject>
26
         </subject>
14
         <subject>
27
         <subject>
15
             <type>opencombat.simulation.subject.ManSubject</type>
28
             <type>opencombat.simulation.subject.ManSubject</type>
16
             <position>10,10</position>
29
             <position>10,10</position>
17
             <direction>270</direction>
30
             <direction>270</direction>
18
             <mode>hiding</mode>
31
             <mode>hiding</mode>
19
-            <side>DE</side>
32
+            <properties>
33
+                <item>
34
+                    <key>SELECTION_COLOR_RGB</key>
35
+                    <value>0,81,211</value>
36
+                </item>
37
+                <item>
38
+                    <key>FLAG</key>
39
+                    <value>FLAG_DE</value>
40
+                </item>
41
+                <item>
42
+                    <key>SIDE</key>
43
+                    <value>AXIS</value>
44
+                </item>
45
+            </properties>
20
         </subject>
46
         </subject>
21
     </subjects>
47
     </subjects>
22
 </state>
48
 </state>

+ 111 - 2
tests/test_state.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
+from collections import OrderedDict
3
+
2
 import pytest
4
 import pytest
3
 from synergine2.config import Config
5
 from synergine2.config import Config
6
+from synergine2_cocos2d.const import SELECTION_COLOR_RGB
4
 
7
 
5
 from opencombat.exception import StateLoadError
8
 from opencombat.exception import StateLoadError
9
+from opencombat.simulation.base import TileStrategySimulation
10
+from opencombat.simulation.base import TileStrategySubjects
6
 from opencombat.simulation.subject import ManSubject
11
 from opencombat.simulation.subject import ManSubject
7
-from opencombat.state import StateLoaderBuilder, StateLoader
12
+from opencombat.state import StateConstructorBuilder, StateDumper
13
+from opencombat.state import StateLoader
14
+from opencombat.const import FLAG
15
+from opencombat.const import SIDE
16
+from opencombat.const import FLAG_DE
17
+from opencombat.const import DE_COLOR
18
+from opencombat.const import URSS_COLOR
19
+from opencombat.const import FLAG_URSS
8
 
20
 
9
 
21
 
10
 class MyStateLoader(StateLoader):
22
 class MyStateLoader(StateLoader):
16
     return StateLoader(config, simulation)
28
     return StateLoader(config, simulation)
17
 
29
 
18
 
30
 
31
+@pytest.fixture
32
+def simulation_for_dump(config) -> TileStrategySimulation:
33
+    simulation = TileStrategySimulation(
34
+        config,
35
+        'tests/fixtures/map_a/map_a.tmx',
36
+    )
37
+    subjects = TileStrategySubjects(simulation=simulation)
38
+    simulation.subjects = subjects
39
+
40
+    man1 = ManSubject(config, simulation)
41
+    man1.position = (10, 11)
42
+    man1.direction = 42
43
+    man1.properties = OrderedDict([
44
+        (SELECTION_COLOR_RGB, DE_COLOR),
45
+        (FLAG, FLAG_DE),
46
+        (SIDE, 'AXIS'),
47
+    ])
48
+
49
+    man2 = ManSubject(config, simulation)
50
+    man2.position = (16, 8)
51
+    man2.direction = 197
52
+    man2.properties = OrderedDict([
53
+        (SELECTION_COLOR_RGB, URSS_COLOR),
54
+        (FLAG, FLAG_URSS),
55
+        (SIDE, 'ALLIES'),
56
+    ])
57
+
58
+    subjects.append(man1)
59
+    subjects.append(man2)
60
+
61
+    return simulation
62
+
63
+
19
 def test_state_loader_builder__ok__nominal_case(
64
 def test_state_loader_builder__ok__nominal_case(
20
     simulation,
65
     simulation,
21
 ):
66
 ):
24
             'state_loader': 'tests.test_state.MyStateLoader',
69
             'state_loader': 'tests.test_state.MyStateLoader',
25
         }
70
         }
26
     })
71
     })
27
-    builder = StateLoaderBuilder(config, simulation)
72
+    builder = StateConstructorBuilder(config, simulation)
28
     state_loader = builder.get_state_loader()
73
     state_loader = builder.get_state_loader()
29
     assert type(state_loader) == MyStateLoader
74
     assert type(state_loader) == MyStateLoader
30
 
75
 
64
     assert (10, 10) == state.subjects[1].position
109
     assert (10, 10) == state.subjects[1].position
65
     assert 90.0 == state.subjects[0].direction
110
     assert 90.0 == state.subjects[0].direction
66
     assert 270.0 == state.subjects[1].direction
111
     assert 270.0 == state.subjects[1].direction
112
+
113
+    assert {
114
+               'SELECTION_COLOR_RGB': (204, 0, 0),
115
+               'FLAG': 'FLAG_URSS',
116
+               'SIDE': 'ALLIES',
117
+           } == state.subjects[0].properties
118
+    assert {
119
+               'SELECTION_COLOR_RGB': (0, 81, 211),
120
+               'FLAG': 'FLAG_DE',
121
+               'SIDE': 'AXIS',
122
+           } == state.subjects[1].properties
123
+
124
+
125
+def test_state__ok__dump(
126
+    config: Config,
127
+    simulation_for_dump: TileStrategySimulation,
128
+):
129
+    state_dumper = StateDumper(config, simulation_for_dump)
130
+    state_xml_str = state_dumper.get_state_dump()
131
+    assert """<?xml version="1.0" ?>
132
+<state type="before_battle">
133
+    <map>
134
+    </map>
135
+    <subjects>
136
+        <subject>
137
+            <type>opencombat.simulation.subject.ManSubject</type>
138
+            <position>10,11</position>
139
+            <direction>42</direction>
140
+            <properties>
141
+                <item>
142
+                    <key>SELECTION_COLOR_RGB</key>
143
+                    <value>0,81,211</value>
144
+                </item>
145
+                <item>
146
+                    <key>FLAG</key>
147
+                    <value>FLAG_DE</value>
148
+                </item>
149
+                <item>
150
+                    <key>SIDE</key>
151
+                    <value>AXIS</value>
152
+                </item>
153
+            </properties>
154
+        </subject>
155
+        <subject>
156
+            <type>opencombat.simulation.subject.ManSubject</type>
157
+            <position>16,8</position>
158
+            <direction>197</direction>
159
+            <properties>
160
+                <item>
161
+                    <key>SELECTION_COLOR_RGB</key>
162
+                    <value>204,0,0</value>
163
+                </item>
164
+                <item>
165
+                    <key>FLAG</key>
166
+                    <value>FLAG_URSS</value>
167
+                </item>
168
+                <item>
169
+                    <key>SIDE</key>
170
+                    <value>ALLIES</value>
171
+                </item>
172
+            </properties>
173
+        </subject>
174
+    </subjects>
175
+</state>""" == state_xml_str