Browse Source

dump subjects + use properties instead side

Bastien Sevajol 5 years ago
parent
commit
46ace62174

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

@@ -9,42 +9,120 @@
9 9
             <position>72,7</position>
10 10
             <direction>90</direction>
11 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 26
         </subject>
14 27
         <subject>
15 28
             <type>opencombat.simulation.subject.ManSubject</type>
16 29
             <position>72,9</position>
17 30
             <direction>90</direction>
18 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 46
         </subject>
21 47
         <subject>
22 48
             <type>opencombat.simulation.subject.ManSubject</type>
23 49
             <position>72,11</position>
24 50
             <direction>90</direction>
25 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 66
         </subject>
28 67
         <subject>
29 68
             <type>opencombat.simulation.subject.ManSubject</type>
30 69
             <position>58,54</position>
31 70
             <direction>270</direction>
32 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 86
         </subject>
35 87
         <subject>
36 88
             <type>opencombat.simulation.subject.ManSubject</type>
37 89
             <position>58,56</position>
38 90
             <direction>270</direction>
39 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 106
         </subject>
42 107
         <subject>
43 108
             <type>opencombat.simulation.subject.ManSubject</type>
44 109
             <position>58,58</position>
45 110
             <direction>270</direction>
46 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 126
         </subject>
49 127
     </subjects>
50 128
 </state>

+ 2 - 2
opencombat/const.py View File

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

+ 83 - 30
opencombat/state.py View File

@@ -6,19 +6,14 @@ from lxml import etree
6 6
 
7 7
 from synergine2.config import Config
8 8
 from synergine2.log import get_logger
9
-from synergine2_cocos2d.const import SELECTION_COLOR_RGB
10 9
 
11 10
 from opencombat.exception import StateLoadError
11
+from opencombat.exception import NotFoundError
12 12
 from opencombat.simulation.base import TileStrategySimulation
13 13
 from opencombat.simulation.subject import TileSubject
14 14
 from opencombat.util import get_class_from_string_path
15
+from opencombat.util import pretty_xml
15 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 19
 class State(object):
@@ -75,28 +70,33 @@ class State(object):
75 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 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 101
 class StateDumper(object):
102 102
     def __init__(
@@ -114,11 +114,64 @@ class StateDumper(object):
114 114
         )
115 115
         with open(state_template, 'r') as xml_file:
116 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 177
 class StateLoader(object):

+ 13 - 8
opencombat/state.xsd View File

@@ -10,13 +10,18 @@
10 10
         </xs:restriction>
11 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 26
     <xs:simpleType name="modetype" final="restriction">
22 27
         <xs:restriction base="xs:string">
@@ -56,7 +61,7 @@
56 61
             <xs:element name="position" type="positiontype" minOccurs="1"/>
57 62
             <xs:element name="direction" type="directiontype" minOccurs="1"/>
58 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 65
         </xs:sequence>
61 66
     </xs:complexType>
62 67
 

+ 17 - 0
opencombat/util.py View File

@@ -2,6 +2,8 @@
2 2
 import importlib
3 3
 
4 4
 from _elementtree import Element
5
+import xml.dom.minidom as md
6
+from io import StringIO
5 7
 
6 8
 from opencombat.exception import NotFoundError
7 9
 
@@ -38,3 +40,18 @@ def get_text_xml_element(
38 40
         return default_value
39 41
 
40 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,14 +9,12 @@
9 9
             <position>1,1</position>
10 10
             <direction>90</direction>
11 11
             <mode>standup</mode>
12
-            <side>URSS</side>
13 12
         </subject>
14 13
         <subjectFOO>
15 14
             <type>opencombat.simulation.subject.ManSubject</type>
16 15
             <position>10,10</position>
17 16
             <direction>270</direction>
18 17
             <mode>hiding</mode>
19
-            <side>DE</side>
20 18
         </subjectFOO>
21 19
     </subjects>
22 20
 </state>

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

@@ -9,14 +9,40 @@
9 9
             <position>1,1</position>
10 10
             <direction>90</direction>
11 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 26
         </subject>
14 27
         <subject>
15 28
             <type>opencombat.simulation.subject.ManSubject</type>
16 29
             <position>10,10</position>
17 30
             <direction>270</direction>
18 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 46
         </subject>
21 47
     </subjects>
22 48
 </state>

+ 70 - 12
tests/test_state.py View File

@@ -1,4 +1,6 @@
1 1
 # coding: utf-8
2
+from collections import OrderedDict
3
+
2 4
 import pytest
3 5
 from synergine2.config import Config
4 6
 from synergine2_cocos2d.const import SELECTION_COLOR_RGB
@@ -33,24 +35,25 @@ def simulation_for_dump(config) -> TileStrategySimulation:
33 35
         'tests/fixtures/map_a/map_a.tmx',
34 36
     )
35 37
     subjects = TileStrategySubjects(simulation=simulation)
38
+    simulation.subjects = subjects
36 39
 
37 40
     man1 = ManSubject(config, simulation)
38 41
     man1.position = (10, 11)
39 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 49
     man2 = ManSubject(config, simulation)
47 50
     man2.position = (16, 8)
48 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 58
     subjects.append(man1)
56 59
     subjects.append(man2)
@@ -107,11 +110,66 @@ def test_state__ok__subjects(
107 110
     assert 90.0 == state.subjects[0].direction
108 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 125
 def test_state__ok__dump(
112 126
     config: Config,
113 127
     simulation_for_dump: TileStrategySimulation,
114 128
 ):
115 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