From 8dcd7725a9edaab3137c0fbcb3c9390af9af275f Mon Sep 17 00:00:00 2001 From: Rendo Date: Thu, 16 Oct 2025 19:47:52 +0500 Subject: [PATCH] rotation rework, deletion of complex objects --- generic/prototypes/dbg_assembler.tres | 2 +- scenes/conveyor.tscn | 88 +++++------------- scenes/debug_assembler.tscn | 25 ++++- scripts/globals.gd | 3 - scripts/grid_controller.gd | 9 +- scripts/placement_manager.gd | 8 +- scripts/structure.gd | 53 ++++------- scripts/structure_behaviour.gd | 2 + scripts/structures/assembler.gd | 6 +- scripts/structures/belt.gd | 36 +------ scripts/structures/belt_animation_sync.gd | 4 + scripts/structures/belt_animation_sync.gd.uid | 1 + scripts/structures/directional_sprite.gd | 12 +++ scripts/structures/directional_sprite.gd.uid | 1 + sprites/atlasses/Popekko.png | Bin 2265 -> 2321 bytes 15 files changed, 103 insertions(+), 147 deletions(-) create mode 100644 scripts/structures/belt_animation_sync.gd create mode 100644 scripts/structures/belt_animation_sync.gd.uid create mode 100644 scripts/structures/directional_sprite.gd create mode 100644 scripts/structures/directional_sprite.gd.uid diff --git a/generic/prototypes/dbg_assembler.tres b/generic/prototypes/dbg_assembler.tres index 072757f..69eeec3 100644 --- a/generic/prototypes/dbg_assembler.tres +++ b/generic/prototypes/dbg_assembler.tres @@ -6,7 +6,7 @@ [sub_resource type="AtlasTexture" id="AtlasTexture_kf3x0"] atlas = ExtResource("1_sh8t1") -region = Rect2(32, 0, 32, 32) +region = Rect2(48, 0, 32, 32) [resource] script = ExtResource("1_mqcr0") diff --git a/scenes/conveyor.tscn b/scenes/conveyor.tscn index dddd070..11b7f5b 100644 --- a/scenes/conveyor.tscn +++ b/scenes/conveyor.tscn @@ -1,10 +1,12 @@ -[gd_scene load_steps=14 format=3 uid="uid://b0h8dd82b3ox5"] +[gd_scene load_steps=16 format=3 uid="uid://b0h8dd82b3ox5"] [ext_resource type="Texture2D" uid="uid://gfkhedfdi7ug" path="res://sprites/atlasses/Popekko.png" id="1_kqxj7"] [ext_resource type="Script" uid="uid://bbd7o2st8kmgl" path="res://scripts/structure.gd" id="1_y326v"] [ext_resource type="Script" uid="uid://bd4ojfqrl8idm" path="res://scripts/inventory/inventory_slot.gd" id="2_54w8r"] [ext_resource type="Script" uid="uid://v0hkuo3gda1b" path="res://scripts/inventory/belt_inventory.gd" id="3_ruvuk"] [ext_resource type="Script" uid="uid://bp341eiwvfvyl" path="res://scripts/structures/belt.gd" id="5_54w8r"] +[ext_resource type="Script" uid="uid://lchhqigib2t0" path="res://scripts/structures/directional_sprite.gd" id="5_ruvuk"] +[ext_resource type="Script" uid="uid://drsty3i1820ha" path="res://scripts/structures/belt_animation_sync.gd" id="7_t4je2"] [sub_resource type="Resource" id="Resource_t4je2"] resource_local_to_scene = true @@ -21,7 +23,19 @@ metadata/_custom_type_script = "uid://v0hkuo3gda1b" [sub_resource type="AtlasTexture" id="AtlasTexture_54w8r"] atlas = ExtResource("1_kqxj7") -region = Rect2(16, 0, 16, 128) +region = Rect2(16, 0, 16, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_crbfm"] +atlas = ExtResource("1_kqxj7") +region = Rect2(32, 64, 16, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_t4je2"] +atlas = ExtResource("1_kqxj7") +region = Rect2(32, 0, 16, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ruvuk"] +atlas = ExtResource("1_kqxj7") +region = Rect2(16, 64, 16, 64) [sub_resource type="Animation" id="Animation_ruvuk"] length = 0.001 @@ -38,41 +52,6 @@ tracks/0/keys = { "values": [0] } -[sub_resource type="Animation" id="Animation_1rfp0"] -resource_name = "down" -loop_mode = 1 -step = 0.25 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Sprite2D:frame") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.25, 0.5, 0.75), -"transitions": PackedFloat32Array(1, 1, 1, 1), -"update": 1, -"values": [7, 6, 5, 4] -} - -[sub_resource type="Animation" id="Animation_t4je2"] -resource_name = "left" -length = 1.000025 -loop_mode = 1 -step = 0.25 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Sprite2D:frame") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.25, 0.5, 0.75), -"transitions": PackedFloat32Array(1, 1, 1, 1), -"update": 1, -"values": [3, 2, 1, 0] -} - [sub_resource type="Animation" id="Animation_54w8r"] resource_name = "right" length = 1.000025 @@ -91,40 +70,24 @@ tracks/0/keys = { "values": [0, 1, 2, 3] } -[sub_resource type="Animation" id="Animation_crbfm"] -resource_name = "up" -loop_mode = 1 -step = 0.25 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Sprite2D:frame") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.25, 0.5, 0.75), -"transitions": PackedFloat32Array(1, 1, 1, 1), -"update": 1, -"values": [4, 5, 6, 7] -} - [sub_resource type="AnimationLibrary" id="AnimationLibrary_crbfm"] _data = { &"RESET": SubResource("Animation_ruvuk"), -&"down": SubResource("Animation_1rfp0"), -&"left": SubResource("Animation_t4je2"), -&"right": SubResource("Animation_54w8r"), -&"up": SubResource("Animation_crbfm") +&"right": SubResource("Animation_54w8r") } [node name="Conveyor" type="Node2D" groups=["buildings"]] script = ExtResource("1_y326v") inventory = SubResource("Resource_t4je2") -facing = 1 +direction = null +maximum_directions = 4 -[node name="Sprite2D" type="Sprite2D" parent="."] +[node name="Sprite2D" type="Sprite2D" parent="." node_paths=PackedStringArray("structure")] texture = SubResource("AtlasTexture_54w8r") -vframes = 8 +vframes = 4 +script = ExtResource("5_ruvuk") +textures = Array[Texture]([SubResource("AtlasTexture_54w8r"), SubResource("AtlasTexture_crbfm"), SubResource("AtlasTexture_t4je2"), SubResource("AtlasTexture_ruvuk")]) +structure = NodePath("..") [node name="Conveyor" type="Node2D" parent="."] z_index = 1 @@ -135,5 +98,4 @@ libraries = { &"": SubResource("AnimationLibrary_crbfm") } autoplay = "right" - -[connection signal="switched_facing" from="." to="Conveyor" method="_on_conveyor_switched_facing"] +script = ExtResource("7_t4je2") diff --git a/scenes/debug_assembler.tscn b/scenes/debug_assembler.tscn index 0da2137..2e4cd68 100644 --- a/scenes/debug_assembler.tscn +++ b/scenes/debug_assembler.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=9 format=3 uid="uid://dfatkxv6n55dw"] +[gd_scene load_steps=13 format=3 uid="uid://dfatkxv6n55dw"] [ext_resource type="Script" uid="uid://bbd7o2st8kmgl" path="res://scripts/structure.gd" id="1_k5y3y"] [ext_resource type="Texture2D" uid="uid://gfkhedfdi7ug" path="res://sprites/atlasses/Popekko.png" id="2_4befw"] [ext_resource type="Script" uid="uid://bd4ojfqrl8idm" path="res://scripts/inventory/inventory_slot.gd" id="2_p1cyh"] [ext_resource type="Script" uid="uid://m6b6vawdqgqb" path="res://scripts/inventory/in_out_inventory.gd" id="3_wqoim"] [ext_resource type="Script" uid="uid://c7mx3uatj6ulm" path="res://scripts/structures/assembler.gd" id="5_0x00x"] +[ext_resource type="Script" uid="uid://lchhqigib2t0" path="res://scripts/structures/directional_sprite.gd" id="5_kno0u"] [ext_resource type="Resource" uid="uid://d2lbc1qqkayaa" path="res://generic/recipes/test_recipe.tres" id="6_wqoim"] [sub_resource type="Resource" id="Resource_kno0u"] @@ -21,17 +22,33 @@ metadata/_custom_type_script = "uid://m6b6vawdqgqb" [sub_resource type="AtlasTexture" id="AtlasTexture_xh4eg"] atlas = ExtResource("2_4befw") -region = Rect2(32, 0, 32, 32) +region = Rect2(48, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_0x00x"] +atlas = ExtResource("2_4befw") +region = Rect2(48, 32, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_dr8in"] +atlas = ExtResource("2_4befw") +region = Rect2(48, 96, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_kno0u"] +atlas = ExtResource("2_4befw") +region = Rect2(48, 64, 32, 32) [node name="DebugAssembler" type="Node2D"] script = ExtResource("1_k5y3y") dimensions = Rect2i(0, 0, 2, 2) inventory = SubResource("Resource_kno0u") -facing = 2 +direction = 1.571 +maximum_directions = 4 -[node name="Sprite2D" type="Sprite2D" parent="."] +[node name="Sprite2D" type="Sprite2D" parent="." node_paths=PackedStringArray("structure")] texture = SubResource("AtlasTexture_xh4eg") offset = Vector2(8, 8) +script = ExtResource("5_kno0u") +textures = Array[Texture]([SubResource("AtlasTexture_0x00x"), SubResource("AtlasTexture_xh4eg"), SubResource("AtlasTexture_dr8in"), SubResource("AtlasTexture_kno0u")]) +structure = NodePath("..") [node name="Assembler" type="Node2D" parent="."] position = Vector2(16, 16) diff --git a/scripts/globals.gd b/scripts/globals.gd index 2ca0efa..1706fcb 100644 --- a/scripts/globals.gd +++ b/scripts/globals.gd @@ -11,6 +11,3 @@ enum Sides { LEFT, UP } - -static func facing_difference(from : Structure.Facing, to : Structure.Facing) -> float: - return Structure.facing_to_vector(from).angle_to(Structure.facing_to_vector(to)) diff --git a/scripts/grid_controller.gd b/scripts/grid_controller.gd index dc927d0..ccfee4e 100644 --- a/scripts/grid_controller.gd +++ b/scripts/grid_controller.gd @@ -42,8 +42,13 @@ func destroy_at(point : Vector2) -> void: var index = building_zone.indexify_global_point(point) if index == -1: return - structures[index].queue_free() - structures[index] = null + + var found: Structure = structures[index] + + for dim_point in found.get_dimension_points(): + structures[building_zone.indexify_global_point(found.global_position+dim_point)] = null + + found.queue_free() func is_point_occupied(point : Vector2) -> bool: return get_at(point) != null diff --git a/scripts/placement_manager.gd b/scripts/placement_manager.gd index 705ebab..81a8e62 100644 --- a/scripts/placement_manager.gd +++ b/scripts/placement_manager.gd @@ -30,18 +30,18 @@ func _input(event: InputEvent) -> void: if event.is_action_pressed("plc_place"): var zone = try_get_zone(held_construction.global_position) if zone != null and held_construction.try_place(zone): - var facing = held_construction.facing + var direction = held_construction.direction held_construction = selected_prototype.scene.instantiate() add_child(held_construction) - held_construction.set_facing(facing) + held_construction.set_direction(direction) if event.is_action_pressed("plc_rotate_up"): if held_construction != null: - held_construction.cycle_up_facing() + held_construction.increment_direction() if event.is_action_pressed("plc_rotate_down"): if held_construction != null: - held_construction.cycle_down_facing() + held_construction.decrement_direction() if event.is_action_pressed("plc_cancel"): held_construction.queue_free() diff --git a/scripts/structure.gd b/scripts/structure.gd index e512e68..e21d788 100644 --- a/scripts/structure.gd +++ b/scripts/structure.gd @@ -4,15 +4,7 @@ extends Node2D ## Game object that interact with other structures in its grid space class_name Structure -enum Facing { - UNDIRECTIONAL = 0, - RIGHT = 1, - DOWN = 2, - LEFT = 3, - UP = 4 -} - -signal switched_facing(to: Facing) +signal changed_direction(to: float,max_directions : int) ## Dimensions of structure in grid tiles @export var dimensions : Rect2i = Rect2i(0,0,1,1): @@ -25,20 +17,8 @@ signal switched_facing(to: Facing) ## Inventory of this structure @export var inventory : Inventory -@export var facing : Facing - -static func facing_to_vector(face : Facing) -> Vector2: - match face: - Facing.RIGHT: - return Vector2.RIGHT - Facing.LEFT: - return Vector2.LEFT - Facing.UP: - return Vector2.UP - Facing.DOWN: - return Vector2.DOWN - _: - return Vector2.ZERO +@export var direction : float +@export var maximum_directions : int ## Debug draw of points func _draw() -> void: @@ -83,16 +63,21 @@ func get_dimension_points() -> Array[Vector2]: result[x + y * dimensions.size.x] = (Vector2(x,y)*Globals.GRID_SIZE + Vector2(dimensions.position)) return result -func set_facing(to : Facing) -> void: - facing = to - switched_facing.emit(facing) +func set_direction(to : float) -> void: + direction = to + changed_direction.emit(direction,maximum_directions) -func cycle_up_facing() -> void: - if facing == Facing.UNDIRECTIONAL: - return - set_facing(wrapi(facing+1,1,5)) +func increment_direction() -> void: + set_direction(wrapf(direction + TAU/maximum_directions,0,TAU)) -func cycle_down_facing() -> void: - if facing == Facing.UNDIRECTIONAL: - return - set_facing(wrapi(facing-1,1,5)) +func decrement_direction() -> void: + set_direction(wrapf(direction - TAU/maximum_directions,0,TAU)) + +func direction_vector() -> Vector2: + return Vector2.from_angle(direction) + +func direction_to(structure: Structure): + return (structure.global_position-global_position).angle() + +func direction_difference(structure: Structure): + return direction-structure.direction diff --git a/scripts/structure_behaviour.gd b/scripts/structure_behaviour.gd index 6341888..d8736de 100644 --- a/scripts/structure_behaviour.gd +++ b/scripts/structure_behaviour.gd @@ -1,5 +1,7 @@ +@abstract extends Node2D class_name StructureBehaviour @onready var structure_parent : Structure = get_parent() +@onready var inventory : Inventory = structure_parent.inventory diff --git a/scripts/structures/assembler.gd b/scripts/structures/assembler.gd index bb25f04..3bbd48f 100644 --- a/scripts/structures/assembler.gd +++ b/scripts/structures/assembler.gd @@ -1,7 +1,6 @@ extends StructureBehaviour @export var selected_recipe : Recipe -@onready var inventory : InOutInventory = get_parent().inventory func _ready() -> void: inventory.stack_added.connect(check_for_recipe) @@ -23,13 +22,12 @@ func _process(_delta: float) -> void: var output : Structure = get_output_structure() if output == null: return - var ang_diff = Globals.facing_difference(output.facing,structure_parent.facing) + var ang_diff = output.direction_difference(structure_parent) if output.inventory.can_add_from_side(ang_diff): inventory.output_slot.merge_stack(output.inventory.add_from_side(inventory.output_slot.extract(),ang_diff)) func get_output_structure() -> Structure: - var facing_direction = Structure.facing_to_vector(structure_parent.facing) - var rotated = Vector2(0.5,1.5).rotated(-Vector2.DOWN.angle_to(facing_direction)) + var rotated = Vector2(1.5,-0.5).rotated(structure_parent.direction) return structure_parent.get_relative(rotated+Vector2(0.5,0.5)) func check_for_recipe(_stack : Stack, _position : int) -> void: diff --git a/scripts/structures/belt.gd b/scripts/structures/belt.gd index 0d5b32f..7c0c2ba 100644 --- a/scripts/structures/belt.gd +++ b/scripts/structures/belt.gd @@ -1,14 +1,7 @@ extends StructureBehaviour -@onready var inventory : BeltInventory = structure_parent.inventory @onready var animator : AnimationPlayer = $"../AnimationPlayer" -func _ready() -> void: - sync_animations.call_deferred() - -func sync_animations() -> void: - animator.seek(BeltManager.sync_time,true) - func _draw() -> void: for i in range(inventory.capacity): if inventory.internal_array[i].amount > 0: @@ -23,41 +16,20 @@ func _process(delta: float) -> void: func calculate_position(index: int) -> Vector2: var indexed_part = 16.0 / inventory.capacity - match structure_parent.facing: - Structure.Facing.RIGHT: - return Vector2.LEFT*8 + Vector2.RIGHT * indexed_part * (index + inventory.progress_array[index]) - Structure.Facing.DOWN: - return Vector2.UP*8 + Vector2.DOWN * indexed_part * (index + inventory.progress_array[index]) - Structure.Facing.LEFT: - return Vector2.RIGHT*8 + Vector2.LEFT * indexed_part * (index + inventory.progress_array[index]) - Structure.Facing.UP: - return Vector2.DOWN*8 + Vector2.UP * indexed_part * (index + inventory.progress_array[index]) - return Vector2.ZERO + + return -structure_parent.direction_vector()*8 + structure_parent.direction_vector() * indexed_part * (index + inventory.progress_array[index]) func get_next() -> Structure: - var faced_vector = Structure.facing_to_vector(structure_parent.facing) - return structure_parent.get_relative(faced_vector) + return structure_parent.get_relative(structure_parent.direction_vector()) func try_transfer(structure : Structure) -> void: if structure == null or inventory.internal_array[inventory.capacity-1].amount == 0: return var last_slot = inventory.internal_array[inventory.capacity-1] - var ang_diff = Globals.facing_difference(structure_parent.facing,structure.facing) + var ang_diff = structure_parent.direction_difference(structure) if structure.inventory.can_add_from_side(ang_diff,last_slot.held_item) == false: return last_slot.merge_stack(structure.inventory.add_from_side(last_slot.extract(),ang_diff)) if last_slot.amount == 0: inventory.sort() - -func _on_conveyor_switched_facing(to: Structure.Facing) -> void: - match to: - Structure.Facing.RIGHT: - animator.play("right") - Structure.Facing.DOWN: - animator.play("down") - Structure.Facing.LEFT: - animator.play("left") - Structure.Facing.UP: - animator.play("up") - sync_animations() diff --git a/scripts/structures/belt_animation_sync.gd b/scripts/structures/belt_animation_sync.gd new file mode 100644 index 0000000..8e5c11b --- /dev/null +++ b/scripts/structures/belt_animation_sync.gd @@ -0,0 +1,4 @@ +extends AnimationPlayer + +func _ready() -> void: + seek.call_deferred(BeltManager.sync_time, true) diff --git a/scripts/structures/belt_animation_sync.gd.uid b/scripts/structures/belt_animation_sync.gd.uid new file mode 100644 index 0000000..ac8565e --- /dev/null +++ b/scripts/structures/belt_animation_sync.gd.uid @@ -0,0 +1 @@ +uid://drsty3i1820ha diff --git a/scripts/structures/directional_sprite.gd b/scripts/structures/directional_sprite.gd new file mode 100644 index 0000000..d3a6c00 --- /dev/null +++ b/scripts/structures/directional_sprite.gd @@ -0,0 +1,12 @@ +extends Sprite2D + +@export var textures : Array[Texture] +@export var structure : Structure + +func _ready() -> void: + structure.changed_direction.connect(on_direction_changed) + +func on_direction_changed(to: float, max_directions: int) -> void: + var indexed_direction = to*max_directions/TAU + + texture = textures[indexed_direction] diff --git a/scripts/structures/directional_sprite.gd.uid b/scripts/structures/directional_sprite.gd.uid new file mode 100644 index 0000000..cd56126 --- /dev/null +++ b/scripts/structures/directional_sprite.gd.uid @@ -0,0 +1 @@ +uid://lchhqigib2t0 diff --git a/sprites/atlasses/Popekko.png b/sprites/atlasses/Popekko.png index 531f8a7ed63827ecdcbf4b1fc78da98f642620c2..fdd2e23fcd7a43de4a338a2fbae084249e519371 100644 GIT binary patch literal 2321 zcmeH|TUb+97RT2~VnaZLAh)3sf}$umRGblF1QMK3h@zwlLLeahxK>iNNGt<^9Jxr4 zK^SgD#K$d$A4x;IJt|)%gS)esGb00ig2}o8|5sU-IEqYs|WSOPEyJ>7}Fx z{DbRmzBAj|@YGsys(9C5c)_JNE%wGGK*3SCDE3D^+`4r zEx%xlyp=z?zw7QoRDFomtk#nyj{i74l|Ixkb9yxD_7@BKybJ+hDx$N^uSuEcs>db0SvW4U4)Y^@GiJDB>UV@uSZZMKKwB*iW2`P@tvlkd=t&&E9 zszocq`TatpZG*2eX?mxZn7rBeXEBd8L4Nhn6WKWn;qJ{>{UN$ReJLmLzRFm3sp0RU z5+B4RD|*rxfWu?Np3t1*D;OIl@yx($INHptT;$1W8*ZnzD<6HQR<9_N@3r4sg~sN+ zMF8p?GIg2#f<=pE7(Z`gghmifLcn9}pg4|^32aD$&7ZFVpnG8@Wa*obTKsaPX_@2lr*psINJ)d8;hlj69XNGb-ZOw{CO?HON z4PxpHyTSFb4s$v8e7YGpMHF;r3LS!}2rb{w?ADQ#u5|D^M;P{=XI7{RST#D3yO|^_ zSz<9g05xVqQ2-_b)YuCgtS9lg>q#=)n-#>Xlt6v-Jp+QRk&c^2fY#;0*3gZZjPhY? zZua7J0#NZ$95O%UKR?)E?o-t$bbx}!J00A(KdMzaYUx_> zRCr*}MXVh9ip2p=rxvB}DLMO}q>4`R(nb;VN1J=PRQv9z2X1?}ogru@N`%t@Tf8n3 zsmJ=otsN^78`Q#S&a4IWgm=zC0cqg97X>8x7Js^VUqM0rQs!@sx*K4K*>{MNzb&;O zt^k#rnklH@DIS6M6IH*?Qxv<)$qxM zm4WADwSmxcC?*E?ePdEXPq#GULDO%XL;CerV~EIPOi8=Py1twAcWqVZ%4Tdo+at;V zPGkElm~-(=fkhu)LNbJ_eZ>(dFa;9?9k4858K7ze6R`HCTndI=((a8Ed}+&s%~g&h zG*lO!^=jk!@goJD3b*?MpeYl-uP>!O3>l^D@hzMkydh1$My|CX7#LfQ42Zxhm{lB#}D?5~(< z(B1vY1Re7UDNwILBuB27V?E$ZjJ7v$O4}#!p+_zM&AZAFBy{#H)KGxwn4s+kvj9xu z#$J4=7WJk~EES14mRgVdbtQsn^(qNC93#xYbi@oaI&H#3QNi--bTh+({{Qzs6F`rs Y3~u@)7lcw`)3k@MJ@&Kec88|?3siP*mH+?% literal 2265 zcmeHI`#+TV9{)VUxFwfzDV$u6)J{hhZE16P60cH*NycTeSVgjxOB&Ng^Ta8GvLm57 zwQTm}F11v0G9%?M*>VX*nQ2Q*mKlq|xXk&EJ%7RZ`8+>-KcDCOdA^^|^SnRr&-=-s z`Rrb)WugTDE4}u3_yLfRi&A@juf`F;S6A}^1f{;V5g?|50JbA})-aXE*df8aD4 z)c*CQQ+jE3aVwtJpM39TxqVgF^|UQsr%t&)GB}5Ksa_o5>J9tG#fu{9$;S7a0xLHO zZPoh?n3j4PSFDycUzVi@c11^R5x!J6I@+5b_4?Ce$r;Ix?#!9-!Fsp3>6g5V?1f=r zyz~#R(cWJd+eWvgS2ZM!`&wD)gt}fkUFg~;>H~skiU)Fl3m;!ShZ;(5K8ly z?PLXqb8y?t21UvarFC2LcD`Xpyt7k=>EqE7-ocNvBlG43I{XTg?3O_JOnJ2~^hS=3 z-<;2F8c3R$=Pih*3hoHa?z;|-KU0?FaL_(I^ibJ_;tBD~NS+EoWj?B1WHlJu(+7Q< z+^lXrNU;s&&9SXI1{wo?&C7opV@{YI`^&$QyFMQe%Nmr`T^l@`fIegs9X89|sE&_+ zdmvXY%ILS>_A2Q|u@=7h(M?&qHlYfH%^P{2aA~e-CvUQ!bZR|Kg`a-h(_>*;mwl%v zpfNI))lcVsKil1)14QsaeSO~ihuE0A_rlh5%yoUDTywMOsvxSY3R{u|)@dy5r|A_c zbPO?Y`mW;h3MXIvoeQ@P|5pA7fk1`8XMHwHQOO zUr}M`gq`VZ!S)#|!9yDeD#)8xcsgrGpM7?dqq` zM{>J8!WrzO4f2K^ony7WLc4?;Khs&kcYym#e>?n4!IxcG@f}8$*oa@uVVT+YkXS}{ zTZ{l`jZE+9rQBEZzN%*-I=|(2lGCiLfx{UI9c_`mlcq`7vhG5ERvpl)P%eO>GEZ`N zPg`Sc9?tYb@NQ7QkXxT`2^KRq%R$<;^O9)(*k8S+PXuI+;vR6B1_y)NE+Y zMniVE^bbpZEy=Foa`=!g{-PGFNpLuNNDF5t4Nomjhz>{X6VkKOi{le_^cYxOD2`u0 zOeV^|I-!Qs)Ct}1yh&Wjin8mXPwo(Rx5XO7=iI-LmK;(xxWZl&cPP{-)c}6*rG0QY zi$VfCZ8hN`QYU^fxdtQiD*Pdnyk*<7g@V_Ql{SC$FZ5m4VP2e4GJgTD0Cx@Ue0$%1&E{mcx6H#;m;zfM1{p)z;WdVnvA* z0IsDnR%&DmNhDPwp-dUKM%zF2mWOqqQUBtTUwM4uY|Ic3gJUL!2xZ#PF5)OTl)1i1 zU{2gH>?K`2TPvZPr6@j>mu&vK2M}h40a#GvzHc5Jpjt*anFJ<;LMh|hoTSdj3t0hx z;El{6!4~RRIvj||NmV#(TY0Zbod~X@f?tPE)p>(iFY;=U%I@@X};>@yv~ z(*cd%aK$@q{?{4{D4b&pAWBeDhE{w8fS8Q5M-Q}jk$}*q50Vg~d1Ej8?t?|(c{-e! zs0_sHDK8Dc~B!#8?ha64am3=2_GI^vi&sYsy<&&sB?@ezdzpYe*R#&-b-Pl@Rx z#pwbB@%q<;@jC>=MxWzH0L^$%q2LH0xqDJ*n`x~=g!J4db5rA&72>}X%P9NlamC(w zc||oiv*i)XOuH^Ph_ITA=|-d#{1roe>0z2$K#gnNRg8hy}O7cFTz;pDMDaU|zkFhKxs41cmDSgxJbrk>n#89Lx|G)ls1hzISAV0BsPQdIi PQD*V-^zo>64^RCMayDh(