Completed
Push — master ( 2dfd0a...960489 )
by Oleksandr
04:25
created

__init__()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1
Metric Value
cc 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 9.4286
1
# -*- coding: utf-8 -*-
2 1
"""
3
This module contains parsers which work with sections related to air flights.
4
5
List of flights is defined in "Wing" section of game's mission file. However,
6
it contains list of flights, not wings. Wings stay above flight in air force
7
unit organization. See example for USA:
8
http://usmilitary.about.com/cs/airforce/a/aforganization.htm
9
10
Despite inconsistency between section name (Wing) and its contents (list of
11
flights), this module was named after section name to keep clear the origin of
12
the data.
13
"""
14
15 1
from il2fb.commons.flight import Formations, RoutePointTypes
16 1
from il2fb.commons.organization import AirForces, Regiments
17 1
from il2fb.commons.spatial import Point3D
18 1
from il2fb.commons.structures import BaseStructure
19
20 1
from ..constants import (
21
    ROUTE_POINT_EXTRA_PARAMETERS_MARK, ROUTE_POINT_RADIO_SILENCE_ON,
22
    ROUTE_POINT_RADIO_SILENCE_OFF,
23
)
24 1
from ..converters import to_skill
25 1
from . import CollectingParser, ValuesParser
26
27
28 1
class FlightSectionParser(CollectingParser):
29
    """
30
    Parses ``Wing`` section.
31
    View :ref:`detailed description <wing-section>`.
32
    """
33
34 1
    def check_section_name(self, section_name):
35 1
        return section_name == "Wing"
36
37 1
    def clean(self):
38 1
        return {'flights': self.data}
39
40
41 1
class FlightInfoSectionParser(ValuesParser):
42
    """
43
    Parses settings for a moving flight group.
44
    View :ref:`detailed description <flight-info-section>`.
45
    """
46
47 1
    def check_section_name(self, section_name):
48 1
        try:
49 1
            self._decompose_section_name(section_name)
50 1
        except Exception:
51 1
            return False
52
        else:
53 1
            return True
54
55 1
    def init_parser(self, section_name):
56 1
        super(FlightInfoSectionParser, self).init_parser(section_name)
57 1
        self.output_key = section_name
58 1
        self.flight_info = self._decompose_section_name(section_name)
59
60 1
    def _decompose_section_name(self, section_name):
61 1
        prefix = section_name[:-2]
62 1
        squadron, flight = section_name[-2:]
63
64 1
        try:
65 1
            regiment = None
66 1
            air_force = AirForces.get_by_flight_prefix(prefix)
67 1
        except ValueError:
68 1
            regiment = Regiments.get_by_code_name(prefix)
69 1
            air_force = regiment.air_force
70
71 1
        return {
72
            'id': section_name,
73
            'air_force': air_force,
74
            'regiment': regiment,
75
            'squadron_index': int(squadron),
76
            'flight_index': int(flight),
77
        }
78
79 1
    def clean(self):
80 1
        count = int(self.data['Planes'])
81 1
        code = self.data['Class'].split('.', 1)[1]
82 1
        aircrafts = []
83
84 1
        def _add_if_present(target, key, value):
85 1
            if value:
86 1
                target[key] = value
87
88 1
        for i in range(count):
89 1
            aircraft = {
90
                'index': i,
91
                'has_markings': self._has_markings(i),
92
                'skill': self._get_skill(i),
93
            }
94 1
            _add_if_present(
95
                aircraft, 'aircraft_skin', self._get_skin('skin', i))
96 1
            _add_if_present(
97
                aircraft, 'nose_art', self._get_skin('nose_art', i))
98 1
            _add_if_present(
99
                aircraft, 'pilot_skin', self._get_skin('pilot', i))
100 1
            _add_if_present(
101
                aircraft, 'spawn_object', self._get_spawn_object_id(i))
102 1
            aircrafts.append(aircraft)
103
104 1
        self.flight_info.update({
105
            'ai_only': 'OnlyAI' in self.data,
106
            'aircrafts': aircrafts,
107
            'code': code,
108
            'fuel': int(self.data['Fuel']),
109
            'with_parachutes': 'Parachute' not in self.data,
110
            'count': count,
111
            'weapons': self.data['weapons'],
112
        })
113
114 1
        return {self.output_key: self.flight_info}
115
116 1
    def _get_skill(self, aircraft_id):
117 1
        if 'Skill' in self.data:
118 1
            return to_skill(self.data['Skill'])
119
        else:
120 1
            return to_skill(self.data['Skill{:}'.format(aircraft_id)])
121
122 1
    def _has_markings(self, aircraft_id):
123 1
        return 'numberOn{:}'.format(aircraft_id) not in self.data
124
125 1
    def _get_skin(self, prefix, aircraft_id):
126 1
        return self.data.get('{:}{:}'.format(prefix, aircraft_id))
127
128 1
    def _get_spawn_object_id(self, aircraft_id):
129 1
        return self.data.get('spawn{:}'.format(aircraft_id))
130
131
132 1
class FlightRoutePoint(BaseStructure):
133 1
    __slots__ = ['type', 'pos', 'speed', 'formation', 'radio_silence', ]
134
135 1
    def __init__(self, type, pos, speed, formation, radio_silence):
136 1
        self.type = type
137 1
        self.pos = pos
138 1
        self.speed = speed
139 1
        self.formation = formation
140 1
        self.radio_silence = radio_silence
141
142 1
    def __repr__(self):
143 1
        return ("<{0} '{1};{2};{3}'>"
144
                .format(self.__class__.__name__,
145
                        self.pos.x, self.pos.y, self.pos.z))
146
147
148 1
class FlightRouteTakeoffPoint(FlightRoutePoint):
149 1
    __slots__ = FlightRoutePoint.__slots__ + ['delay', 'spacing', ]
150
151 1
    def __init__(self, type, pos, speed, formation, radio_silence, delay,
152
                 spacing):
153 1
        super(FlightRouteTakeoffPoint, self).__init__(
154
            type, pos, speed, formation, radio_silence)
155 1
        self.delay = delay
156 1
        self.spacing = spacing
157
158
159 1
class FlightRoutePatrolPoint(FlightRoutePoint):
160 1
    __slots__ = FlightRoutePoint.__slots__ + [
161
        'patrol_cycles', 'patrol_timeout',
162
        'pattern_angle', 'pattern_side_size', 'pattern_altitude_difference',
163
    ]
164
165 1
    def __init__(self, type, pos, speed, formation, radio_silence,
166
                 patrol_cycles, patrol_timeout, pattern_angle,
167
                 pattern_side_size, pattern_altitude_difference):
168 1
        super(FlightRoutePatrolPoint, self).__init__(
169
            type, pos, speed, formation, radio_silence)
170 1
        self.patrol_cycles = patrol_cycles
171 1
        self.patrol_timeout = patrol_timeout
172 1
        self.pattern_angle = pattern_angle
173 1
        self.pattern_side_size = pattern_side_size
174 1
        self.pattern_altitude_difference = pattern_altitude_difference
175
176
177 1
class FlightRouteAttackPoint(FlightRoutePoint):
178 1
    __slots__ = FlightRoutePoint.__slots__ + [
179
        'target_id', 'target_route_point',
180
    ]
181
182 1
    def __init__(self, type, pos, speed, formation, radio_silence, target_id,
183
                 target_route_point):
184 1
        super(FlightRouteAttackPoint, self).__init__(
185
            type, pos, speed, formation, radio_silence)
186 1
        self.target_id = target_id
187 1
        self.target_route_point = target_route_point
188
189
190 1
class FlightRouteSectionParser(CollectingParser):
191
    """
192
    Parses ``*_Way`` section.
193
    View :ref:`detailed description <flight-route-section>`.
194
    """
195 1
    input_suffix = "_Way"
196 1
    output_prefix = 'flight_route_'
197
198 1
    def check_section_name(self, section_name):
199 1
        return section_name.endswith(self.input_suffix)
200
201 1
    def _extract_flight_code(self, section_name):
202 1
        return section_name[:-len(self.input_suffix)]
203
204 1
    def init_parser(self, section_name):
205 1
        super(FlightRouteSectionParser, self).init_parser(section_name)
206 1
        flight_code = self._extract_flight_code(section_name)
207 1
        self.output_key = "{}{}".format(self.output_prefix, flight_code)
208 1
        self.point = None
209 1
        self.point_class = None
210
211 1
    def parse_line(self, line):
212 1
        params = line.split()
213 1
        type_code, params = params[0], params[1:]
214 1
        if type_code == ROUTE_POINT_EXTRA_PARAMETERS_MARK:
215 1
            self._parse_options(params)
216
        else:
217 1
            self._finalize_current_point()
218 1
            pos, speed, params = params[0:3], params[3], params[4:]
219 1
            self.point = {
220
                'type': RoutePointTypes.get_by_value(type_code),
221
                'pos': Point3D(*pos),
222
                'speed': float(speed),
223
            }
224 1
            self._parse_extra(params)
225
226 1
    def _parse_options(self, params):
227 1
        try:
228 1
            cycles, timeout, angle, side_size, altitude_difference = params
229 1
            self.point.update({
230
                'patrol_cycles': int(cycles),
231
                'patrol_timeout': int(timeout),
232
                'pattern_angle': int(angle),
233
                'pattern_side_size': int(side_size),
234
                'pattern_altitude_difference': int(altitude_difference),
235
            })
236 1
            self.point_class = FlightRoutePatrolPoint
237 1
        except ValueError:
238 1
            delay, spacing = params[1:3]
239 1
            self.point.update({
240
                'delay': int(delay),
241
                'spacing': int(spacing),
242
            })
243 1
            self.point_class = FlightRouteTakeoffPoint
244
245 1
    def _parse_extra(self, params):
246 1
        if FlightRouteSectionParser._is_new_game_version(params):
247 1
            radio_silence, formation, params = FlightRouteSectionParser._parse_new_version_extra(params)
248 1
            if params:
249 1
                self._parse_target(params)
250
        else:
251 1
            radio_silence = False
252 1
            formation = None
253
254 1
        self.point.update({
255
            'radio_silence': radio_silence,
256
            'formation': formation,
257
        })
258
259 1
    @staticmethod
260
    def _is_new_game_version(params):
261 1
        return (
262
            ROUTE_POINT_RADIO_SILENCE_ON in params
263
            or ROUTE_POINT_RADIO_SILENCE_OFF in params
264
        )
265
266 1
    @staticmethod
267
    def _parse_new_version_extra(params):
268 1
        try:
269 1
            index = params.index(ROUTE_POINT_RADIO_SILENCE_ON)
270 1
        except ValueError:
271 1
            index = params.index(ROUTE_POINT_RADIO_SILENCE_OFF)
272
273 1
        params, radio_silence, extra = params[:index], params[index], params[index+1:]
274
275 1
        radio_silence = radio_silence == ROUTE_POINT_RADIO_SILENCE_ON
276 1
        formation = Formations.get_by_value(extra[0]) if extra else None
277
278 1
        return radio_silence, formation, params
279
280 1
    def _parse_target(self, params):
281 1
        target_id, target_route_point = params[:2]
282
283 1
        self.point.update({
284
            'target_id': target_id,
285
            'target_route_point': int(target_route_point),
286
        })
287
288 1
        if self.point['type'] is RoutePointTypes.normal:
289 1
            self.point['type'] = RoutePointTypes.air_attack
290
291 1
        self.point_class = FlightRouteAttackPoint
292
293 1
    def clean(self):
294 1
        self._finalize_current_point()
295 1
        return {self.output_key: self.data}
296
297 1
    def _finalize_current_point(self):
298 1
        if self.point:
299 1
            point_class = getattr(self, 'point_class') or FlightRoutePoint
300 1
            self.data.append(point_class(**self.point))
301 1
            self.point = None
302
            self.point_class = None
303