123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- # coding: utf-8
- import typing
-
- from _elementtree import Element
- from lxml import etree
-
- from synergine2.config import Config
- from synergine2.log import get_logger
-
- from opencombat.exception import NotFoundError
- from opencombat.simulation.base import TileStrategySimulation
- from opencombat.simulation.subject import TileSubject
- from opencombat.util import get_class_from_string_path
- from opencombat.util import pretty_xml
- from opencombat.util import get_text_xml_element
- from opencombat.xml import XmlValidator
-
-
- class State(object):
- def __init__(
- self,
- config: Config,
- state_root: Element,
- simulation: TileStrategySimulation,
- ) -> None:
- self._config = config
- self._state_root = state_root
- self._subjects = None # type: typing.List[TileSubject]
- self._simulation = simulation
-
- @property
- def subjects(self) -> typing.List[TileSubject]:
- if self._subjects is None:
- self._subjects = self._get_subjects()
-
- return self._subjects
-
- def _get_subjects(self) -> typing.List[TileSubject]:
- subjects = []
- subject_elements = self._state_root.find('subjects').findall('subject')
-
- for subject_element in subject_elements:
- subject_class_path = subject_element.find('type').text
- subject_class = get_class_from_string_path(
- self._config,
- subject_class_path,
- )
- subject = subject_class(self._config, self._simulation)
- self._fill_subject(subject, subject_element)
- subjects.append(subject)
-
- return subjects
-
- def _fill_subject(
- self,
- subject: TileSubject,
- subject_element: Element,
- ) -> None:
- subject_properties = {}
-
- subject.position = tuple(
- map(
- int,
- get_text_xml_element(subject_element, 'position').split(','),
- ),
- )
- subject.direction = float(
- get_text_xml_element(subject_element, 'direction'),
- )
- # TODO BS 2018-06-20: Maybe need apply this mode no ?
- subject.combat_mode = \
- get_text_xml_element(subject_element, 'combat_mode')
-
- properties_element = subject_element.find('properties')
- decode_properties_map = self._get_decode_properties_map()
-
- for item_element in properties_element.findall('item'):
- key_text = item_element.find('key').text
- value_text = item_element.find('value').text
-
- try:
- decoded_value = decode_properties_map[key_text](value_text)
- except KeyError:
- raise NotFoundError(
- 'You try to load property "{}" but it is unknown'.format(
- key_text,
- )
- )
-
- subject_properties[key_text] = decoded_value
-
- subject.properties = subject_properties
-
- def _get_decode_properties_map(self) -> typing.Dict[str, typing.Callable[[str], typing.Any]]: # nopep8
- return {
- 'SELECTION_COLOR_RGB': lambda v: tuple(map(int, v.split(','))),
- 'FLAG': str,
- 'SIDE': str,
- }
-
-
- class StateDumper(object):
- def __init__(
- self,
- config: Config,
- simulation: TileStrategySimulation,
- ) -> None:
- self._logger = get_logger('StateDumper', config)
- self._config = config
- self._simulation = simulation
-
- state_template = self._config.resolve(
- 'global.state_template',
- 'opencombat/state_template.xml',
- )
- with open(state_template, 'r') as xml_file:
- template_str = xml_file.read()
-
- parser = etree.XMLParser(remove_blank_text=True)
- self._state_root = etree.fromstring(
- template_str.encode('utf-8'),
- parser,
- )
- self._state_root_filled = False
-
- def get_state_dump(self) -> str:
- if not self._state_root_filled:
- self._fill_state_root()
-
- return pretty_xml(
- etree.tostring(
- self._state_root,
- ).decode('utf-8'),
- )
-
- def _fill_state_root(self) -> None:
- subjects_element = self._state_root.find('subjects')
- map_element = self._state_root.find('map')
-
- map_name_element = etree.SubElement(map_element, 'name')
- map_name_element.text = self._config.resolve('_runtime.map_dir_path')
-
- for subject in self._simulation.subjects:
- subject_element = etree.SubElement(subjects_element, 'subject')
-
- position_element = etree.SubElement(subject_element, 'type')
- position_element.text = '.'.join([
- subject.__module__,
- subject.__class__.__name__,
- ])
-
- position_element = etree.SubElement(subject_element, 'position')
- position_element.text = ','.join(map(str, subject.position))
-
- direction_element = etree.SubElement(subject_element, 'direction')
- direction_element.text = str(subject.direction)
-
- combat_mode_element = etree.SubElement(
- subject_element,
- 'combat_mode',
- )
- combat_mode_element.text = str(subject.combat_mode)
-
- properties_element = etree.SubElement(
- subject_element,
- 'properties',
- )
- encode_properties_map = self._get_encode_properties_map()
-
- for key, value in subject.properties.items():
- item_element = etree.SubElement(properties_element, 'item')
- key_element = etree.SubElement(item_element, 'key')
- value_element = etree.SubElement(item_element, 'value')
-
- key_element.text = str(key)
- value_element.text = encode_properties_map[key](value)
-
- self._state_root_filled = True
-
- def _get_encode_properties_map(self) -> typing.Dict[str, typing.Callable[[typing.Any], str]]: # nopep8:
- return {
- 'SELECTION_COLOR_RGB': lambda v: ','.join(map(str, v)),
- 'FLAG': str,
- 'SIDE': str,
- }
-
-
- class StateLoader(object):
- def __init__(
- self,
- config: Config,
- simulation: TileStrategySimulation,
- ) -> None:
- self._logger = get_logger('StateLoader', config)
- self._config = config
- self._simulation = simulation
-
- schema_file_path = self._config.get(
- 'global.state_schema',
- 'opencombat/state.xsd',
- )
- self._xml_validator = XmlValidator(
- config,
- schema_file_path,
- )
-
- def get_state(
- self,
- state_file_path: str,
- ) -> State:
- return State(
- self._config,
- self._validate_and_return_state_element(state_file_path),
- self._simulation,
- )
-
- def _validate_and_return_state_element(
- self,
- state_file_path: str,
- ) -> Element:
- return self._xml_validator.validate_and_return(state_file_path)
-
-
- class StateConstructorBuilder(object):
- def __init__(
- self,
- config: Config,
- simulation: TileStrategySimulation,
- ) -> None:
- self._logger = get_logger('StateConstructorBuilder', config)
- self._config = config
- self._simulation = simulation
-
- def get_state_loader(
- self,
- ) -> StateLoader:
- class_address = self._config.resolve(
- 'global.state_loader',
- 'opencombat.state.StateLoader',
- )
- state_loader_class = get_class_from_string_path(
- self._config,
- class_address,
- )
- return state_loader_class(
- self._config,
- self._simulation,
- )
-
- def get_state_dumper(
- self,
- ) -> StateDumper:
- class_address = self._config.resolve(
- 'global.state_dumper',
- 'opencombat.state.StateDumper',
- )
- state_loader_class = get_class_from_string_path(
- self._config,
- class_address,
- )
- return state_loader_class(
- self._config,
- self._simulation,
- )
|