From bc6bda2753d786f19eacfb88b893a1759899c20f Mon Sep 17 00:00:00 2001 From: moonbak Date: Sun, 22 Sep 2024 12:27:29 +0200 Subject: [PATCH] Reverting step "remove all ECS implementation(to be replaced by esper things)" This reverts commit 90438b99b6984db9893948a0504c21d919d84a4e. --- src/pyved_engine/__init__.py | 2 +- src/pyved_engine/_ecs.py | 300 ++++++++++++++++++ src/pyved_engine/_ecs_pattern.py | 170 ++++++++++ src/pyved_engine/context_bridge.py | 43 ++- t-workshop/ECSRogue/cartridge/__init__.py | 0 t-workshop/ECSRogue/cartridge/avatar1.png | Bin 0 -> 5222 bytes t-workshop/ECSRogue/cartridge/gamedef.py | 40 +++ t-workshop/ECSRogue/cartridge/metadat.json | 17 + t-workshop/ECSRogue/cartridge/monster.png | Bin 0 -> 876 bytes t-workshop/ECSRogue/cartridge/pimodules.py | 10 + t-workshop/ECSRogue/cartridge/shared.py | 42 +++ t-workshop/ECSRogue/cartridge/systems.py | 276 ++++++++++++++++ t-workshop/ECSRogue/cartridge/tileset.png | Bin 0 -> 88026 bytes t-workshop/ECSRogue/cartridge/world.py | 113 +++++++ t-workshop/ECSRogue/launch_game.py | 98 ++++++ t-workshop/ecs0Breakout/cartridge/__init__.py | 0 t-workshop/ecs0Breakout/cartridge/gamedef.py | 43 +++ .../ecs0Breakout/cartridge/metadat.json | 13 + .../ecs0Breakout/cartridge/pimodules.py | 10 + t-workshop/ecs0Breakout/cartridge/shared.py | 24 ++ t-workshop/ecs0Breakout/cartridge/systems.py | 119 +++++++ t-workshop/ecs0Breakout/cartridge/world.py | 43 +++ t-workshop/ecs0Breakout/launch_game.py | 98 ++++++ t-workshop/ecs1Breakout/cartridge/__init__.py | 0 t-workshop/ecs1Breakout/cartridge/gamedef.py | 42 +++ .../ecs1Breakout/cartridge/metadat.json | 13 + .../ecs1Breakout/cartridge/pimodules.py | 10 + t-workshop/ecs1Breakout/cartridge/shared.py | 24 ++ t-workshop/ecs1Breakout/cartridge/systems.py | 120 +++++++ t-workshop/ecs1Breakout/cartridge/world.py | 43 +++ t-workshop/ecs1Breakout/launch_game.py | 98 ++++++ 31 files changed, 1794 insertions(+), 17 deletions(-) create mode 100644 src/pyved_engine/_ecs.py create mode 100644 src/pyved_engine/_ecs_pattern.py create mode 100644 t-workshop/ECSRogue/cartridge/__init__.py create mode 100644 t-workshop/ECSRogue/cartridge/avatar1.png create mode 100644 t-workshop/ECSRogue/cartridge/gamedef.py create mode 100644 t-workshop/ECSRogue/cartridge/metadat.json create mode 100644 t-workshop/ECSRogue/cartridge/monster.png create mode 100644 t-workshop/ECSRogue/cartridge/pimodules.py create mode 100644 t-workshop/ECSRogue/cartridge/shared.py create mode 100644 t-workshop/ECSRogue/cartridge/systems.py create mode 100644 t-workshop/ECSRogue/cartridge/tileset.png create mode 100644 t-workshop/ECSRogue/cartridge/world.py create mode 100644 t-workshop/ECSRogue/launch_game.py create mode 100644 t-workshop/ecs0Breakout/cartridge/__init__.py create mode 100644 t-workshop/ecs0Breakout/cartridge/gamedef.py create mode 100644 t-workshop/ecs0Breakout/cartridge/metadat.json create mode 100644 t-workshop/ecs0Breakout/cartridge/pimodules.py create mode 100644 t-workshop/ecs0Breakout/cartridge/shared.py create mode 100644 t-workshop/ecs0Breakout/cartridge/systems.py create mode 100644 t-workshop/ecs0Breakout/cartridge/world.py create mode 100644 t-workshop/ecs0Breakout/launch_game.py create mode 100644 t-workshop/ecs1Breakout/cartridge/__init__.py create mode 100644 t-workshop/ecs1Breakout/cartridge/gamedef.py create mode 100644 t-workshop/ecs1Breakout/cartridge/metadat.json create mode 100644 t-workshop/ecs1Breakout/cartridge/pimodules.py create mode 100644 t-workshop/ecs1Breakout/cartridge/shared.py create mode 100644 t-workshop/ecs1Breakout/cartridge/systems.py create mode 100644 t-workshop/ecs1Breakout/cartridge/world.py create mode 100644 t-workshop/ecs1Breakout/launch_game.py diff --git a/src/pyved_engine/__init__.py b/src/pyved_engine/__init__.py index 9e6e558..258b13a 100644 --- a/src/pyved_engine/__init__.py +++ b/src/pyved_engine/__init__.py @@ -28,7 +28,7 @@ # TODO remove this when we can # ive kept it for retro-compatibility with projects that target pyv v23.6a1 # such as demos/ecs_naif or the very early stage pyved ships-with-GUI editor -# from ._ecs_pattern import entity, component, System, SystemManager, EntityManager +from ._ecs_pattern import entity, component, System, SystemManager, EntityManager # useful ALIAS! (webctx) defs = vars diff --git a/src/pyved_engine/_ecs.py b/src/pyved_engine/_ecs.py new file mode 100644 index 0000000..f9cdaf1 --- /dev/null +++ b/src/pyved_engine/_ecs.py @@ -0,0 +1,300 @@ +""" +ECS (Entity-Component-System) is an architectural pattern commonly used in game dev +to structure the organization and behavior of entities within a game. It provides +a flexible and scalable approach to handle complex game systems and interactions. +""" + +# ------------- +# kept for retro-compatibility and not breaking +# the platformer video tutorial +# ------------- +_legacy_components = {} +_legacy_archetypes = {} +_legacy_entities = [] +# _legacy_systems = [] +# ------------- +# new storage +# ------------- +_archetypes = dict() # arche-name to +_archetype_def = dict() # arche-name to +_components = dict() # compo-name to +_free_id = -1 * 0x87aebf7 +_curr_world = 'default' # TODO structure scene by scene +_entities = list() +_systems = list() + + +# def new_entity(): +# entity = {} +# _legacy_entities.append(entity) +# return entity + + +# def delete_entity(entity): +# _legacy_entities.remove(entity) + +def init_entity(entity, values): + entity.update(values) + + +def dissect_entity(given_ent, keys_li): + return [given_ent[k] for k in keys_li] + + +# def new_from_archetype(archetype_name): +# if archetype_name not in _legacy_archetypes: +# raise KeyError(f"ERR: Archetype {archetype_name} is not valid!") +# entity = new_entity() +# for component_name in _legacy_archetypes[archetype_name]: +# add_component(entity, component_name, None) +# add_component(entity, 'Archetype', archetype_name, bypass=True) +# return entity + + +# fix +def new_from_archetype(archetype_name): + eo = new_entity() + eo.set_archetype(archetype_name) + return eo + + +# def define_archetype(archetype_name, component_names): +# _legacy_archetypes[archetype_name] = component_names +# +def define_archetype(archetype_name, component_names): + _archetype_def[archetype_name] = component_names + _archetypes[archetype_name] = list() + + +# def has_archetype(entity, archetype_name): +# if 'Archetype' not in entity: +# return False +# return entity['Archetype'] == archetype_name +# +# +# def find_by_archetype(archetype_name): +# return list(filter(lambda e: has_archetype(e, archetype_name), _legacy_entities)) +# +# +# def list_all_archetypes(): +# return tuple(_legacy_archetypes.keys()) + +def has_archetype(entity, archetype_name): + return entity.archetype == archetype_name + + +def find_by_archetype(archetype_name): + li_e_id = _archetypes[archetype_name] + return list(map(lambda x: _EntityCls.globalmapping[x], li_e_id)) + + +def list_all_archetypes(): + return tuple(_archetypes.keys()) + + +def archetype_of(entity_ref): # we simply extract the archetype + return entity_ref.archetype + + +# def archetype_of(entity_ref): +# if 'Archetype' not in entity_ref: +# return None +# return entity_ref['Archetype'] + + +# -------------------------------- +# new API for using ecs +# -------------------------------- +class _EntityCls: + globalmapping = dict() + + def __init__(self, eid, li_components=None): + self._id = eid + self._archetype = None + self.__class__.globalmapping[eid] = self + self.icomponents = dict() + self._compo_order = list() + if li_components: + for componame in li_components: + self.add_component(componame) + + @property + def id(self): + return self._id + + @property + def components(self): + return list(self._compo_order) + + def has_component(self, componame): + return self._id in _components[componame] + + @property + def archetype(self): + return self._archetype + + def set_archetype(self, a_name): + if a_name not in _archetypes.keys(): + raise ValueError(f'Archetype {a_name} not declared') + if self._archetype: + raise NotImplementedError(f'Cannot set new archetype, entity is already archetype {self._archetype}') + self._archetype = a_name + _archetypes[a_name].append(self._id) + if len(self.icomponents): + print('* warning setting archetype to an entity that alreay has components ->wipe out existing data *') + self.icomponents.clear() + _exp_components = _archetype_def[a_name] + for c in _exp_components: + self.add_component(c) + + def add_component(self, key, val=None): + if key in self.icomponents: + raise ValueError(f'Compo {key} already exist on entity, cannot add again!') + self.icomponents[key] = val + self._compo_order.append(key) + + if key not in _components: + _components[key] = list() + _components[key].append(self._id) + + def remove_component(self, key): + raise NotImplementedError + + def update(self, dico): + for elt in dico.keys(): + if elt not in self.icomponents.keys(): + raise ValueError(f'Compo found in dictionary ({elt})does not exist in the entity') + for k, v in dico.items(): + self.icomponents[k] = v + + def __getattr__(self, item): + return self.icomponents[item] + + def __getitem__(self, k): # forward to dict, unless its an int + if isinstance(k, int): + return self._compo_order[k] + else: + return self.icomponents.__getitem__(k) + + def __setitem__(self, key, value): + self.icomponents[key] = value + + +# Entity functions ----- +def new_entity(archetype=None, **kwargs): + global _free_id + e_id = _free_id + _free_id += 1 + res = _EntityCls(e_id) + if archetype: + if archetype in _archetypes.keys(): + res.set_archetype(archetype) + if len(kwargs) > 0: + res.update(kwargs) + else: + raise ValueError(f'Specified archetype {archetype} is unknown!') + _entities.append(res) + return res + + +def delete_entity(entity): + eid = entity.id + if entity.archetype is not None: + _archetypes[entity.archetype].remove(eid) + for cname in entity.components: + _components[cname].remove(eid) + _entities.remove(entity) + + +# def wipe_entities(): +# del _legacy_entities[:] + +def wipe_entities(): + for cn, li_entities in _components.items(): + del li_entities[:] + for a, li_eid in _archetypes.items(): + del li_eid[:] + + +# def add_component(entity, component_name, data, bypass=False): +# if not bypass and component_name == 'Archetype': +# raise ValueError('ERR: Cannot declare a component named "Archetype"!') +# component = {component_name: data} +# entity.update(component) +# if component_name not in _legacy_components: +# _legacy_components[component_name] = [] +# _legacy_components[component_name].append(entity) + +def add_component(entity, component_name, data, bypass=False): + if not bypass and component_name == 'Archetype': + raise ValueError('ERR: Cannot declare a component named "Archetype"!') + entity.add_component(component_name, data) + + +# def remove_component(entity, component_name): +# if component_name in entity: +# entity.pop(component_name) +# if component_name in _legacy_components: +# _legacy_components[component_name].remove(entity) + +def remove_component(entity, component_name): + if component_name in entity: + entity.pop(component_name) + if component_name in _components: + _components[component_name].remove(entity) + + +def find_by_components(*compokeys): + """ + this func will return all entities that satisfy each on of compokeys (=has that list of components in it) + :param compokeys: + :return: a list + """ + res = list() + for entity in _entities: + # print('curr entity:', entity) + compat = True + for c in compokeys: + if not entity.has_component(c): + # below line = faster, used to break things in web Ctx + # if c not in entity: + compat = False + break + if compat: + res.append(entity) + return res + + +def all_entities(scene=None): + return iter(_entities) # TODO fetch from a given scene ... + + +def one_by_archetype(archetype_name): + if (archetype_name not in _archetypes) or (not len(_archetypes[archetype_name])): + raise ValueError(f'archetype named {archetype_name} not found!') + adhoc_eid = _archetypes[archetype_name][0] + return _EntityCls.globalmapping[adhoc_eid] + + +def add_system(system_func): + _systems.append(system_func) + + +def remove_system(system_func): + _systems.remove(system_func) + + +def bulk_add_systems(module): + # hacky but very convenient procedure to add all systems directly from a module + # :param module: Python module that contains all your game-specific 'system_func' items + names = module.__all__ if hasattr(module, '__all__') else dir(module) + bulk = [name for name in names if not name.startswith('_')] + for syst_name in bulk: + add_system(getattr(module, syst_name)) + + +def systems_proc(*args): + if not len(_systems): + raise ValueError('[PYV/ecs] systems_proc called, but no systems has been added!') + for system_func in _systems: + system_func(*args) diff --git a/src/pyved_engine/_ecs_pattern.py b/src/pyved_engine/_ecs_pattern.py new file mode 100644 index 0000000..f2e1e8b --- /dev/null +++ b/src/pyved_engine/_ecs_pattern.py @@ -0,0 +1,170 @@ +""" +ECS - Entity Component system +heavily inspired by other projects authored by: + - Vladimir Kaukin [KaukinVK@ya.ru] + - Rik Cross [rik@raspberrypi.org] +""" +# from typing import Iterable, Iterator, Any +import dataclasses +import collections +import functools + + +# any entity class must be decorated with this function +entity = functools.partial(dataclasses.dataclass, slots=True) + +# any component class must be decorated with this function +component = dataclasses.dataclass + + +class EntityManager: + """Entity manager""" + + def __init__(self): + self._entity_map = {} # Person: [ent1, ent2] + self._entity_components_map = {} # Person: (MoveCom, DamageCom, NameCom) + self._set_cache_map = {} # (MoveCom, DamageCom, NameCom): {MoveCom, DamageCom, NameCom} + self._delete_entity_buffer = collections.deque() # deque([Person1, Person2]) + + def add(self, *entity_value_list): + """Add entities to world""" + for entity_value in entity_value_list: + assert getattr(entity_value, '__dict__', None) in (None, {}), 'Data class with inefficient memory usage' + entity_value_class = entity_value.__class__ + self._entity_map.setdefault(entity_value_class, []).append(entity_value) + if entity_value_class not in self._entity_components_map: + self._entity_components_map[entity_value_class] = tuple(sorted( + (i for i in entity_value_class.__mro__ if i is not object), + key=lambda x: x.__class__.__name__ + )) + + def delete(self, *entity_value_list): + """Delete entities from world""" + for entity_value in entity_value_list: + self._entity_map[entity_value.__class__].remove(entity_value) + + def delete_buffer_add(self, *entity_value_list): + """Save entities into delete buffer for delete them from world later""" + for entity_value in entity_value_list: + self._delete_entity_buffer.append(entity_value) + + def delete_buffer_purge(self): + """Delete all entities from delete buffer""" + for delete_entity in self._delete_entity_buffer: + self.delete(delete_entity) + self._delete_entity_buffer.clear() + + def init(self, *entity_list): + """ + Let entity manager to "know" about entities before work + If manager do not know about entity, it will raise KeyError on access to it. + event: SomeEvent = next(self.entities.get_by_class(SomeEvent), None) + """ + for ent in entity_list: + self.add(ent) + self.delete(ent) + + def get_by_class(self, *entity_class_val_list: type): + """ + Get all entities by specified entity class in specified order + raise KeyError for uninitialized (never added) entities + type returned is + -> Iterator[Any] + """ + for entity_class_val in entity_class_val_list: + yield from self._entity_map[entity_class_val] + + def get_with_component(self, *component_class_val_list: type): + """ + Get all entities that contains all specified component classes + Sometimes it will be useful to warm up the cache + raise KeyError for uninitialized (never added) entities + type returned is + -> Iterator[Any] + """ + for entity_class, entity_component_list in self._entity_components_map.items(): + entity_component_set = \ + self._set_cache_map.setdefault(entity_component_list, set(entity_component_list)) + component_class_val_set = \ + self._set_cache_map.setdefault(component_class_val_list, set(component_class_val_list)) + if component_class_val_set.issubset(entity_component_set): + yield from self._entity_map[entity_class] + + +class System: + """ + Abstract base class for system + All systems must be derived from this class + System should have data for work: implement __init__ method + """ + + def initialize(self): + """ + Preparing system to work before starting SystemManager.systems_update loop + Runs by SystemManager.start_systems + """ + + def proc(self): + """ + Run main system logic + Runs by SystemManager.update_systems + """ + + def cleanup(self): + """ + Clean system resources after stop update loop + Runs by SystemManager.stop_systems + """ + + +class SystemManager: + """ + System manager + """ + + def __init__(self): + """ + :param system_list: an ordered sequence with systems, expected type is Iterable[System] + such that + for each pair of elements elt_i, elt_j we have classname(elt_i) != classname(elt_j) + """ + self._system_list = None + self._name_to_sys = dict() + + self._system_with_start_list = None + self._system_with_update_list = None + self._system_with_stop_list = None + + def declare_systems(self, system_list): + self._system_list = tuple(system_list) + + self._name_to_sys.clear() + + for e in self._system_list: + cls_name = e.__class__.__name__ + if cls_name in self._name_to_sys: + raise Exception('ERR: Duplicate name for system detected!', cls_name) + self._name_to_sys[cls_name] = e + + self._system_with_start_list = tuple(e for e in self._system_list if hasattr(e, 'initialize')) + self._system_with_update_list = tuple(e for e in self._system_list if hasattr(e, 'proc')) + self._system_with_stop_list = tuple(e for e in self._system_list if hasattr(e, 'cleanup')) + + # get by system name + def __getitem__(self, item): + return self._name_to_sys[item] + + def init_all(self): + """Start all systems""" + for system in self._system_with_start_list: + system.initialize() + + def proc_all(self): + """Update all systems""" + for system in self._system_with_update_list: + system.proc() + + def cleanup_all(self): + """Stop all systems""" + for system in self._system_with_stop_list: + system.cleanup() diff --git a/src/pyved_engine/context_bridge.py b/src/pyved_engine/context_bridge.py index 45422ae..8dc19d6 100644 --- a/src/pyved_engine/context_bridge.py +++ b/src/pyved_engine/context_bridge.py @@ -7,38 +7,44 @@ from .api import curr_state, draw_polygon, draw_line, declare_game_states, enum, enum_from_n, game_events_enum,\ get_ev_manager, get_surface, get_pressed_keys, close_game, bootstrap_e, declare_begin, declare_update, declare_end,\ preload_assets, run_game, init, draw_rect, draw_circle, flip, get_gs_obj -# from ._ecs import all_entities, find_by_components, find_by_archetype, archetype_of, define_archetype, init_entity,\ -# new_from_archetype, bulk_add_systems, systems_proc, delete_entity +from ._ecs import all_entities, find_by_components, find_by_archetype, archetype_of, define_archetype, init_entity,\ + new_from_archetype, bulk_add_systems, systems_proc, delete_entity # - define the full engine API for end users! __all__ = [ # - API of the Legacy ECS implementation - # this has been superseded by the usage of "esper" as a game bundle dependency - # for modern projects - # We keep this info as long as required so old game demos(roguelike, Breakout, etc) - # can be rewritten using "esper" + # this may be superseded soon by the usage of "esper" as a game bundle dependency + # for modern projects + # But we keep this info as long as required so old game demos(roguelike, Breakout, etc) + # need to be functional - # 'all_entities', - # 'archetype_of', - # 'bulk_add_systems', - # 'define_archetype', - # 'delete_entity', - # 'find_by_archetype', - # 'find_by_components', - # 'init_entity', - # 'new_from_archetype', - # 'systems_proc' + # legacy part of the api + 'all_entities', + 'archetype_of', + 'bulk_add_systems', + 'define_archetype', + 'delete_entity', + 'find_by_archetype', + 'find_by_components', + 'init_entity', + 'new_from_archetype', + 'systems_proc', + + # other 'get_gs_obj', 'bootstrap_e', + 'bulk_add_systems', 'close_game', 'curr_state', 'declare_begin', 'declare_end', 'declare_game_states', 'declare_update', + 'define_archetype', + 'delete_entity', 'draw_circle', 'draw_line', 'draw_polygon', @@ -47,16 +53,21 @@ 'engine_init', 'enum', 'enum_from_n', + 'find_by_archetype', + 'find_by_components', 'flip', 'game_events_enum', 'get_ev_manager', 'get_pressed_keys', 'get_surface', 'init', + 'init_entity', 'machin', 'my_enum', + 'new_from_archetype', 'preload_assets', 'run_game', + 'systems_proc' ] diff --git a/t-workshop/ECSRogue/cartridge/__init__.py b/t-workshop/ECSRogue/cartridge/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/t-workshop/ECSRogue/cartridge/avatar1.png b/t-workshop/ECSRogue/cartridge/avatar1.png new file mode 100644 index 0000000000000000000000000000000000000000..4c53353281677ba735fa6435323744f7c3dbd661 GIT binary patch literal 5222 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 literal 0 HcmV?d00001 diff --git a/t-workshop/ECSRogue/cartridge/gamedef.py b/t-workshop/ECSRogue/cartridge/gamedef.py new file mode 100644 index 0000000..2d3e493 --- /dev/null +++ b/t-workshop/ECSRogue/cartridge/gamedef.py @@ -0,0 +1,40 @@ +from . import pimodules +from . import shared +from . import systems +from . import world + + +pyv = pimodules.pyved_engine + + +@pyv.declare_begin +def init_game(vmst=None): + pyv.init(wcaption='Roguata') + screen = pyv.get_surface() + shared.screen = screen + pyv.define_archetype('player', ( + 'position', 'controls', 'body', 'damages', 'health_point', 'enter_new_map' + )) + pyv.define_archetype('wall', ('body',)) + pyv.define_archetype('monster', ('position', 'damages', 'health_point', 'active')) + pyv.define_archetype('exit', ('position',)) + pyv.define_archetype('potion', ('position', 'effect',)) + + world.create_player() + # world.create_wall() + world.create_exit() + world.create_potion() + world.init_images() + pyv.bulk_add_systems(systems) + + +@pyv.declare_update +def upd(time_info=None): + pyv.systems_proc() + pyv.flip() + + +@pyv.declare_end +def done(vmst=None): + pyv.close_game() + print('gameover!') diff --git a/t-workshop/ECSRogue/cartridge/metadat.json b/t-workshop/ECSRogue/cartridge/metadat.json new file mode 100644 index 0000000..800dbf7 --- /dev/null +++ b/t-workshop/ECSRogue/cartridge/metadat.json @@ -0,0 +1,17 @@ +{ + "vmlib_ver": "23_9a2", + "dependencies": { + "pyved_engine": "???" + }, + "description": "this is a placeholder so you can describe your game", + "author": "Unknown", + "assets": [ + "tileset.png", + "monster.png", + "avatar1.png" + ], + "sounds": [], + "cartridge": "ecsRogue", + "game_title": "ecsRogue", + "build_date": "Wed Sep 20 15:55:17 2023" +} \ No newline at end of file diff --git a/t-workshop/ECSRogue/cartridge/monster.png b/t-workshop/ECSRogue/cartridge/monster.png new file mode 100644 index 0000000000000000000000000000000000000000..6af4ae94f517971f3415b5475a3b2c3c19d29d01 GIT binary patch literal 876 zcmV-y1C#uTP)EX>4Tx04R}tkv&MmKpe$iTeTvU4pxxjkfAzR5G~>;RV;#q(pG5I!Q|2}Xws0R zxHt-~1qVMCs}3&Cx;nTDg5U>;qmz@Oir6bq^0;@1i`*{oJ2pP%an@@Cd|nOw&!`4dR(i zQ{%i(9AafrCO#*g(CLE2k6f1=e&bxS*~>FSdNw^z93mD1~ z4$GXkI4jjUYu}Teq!>uke%!?$w*4t`N#v@4 zkz)ZhC=hKw_#gc4)+kO-IZ1&y(EDOpA7g-j7icvs>-*TUS|@<-8MsnA{#p~5{Up87 z(IQ7ca2vR|?r72;aJd7FJZYjX+LDi^w^#(;&*+=-z|bu)u;%pE-pAn`u^@9yp2GwuF<0J|J=#NgX3qyPW_24YJ`L;wH){{R3-+mDg}000SaNLh0L z01FcU01FcV0GgZ_00007bV*G`2j&J36*U^3RPCMs000?uMObu0Z*6U5Zgc=ca%Ew3 zWn>_CX>@2HM@dakSAh-}0003)NkluqEy_#Ek>4X(IQt=R8YkQ z1uX}FCJjmjSI8ACxiA%}IDnPjNKqV#Dwjwz6#K)j9ZMN$WO;Vpn>RDNN)VxEs!;t9 zQV-H}Zl6FE3JM@Mfd;6@NhSd}HI;KOn4<}|Ck2EAV3Hf}-zrFS0awJ>^dxO znL;5yrX4}VIw?YdkRsJ;A(RNe#!Z|Q2`~Y;)Sl!sj zo*mp){4IntNDR{!q=k>yIn{atpa}rJOwNN=GGWA?Z!Z|NX5=mS{b$FB<;xQQyKc|& z`!gKAX<^b2+Jkm>VAt&dTpRX0Smntz+DmoDMfw9NO#Lc^9zm!80000