Browse Source

dump subjects + use properties instead side

Bastien Sevajol 6 years ago
parent
commit
46ace62174

+ 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'

+ 83 - 30
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
17
-from opencombat.const import SIDE
18
-from opencombat.const import FLAG_DE
19
-from opencombat.const import DE_COLOR
20
-from opencombat.const import URSS_COLOR
21
-from opencombat.const import FLAG_URSS
22
 
17
 
23
 
18
 
24
 class State(object):
19
 class State(object):
75
             get_text_xml_element(subject_element, 'direction'),
70
             get_text_xml_element(subject_element, 'direction'),
76
         )
71
         )
77
 
72
 
78
-        # FIXME BS 218-06-14: There is a confusion: don't use side but
79
-        # "team". It probably need change in other code.
80
-        side = get_text_xml_element(subject_element, 'side')
81
-        if side == 'DE':
82
-            subject_properties.update({
83
-                SELECTION_COLOR_RGB: DE_COLOR,
84
-                FLAG: FLAG_DE,
85
-                SIDE: 'AXIS',
86
-            })
87
-        elif side == 'URSS':
88
-            subject_properties.update({
89
-                SELECTION_COLOR_RGB: URSS_COLOR,
90
-                FLAG: FLAG_URSS,
91
-                SIDE: 'ALLIES',
92
-            })
93
-        else:
94
-            raise NotImplementedError('Don\'t know "{}" side'.format(
95
-                side,
96
-            ))
73
+        properties_element = subject_element.find('properties')
74
+        decode_properties_map = self._get_decode_properties_map()
75
+
76
+        for item_element in properties_element.findall('item'):
77
+            key_text = item_element.find('key').text
78
+            value_text = item_element.find('value').text
79
+
80
+            try:
81
+                decoded_value = decode_properties_map[key_text](value_text)
82
+            except KeyError:
83
+                raise NotFoundError(
84
+                    'You try to load property "{}" but it is unknown'.format(
85
+                        key_text,
86
+                    )
87
+                )
88
+
89
+            subject_properties[key_text] = decoded_value
97
 
90
 
98
         subject.properties = subject_properties
91
         subject.properties = subject_properties
99
 
92
 
93
+    def _get_decode_properties_map(self) -> typing.Dict[str, typing.Callable[[str], typing.Any]]:  # nopep8
94
+        return {
95
+            'SELECTION_COLOR_RGB': lambda v: tuple(map(int, v.split(','))),
96
+            'FLAG': str,
97
+            'SIDE': str,
98
+        }
99
+
100
 
100
 
101
 class StateDumper(object):
101
 class StateDumper(object):
102
     def __init__(
102
     def __init__(
114
         )
114
         )
115
         with open(state_template, 'r') as xml_file:
115
         with open(state_template, 'r') as xml_file:
116
             template_str = xml_file.read()
116
             template_str = xml_file.read()
117
-        self._state_root = etree.fromstring(template_str.encode('utf-8'))
118
 
117
 
119
-    def get_state_dump(self) -> Element:
120
-        # TODO BS 2018-06-14: Code here xml construction
121
-        return self._state_root
118
+        parser = etree.XMLParser(remove_blank_text=True)
119
+        self._state_root = etree.fromstring(
120
+            template_str.encode('utf-8'),
121
+            parser,
122
+        )
123
+        self._state_root_filled = False
124
+
125
+    def get_state_dump(self) -> str:
126
+        if not self._state_root_filled:
127
+            self._fill_state_root()
128
+
129
+        return pretty_xml(
130
+            etree.tostring(
131
+                self._state_root,
132
+            ).decode('utf-8'),
133
+        )
134
+
135
+    def _fill_state_root(self) -> None:
136
+        subjects_element = self._state_root.find('subjects')
137
+
138
+        for subject in self._simulation.subjects:
139
+            subject_element = etree.SubElement(subjects_element, 'subject')
140
+
141
+            position_element = etree.SubElement(subject_element, 'type')
142
+            position_element.text = '.'.join([
143
+                subject.__module__,
144
+                subject.__class__.__name__,
145
+            ])
146
+
147
+            position_element = etree.SubElement(subject_element, 'position')
148
+            position_element.text = ','.join(map(str, subject.position))
149
+
150
+            direction_element = etree.SubElement(subject_element, 'direction')
151
+            direction_element.text = str(subject.direction)
152
+
153
+            properties_element = etree.SubElement(
154
+                subject_element,
155
+                'properties',
156
+            )
157
+            encode_properties_map = self._get_encode_properties_map()
158
+
159
+            for key, value in subject.properties.items():
160
+                item_element = etree.SubElement(properties_element, 'item')
161
+                key_element = etree.SubElement(item_element, 'key')
162
+                value_element = etree.SubElement(item_element, 'value')
163
+
164
+                key_element.text = str(key)
165
+                value_element.text = encode_properties_map[key](value)
166
+
167
+        self._state_root_filled = True
168
+
169
+    def _get_encode_properties_map(self) -> typing.Dict[str, typing.Callable[[typing.Any], str]]:  # nopep8:
170
+        return {
171
+            'SELECTION_COLOR_RGB': lambda v: ','.join(map(str, v)),
172
+            'FLAG': str,
173
+            'SIDE': str,
174
+        }
122
 
175
 
123
 
176
 
124
 class StateLoader(object):
177
 class StateLoader(object):

+ 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
 

+ 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
+    )

+ 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>

+ 70 - 12
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
4
 from synergine2_cocos2d.const import SELECTION_COLOR_RGB
6
 from synergine2_cocos2d.const import SELECTION_COLOR_RGB
33
         'tests/fixtures/map_a/map_a.tmx',
35
         'tests/fixtures/map_a/map_a.tmx',
34
     )
36
     )
35
     subjects = TileStrategySubjects(simulation=simulation)
37
     subjects = TileStrategySubjects(simulation=simulation)
38
+    simulation.subjects = subjects
36
 
39
 
37
     man1 = ManSubject(config, simulation)
40
     man1 = ManSubject(config, simulation)
38
     man1.position = (10, 11)
41
     man1.position = (10, 11)
39
     man1.direction = 42
42
     man1.direction = 42
40
-    man1.properties = {
41
-        SELECTION_COLOR_RGB: DE_COLOR,
42
-        FLAG: FLAG_DE,
43
-        SIDE: 'AXIS',
44
-    }
43
+    man1.properties = OrderedDict([
44
+        (SELECTION_COLOR_RGB, DE_COLOR),
45
+        (FLAG, FLAG_DE),
46
+        (SIDE, 'AXIS'),
47
+    ])
45
 
48
 
46
     man2 = ManSubject(config, simulation)
49
     man2 = ManSubject(config, simulation)
47
     man2.position = (16, 8)
50
     man2.position = (16, 8)
48
     man2.direction = 197
51
     man2.direction = 197
49
-    man2.properties = {
50
-        SELECTION_COLOR_RGB: URSS_COLOR,
51
-        FLAG: FLAG_URSS,
52
-        SIDE: 'ALLIES',
53
-    }
52
+    man2.properties = OrderedDict([
53
+        (SELECTION_COLOR_RGB, URSS_COLOR),
54
+        (FLAG, FLAG_URSS),
55
+        (SIDE, 'ALLIES'),
56
+    ])
54
 
57
 
55
     subjects.append(man1)
58
     subjects.append(man1)
56
     subjects.append(man2)
59
     subjects.append(man2)
107
     assert 90.0 == state.subjects[0].direction
110
     assert 90.0 == state.subjects[0].direction
108
     assert 270.0 == state.subjects[1].direction
111
     assert 270.0 == state.subjects[1].direction
109
 
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
+
110
 
124
 
111
 def test_state__ok__dump(
125
 def test_state__ok__dump(
112
     config: Config,
126
     config: Config,
113
     simulation_for_dump: TileStrategySimulation,
127
     simulation_for_dump: TileStrategySimulation,
114
 ):
128
 ):
115
     state_dumper = StateDumper(config, simulation_for_dump)
129
     state_dumper = StateDumper(config, simulation_for_dump)
116
-    state_xml = state_dumper.get_state_dump()
117
-    assert False
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