From bda9232e723b56b60f84d8141bfe963de90deddd Mon Sep 17 00:00:00 2001 From: 2ndbeam <166764593+2ndbeam@users.noreply.github.com> Date: Wed, 1 May 2024 17:45:17 +0300 Subject: [PATCH] Implemented state machine & added NPC ship template --- scenes/Ships/Modules/Hulls/kamikaze_hull.tscn | 16 ++++++ scenes/Ships/NPC Ships/kamikaze_ship.tscn | 35 ++++++++++++ scenes/Ships/player_ship.tscn | 44 ++++++++++----- scenes/Star Systems/star_system_debug.tscn | 16 ++++-- scenes/Star Systems/star_system_template.tscn | 19 ++++++- scenes/Star.tscn | 3 +- scripts/Ship/hull.gd | 2 + scripts/Ship/ship.gd | 2 +- .../State Machine/Kamikaze/WanderingState.gd | 52 ++++++++++++++++++ scripts/State Machine/state.gd | 20 +++++++ scripts/State Machine/tree.gd | 51 +++++++++++++++++ scripts/faction.gd | 11 ++++ scripts/objects/Star.gd | 1 + sprites/kamikaze.png | Bin 0 -> 3857 bytes sprites/kamikaze.png.import | 34 ++++++++++++ 15 files changed, 284 insertions(+), 22 deletions(-) create mode 100644 scenes/Ships/Modules/Hulls/kamikaze_hull.tscn create mode 100644 scenes/Ships/NPC Ships/kamikaze_ship.tscn create mode 100644 scripts/State Machine/Kamikaze/WanderingState.gd create mode 100644 scripts/State Machine/state.gd create mode 100644 scripts/State Machine/tree.gd create mode 100644 scripts/faction.gd create mode 100644 sprites/kamikaze.png create mode 100644 sprites/kamikaze.png.import diff --git a/scenes/Ships/Modules/Hulls/kamikaze_hull.tscn b/scenes/Ships/Modules/Hulls/kamikaze_hull.tscn new file mode 100644 index 0000000..b686c17 --- /dev/null +++ b/scenes/Ships/Modules/Hulls/kamikaze_hull.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=3 format=3 uid="uid://bkd4gyhlota7l"] + +[ext_resource type="PackedScene" uid="uid://bsu4eqwdfewwi" path="res://scenes/Ships/Modules/Hulls/hull.tscn" id="1_l56rl"] +[ext_resource type="Texture2D" uid="uid://bchkncbniclhh" path="res://sprites/kamikaze.png" id="2_8ba3c"] + +[node name="Hull" instance=ExtResource("1_l56rl")] +max_hp = 10.0 +velocity_collision_treshold = 150.0 +collision_damage = 10.0 + +[node name="Sprite" parent="." index="0"] +position = Vector2(0.5, 0.5) +texture = ExtResource("2_8ba3c") + +[node name="Collision" parent="." index="1"] +polygon = PackedVector2Array(-69.5, 21, -69.5, 66, -68.5, 66, -57.5, 55, -57.5, 65, -56.5, 65, 70.5, 1, 70.5, 0, -56.5, -64, -57.5, -64, -57.5, -54, -68.5, -65, -69.5, -65, -69.5, -20, -68.5, -20, -57.5, -31, -57.5, -23, -32.5, -10, -32.5, 11, -57.5, 24, -57.5, 32, -68.5, 21) diff --git a/scenes/Ships/NPC Ships/kamikaze_ship.tscn b/scenes/Ships/NPC Ships/kamikaze_ship.tscn new file mode 100644 index 0000000..6566c2e --- /dev/null +++ b/scenes/Ships/NPC Ships/kamikaze_ship.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=7 format=3 uid="uid://pev6k21vqaem"] + +[ext_resource type="Script" path="res://scripts/Ship/ship.gd" id="1_82bba"] +[ext_resource type="PackedScene" uid="uid://bkd4gyhlota7l" path="res://scenes/Ships/Modules/Hulls/kamikaze_hull.tscn" id="2_165nu"] +[ext_resource type="PackedScene" uid="uid://mw4kwxoeqch3" path="res://scenes/Ships/Modules/Engines/engine.tscn" id="3_plu72"] +[ext_resource type="PackedScene" uid="uid://bunboi5ouscw8" path="res://scenes/Ships/Modules/Shields/shield.tscn" id="4_aos8x"] +[ext_resource type="Script" path="res://scripts/State Machine/tree.gd" id="5_m8m3f"] +[ext_resource type="Script" path="res://scripts/State Machine/Kamikaze/WanderingState.gd" id="6_emcj1"] + +[node name="KamikazeShip" type="Node2D"] +script = ExtResource("1_82bba") + +[node name="HullHolder" type="Node" parent="."] + +[node name="Hull" parent="HullHolder" instance=ExtResource("2_165nu")] +collision_damage = 15.0 + +[node name="Engine" parent="." instance=ExtResource("3_plu72")] + +[node name="Shield" parent="." instance=ExtResource("4_aos8x")] +max_capacity = 5 + +[node name="Weapons" type="Node2D" parent="."] + +[node name="StateTree" type="Node" parent="." node_paths=PackedStringArray("state")] +script = ExtResource("5_m8m3f") +state = NodePath("WanderingState") + +[node name="WanderingState" type="Node" parent="StateTree"] +script = ExtResource("6_emcj1") + +[node name="UpdateDestination" type="Timer" parent="StateTree/WanderingState"] +wait_time = 10.0 + +[connection signal="timeout" from="StateTree/WanderingState/UpdateDestination" to="StateTree/WanderingState" method="update_destination"] diff --git a/scenes/Ships/player_ship.tscn b/scenes/Ships/player_ship.tscn index 2660164..b79bfac 100644 --- a/scenes/Ships/player_ship.tscn +++ b/scenes/Ships/player_ship.tscn @@ -1,10 +1,13 @@ -[gd_scene load_steps=11 format=3 uid="uid://dok3i8u5t1ka4"] +[gd_scene load_steps=14 format=3 uid="uid://dok3i8u5t1ka4"] -[ext_resource type="PackedScene" uid="uid://bjkshql8ut6hk" path="res://scenes/Ships/ship.tscn" id="1_6x7bu"] [ext_resource type="Script" path="res://scripts/Ship/player_ship.gd" id="2_oqdd7"] [ext_resource type="Script" path="res://scripts/Ship/player_input_controller.gd" id="3_0e84a"] +[ext_resource type="PackedScene" uid="uid://bsu4eqwdfewwi" path="res://scenes/Ships/Modules/Hulls/hull.tscn" id="3_ku5af"] [ext_resource type="PackedScene" uid="uid://ryy1tdrxmjav" path="res://scenes/Ships/Modules/Weapons/weapon.tscn" id="4_fy1be"] +[ext_resource type="PackedScene" uid="uid://mw4kwxoeqch3" path="res://scenes/Ships/Modules/Engines/engine.tscn" id="4_pmbbn"] +[ext_resource type="PackedScene" uid="uid://bunboi5ouscw8" path="res://scenes/Ships/Modules/Shields/shield.tscn" id="5_7fjpq"] [ext_resource type="Script" path="res://scripts/Ship/player_camera.gd" id="5_rclap"] +[ext_resource type="Script" path="res://scripts/Ship/weapons.gd" id="6_f6fm2"] [sub_resource type="GDScript" id="GDScript_ry4sc"] resource_name = "money_counter" @@ -77,20 +80,35 @@ func _process(_delta): text = unformatted_text.format(format) " -[node name="PlayerShip" instance=ExtResource("1_6x7bu")] +[node name="PlayerShip" type="Node2D"] +process_mode = 1 script = ExtResource("2_oqdd7") -[node name="InputController" type="Node2D" parent="." index="0"] +[node name="InputController" type="Node2D" parent="."] script = ExtResource("3_0e84a") -[node name="Weapon" parent="Weapons" index="0" instance=ExtResource("4_fy1be")] +[node name="HullHolder" type="Node" parent="."] + +[node name="Hull" parent="HullHolder" instance=ExtResource("3_ku5af")] +collision_mask = 3 +max_contacts_reported = 1 +contact_monitor = true + +[node name="Engine" parent="." instance=ExtResource("4_pmbbn")] + +[node name="Shield" parent="." instance=ExtResource("5_7fjpq")] + +[node name="Weapons" type="Node2D" parent="."] +script = ExtResource("6_f6fm2") + +[node name="Weapon" parent="Weapons" instance=ExtResource("4_fy1be")] action_id = "primary" ammo_type = "Laser Energy" ammo_consumption = 1.0 -[node name="ColorableGUI" type="CanvasLayer" parent="." index="5"] +[node name="ColorableGUI" type="CanvasLayer" parent="."] -[node name="Money" type="Label" parent="ColorableGUI" index="0"] +[node name="Money" type="Label" parent="ColorableGUI"] offset_left = 7.0 offset_top = 611.0 offset_right = 92.0 @@ -98,7 +116,7 @@ offset_bottom = 634.0 text = "Money: %d" script = SubResource("GDScript_ry4sc") -[node name="Velocity" type="Label" parent="ColorableGUI" index="1"] +[node name="Velocity" type="Label" parent="ColorableGUI"] offset_left = 7.0 offset_top = 688.0 offset_right = 137.0 @@ -106,7 +124,7 @@ offset_bottom = 711.0 text = "Velocity: %d px/s" script = SubResource("GDScript_vko7a") -[node name="Health" type="Label" parent="ColorableGUI" index="2"] +[node name="Health" type="Label" parent="ColorableGUI"] offset_left = 7.0 offset_top = 663.0 offset_right = 168.0 @@ -114,7 +132,7 @@ offset_bottom = 686.0 text = "Health: {0} / {1} units" script = SubResource("GDScript_uoaip") -[node name="Shield" type="Label" parent="ColorableGUI" index="3"] +[node name="Shield" type="Label" parent="ColorableGUI"] offset_left = 8.0 offset_top = 638.0 offset_right = 164.0 @@ -122,7 +140,7 @@ offset_bottom = 661.0 text = "Shield: {0} / {1} units" script = SubResource("GDScript_q1sx2") -[node name="Ammunition" type="Label" parent="ColorableGUI" index="4"] +[node name="Ammunition" type="Label" parent="ColorableGUI"] offset_left = 1060.0 offset_top = 638.0 offset_right = 1273.0 @@ -133,8 +151,8 @@ Rockets: {2} / {3}" horizontal_alignment = 2 script = SubResource("GDScript_rrgab") -[node name="NonColorableGUI" type="CanvasLayer" parent="." index="6"] +[node name="NonColorableGUI" type="CanvasLayer" parent="."] -[node name="Camera" type="Camera2D" parent="." index="7"] +[node name="Camera" type="Camera2D" parent="."] zoom = Vector2(0.5, 0.5) script = ExtResource("5_rclap") diff --git a/scenes/Star Systems/star_system_debug.tscn b/scenes/Star Systems/star_system_debug.tscn index d819dff..2224de4 100644 --- a/scenes/Star Systems/star_system_debug.tscn +++ b/scenes/Star Systems/star_system_debug.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=4 format=3 uid="uid://crneq2enhxsw5"] +[gd_scene load_steps=5 format=3 uid="uid://crneq2enhxsw5"] [ext_resource type="PackedScene" uid="uid://bsnrcw64qr2hr" path="res://scenes/Star Systems/star_system_template.tscn" id="1_2ai1l"] [ext_resource type="PackedScene" uid="uid://dk3nvl8f0v24e" path="res://scenes/base_template.tscn" id="3_m5ica"] +[ext_resource type="PackedScene" uid="uid://pev6k21vqaem" path="res://scenes/Ships/NPC Ships/kamikaze_ship.tscn" id="4_i6rbg"] [ext_resource type="PackedScene" uid="uid://dok3i8u5t1ka4" path="res://scenes/Ships/player_ship.tscn" id="7_jyplv"] [node name="StarSystem" instance=ExtResource("1_2ai1l")] @@ -12,13 +13,16 @@ height = 16384 scroll_offset = Vector2(681.667, 317.783) stars_amount = 4000 -[node name="PlayerShip" parent="." index="3" instance=ExtResource("7_jyplv")] +[node name="Nebula" parent="Background" index="0" node_paths=PackedStringArray("tracked_node")] +tracked_node = NodePath("../../FactionPlayer/PlayerShip") +color_background = Color(0.276474, 0.0962249, 0.200656, 1) + +[node name="PlayerShip" parent="FactionPlayer" index="0" instance=ExtResource("7_jyplv")] process_mode = 0 position = Vector2(19, 10) -[node name="Base" parent="." index="4" instance=ExtResource("3_m5ica")] +[node name="Base" parent="FactionPeaceful" index="0" instance=ExtResource("3_m5ica")] position = Vector2(7171, -28) -[node name="Nebula" parent="Background" index="0" node_paths=PackedStringArray("tracked_node")] -tracked_node = NodePath("../../PlayerShip") -color_background = Color(0.276474, 0.0962249, 0.200656, 1) +[node name="KamikazeShip" parent="FactionAggressive" index="0" instance=ExtResource("4_i6rbg")] +position = Vector2(687, -302) diff --git a/scenes/Star Systems/star_system_template.tscn b/scenes/Star Systems/star_system_template.tscn index 69124e2..0227716 100644 --- a/scenes/Star Systems/star_system_template.tscn +++ b/scenes/Star Systems/star_system_template.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://bsnrcw64qr2hr"] +[gd_scene load_steps=9 format=3 uid="uid://bsnrcw64qr2hr"] [ext_resource type="Script" path="res://scripts/star_system.gd" id="1_xx8w2"] [ext_resource type="PackedScene" uid="uid://dpggye27ln436" path="res://scenes/Star Systems/Required Scenes/star_generator.tscn" id="2_iqrn0"] @@ -6,6 +6,7 @@ [ext_resource type="Material" uid="uid://bawhivm5cr2w" path="res://shaders/materials/nebula_shader.tres" id="4_o0ld7"] [ext_resource type="Script" path="res://scripts/nebula.gd" id="5_la84n"] [ext_resource type="PackedScene" uid="uid://c7is7y341f6wa" path="res://scenes/Star Systems/Required Scenes/pause_controller.tscn" id="6_61vq5"] +[ext_resource type="Script" path="res://scripts/faction.gd" id="7_wm3vi"] [sub_resource type="GDScript" id="GDScript_4yoh5"] resource_name = "version_label" @@ -57,3 +58,19 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("5_la84n") color_background = Color(0.36, 0.18612, 0.1116, 1) + +[node name="FactionPlayer" type="Node" parent="."] +script = ExtResource("7_wm3vi") +faction = 1 + +[node name="FactionPeaceful" type="Node" parent="."] +script = ExtResource("7_wm3vi") +faction = 2 + +[node name="FactionNeutral" type="Node" parent="."] +script = ExtResource("7_wm3vi") +faction = 3 + +[node name="FactionAggressive" type="Node" parent="."] +script = ExtResource("7_wm3vi") +faction = 4 diff --git a/scenes/Star.tscn b/scenes/Star.tscn index ed39ae6..b2d688c 100644 --- a/scenes/Star.tscn +++ b/scenes/Star.tscn @@ -43,8 +43,9 @@ animations = [{ }] [node name="Star" type="AnimatedSprite2D"] -self_modulate = Color(1, 1, 1, 0.501961) z_index = -19 sprite_frames = SubResource("SpriteFrames_oggfj") autoplay = "default" +frame = 1 +frame_progress = 0.66336 script = ExtResource("9_kew3k") diff --git a/scripts/Ship/hull.gd b/scripts/Ship/hull.gd index 97d371d..4fbcd99 100644 --- a/scripts/Ship/hull.gd +++ b/scripts/Ship/hull.gd @@ -19,6 +19,8 @@ class_name Hull } ## How much speed should ship have before collision to take damage @export var velocity_collision_treshold: float = 200.0 +## How much damage should ship take when its velocity equals to treshold +@export var collision_damage: float = 20.0 ## Current ammunition. Change this with set_ammunition @onready var ammunition: Dictionary = max_ammunition.duplicate() diff --git a/scripts/Ship/ship.gd b/scripts/Ship/ship.gd index 79645b3..fb8fad0 100644 --- a/scripts/Ship/ship.gd +++ b/scripts/Ship/ship.gd @@ -17,7 +17,7 @@ signal destroyed @onready var spawn_position: Vector2 = global_position ## Faction which this ship belongs to -var faction : Game.Faction = Game.Faction.Player +var faction : Game.Faction func _ready() -> void: hull.global_position = global_position diff --git a/scripts/State Machine/Kamikaze/WanderingState.gd b/scripts/State Machine/Kamikaze/WanderingState.gd new file mode 100644 index 0000000..c12fbab --- /dev/null +++ b/scripts/State Machine/Kamikaze/WanderingState.gd @@ -0,0 +1,52 @@ +extends State + +@onready var ship = get_parent().get_parent() +@onready var star_system = get_tree().current_scene + +@onready var update_timer = $UpdateDestination +## Ship will strive to achieve this angle +var destination_degrees: float = 0: + set(value): + if value > 180: + destination_degrees = value - 360 + elif value < -180: + destination_degrees = 360 + value + else: + destination_degrees = value +## Delta to destination_degrees +var destination_difference: float = 15.0 +## available map bounds (use with absolute position) +var available_bounds: Vector2 + +func _ready(): + available_bounds = Vector2(star_system.width / 2, star_system.height / 2) + randomize() + +func enter(_message): + update_timer.start() + update_destination() + +func process(_delta): + # checking if need to apply torque + var current_destination_difference = destination_degrees - ship.hull.global_rotation_degrees + var rotation_sign = sign(current_destination_difference) + if current_destination_difference < 0: + current_destination_difference = 180 - current_destination_difference + if current_destination_difference > 180: + current_destination_difference -= 180 + if abs(current_destination_difference) > destination_difference: + ship.engine.rotation_axis = rotation_sign * (current_destination_difference / 180) + else: + ship.engine.rotation_axis = 0.0 + # making ship always accelerate + ship.engine.acceleration_axis = 1.0 + # if ship is out of star system bounds, set destination angle to center + if abs(ship.global_position.x) > available_bounds.x or abs(ship.global_position.y) > available_bounds.y: + destination_degrees = rad_to_deg(ship.global_position.angle_to_point(Vector2.ZERO)) + +func exit(): + update_timer.stop() + +## Set new random direction +func update_destination(): + destination_degrees += randi_range(-180, 180) diff --git a/scripts/State Machine/state.gd b/scripts/State Machine/state.gd new file mode 100644 index 0000000..6e2a38d --- /dev/null +++ b/scripts/State Machine/state.gd @@ -0,0 +1,20 @@ +extends Node + +class_name State + +var tree : StateTree + +func enter(_message : Dictionary): + pass + +func exit(): + pass + +func process(_delta): + pass + +func physics_process(_delta): + pass + +func input(_message : Dictionary): + pass diff --git a/scripts/State Machine/tree.gd b/scripts/State Machine/tree.gd new file mode 100644 index 0000000..579e16d --- /dev/null +++ b/scripts/State Machine/tree.gd @@ -0,0 +1,51 @@ +extends Node + +class_name StateTree + +## Emitted when active state is transitioned +signal transitioned(to_state) + +## Active state +@export var state : State + +## Dictionary of all states +var cached_states = {} + +func _ready(): + for child in get_children(): + child.tree = self + cached_states[child.name] = child + + state.enter({}) + +## Make another state active. Optionally, properties can be provided with message +func transit(to : String, message : Dictionary = {}): + + state.exit() + + state = cached_states[to] + state.enter(message) + + transitioned.emit(to) + +## Provides properties to active state +func send_input(message : Dictionary = {}): + state.input(message) + +## Provides properties to all states +func send_input_to_system(message : Dictionary = {}): + for state in cached_states.values(): + state.input(message) + +## Returns state with provided name +func get_state(state_name) -> State: + return cached_states[state_name] + +#region Перегруженные виртуальные функции гойды +func _process(delta) -> void: + state.process(delta) + +func _physics_process(delta) -> void: + state.physics_process(delta) + +#endregion diff --git a/scripts/faction.gd b/scripts/faction.gd new file mode 100644 index 0000000..0e3d8d3 --- /dev/null +++ b/scripts/faction.gd @@ -0,0 +1,11 @@ +extends Node + +class_name FactionNode + +## Faction that is represented with this node +@export var faction: Game.Faction + +func _ready(): + for child in get_children(): + if "faction" in child: + child.faction = faction diff --git a/scripts/objects/Star.gd b/scripts/objects/Star.gd index 1f27893..6979a00 100644 --- a/scripts/objects/Star.gd +++ b/scripts/objects/Star.gd @@ -8,3 +8,4 @@ func _ready(): speed_scale = randf_range(0, 2) var colors = [Color.LIGHT_BLUE, Color.WHITE, Color.LIGHT_GOLDENROD, Color.YELLOW, Color.ORANGE, Color.ORANGE_RED, Color.RED] modulate = colors.pick_random() + modulate.a = randf_range(0.5, 1) diff --git a/sprites/kamikaze.png b/sprites/kamikaze.png new file mode 100644 index 0000000000000000000000000000000000000000..cb485b65cfebfcbc9d6409c758e4fe84669c10df GIT binary patch literal 3857 zcmX9>c|6oz7ylt--^LhAmW(CFo{Uh!FpF*M6c0xBeP3&0Ml-{c3^8gRG+IS%CB_%~SEIJ~A5DKtV&*QrSI1gAs=7&pR{ zR{3AieqD?@Vuia`{HEdB$H?ReFB6qV9Otnc^$WLn155tSTd%IoR$dA0bkUp@Y5cvc zR4SCRJZ6?~`=7FekT03^zbb!@_168&On=)P!~fD4z^W8_DT`VP5I=AsTmfYZ_y{`E zX>9yFj;rl#Tv-|*${h;mAAw*n-*C(?e`)a(`gFxPMyK8p8cjkCLiJ_Q}DuZ++ zaxdyTI5@;MpGgm!xS$jUyy7Sj4WFmA^_JmCQ^vVr;o*um?89#vmLGKlPTpbzw4;3ELzLyn7cAsOm7WL2C&*kx z9)3~(<+0`+!R2F~b$&m@=hkOm?PNaUT+JZttg>*Ux$CSFvIY6e!*v_)aLW-QRwlvq%h#mn+ylOs8%P~H_K{_rM^oK6s z#b|1J5sT=A+R+HoOT6;uhw1AZ8zUnlX)$~LemKD-^WD|1n(FGQzvWFrJ9X$E;7Sr% z_n$8Zcr)7C%##xnQ@_oEEJ2y;t@~Dn(9A`+_D!S(BzS;@+MT~$(YCp~@Zla=sDB)G zEE1-zt^KCEIoXq`!qR>bJ)L6N5Ib!ZIngHDp7GN|BWe5UNf%_sCc^Lh@1%TLBR+R` z_ok%9lEx+_;v_ecuTrv8T*7ozqJoV$(VWzE# z%fulOJIn>!_;|y6_wI=lKrw!5-lyU#zg97vFJ9C?9$l3A(f>LFVoUq{tA0nccrItb zp^s29blZtAKX?;nk$!X{W5psdF)=jnmvtI*>u7OpcqK)_np&v8z(DnJa$Ija2JB0k zL@OMN8YR7b>lhU$35j#GOtYTPIUFx60Bu$2(wzs-x2C3MX6jiEIYpX9*jk=kb;pPsR37@fAF*mDdhUREQF$td zfyQMwH%!A#T;?atLt|n>yuHgH{8L+NpSPzkDy5H6ZcTH)G{J02>DVGkc-69o zo;CT;&;Q=|z&TSn$PpITK_f{6G*^eg#n@A{&m$ZgI&%8ci-YtPd1}J;`q`3_lK834 z3v6x2M0+N(@ZZKLf~RL`A#x70cPVJnOWgMFLinF(xFcB0c{JLmCoz9bOKTh-930#l zuv03dCS014hbvKd1Ky*rV>~|$4zjbexz*wDgLTLOg8=q{mhot#I3F~WB5p^yxz}wP zKXWi4Az?lixgQ$Z!Qt^0JK_PChVC?XkCpF z4JZju5PWI-yF7Jg{x;dOZu3ns@x3nFo0``mgkQ5Yi zBwx6IaV*N`NwN|E3#LL+W!4d8KDZM z?+={Zo{=&NJC5#41)LF zwmBNHXXDK?Wm&mc!nTp1QmI~TsRmdqHd~iQgQyESqr4vmi{y$L@6w%AiB^Ec9=08# zo{d4HJ8^pLGfu^afnV8=9n1tbG>Lw!qI#pOo$t@+YB* z(aXDO$VCT|rhwb>edO&wODki|T?-50qKA&VrJkkcZ*8T1nVZ`HtE|H&&Bbz@?9SL~ zW$jk}Spw5wKDiZ4Wc2ivv;24v3Iwb7DbkH>D3Xo=983pxwpK?EA08258vGhIeMQai zeeK-)JFda3-MpYiG@h+uZ~ih<>nAyVpub;ygU9DFYI`rE%hyIrrw=heC;7mhvh2$Y zQK@G!_--M>=#R^Nb_2h&&=#YrCr*^>J>6}nd-lzdK7_(eJ2A>T1ja)08baPFzAZDd z+xNSv3HjyAm-Vj$Rl%%pDyDXiggwaf%TM@jXhKo@K6mG_q;rM8MX0+JPf;2y6K8+g zz})4uCvkU8MtRIqmh-m_uyuazjMCCMT=DrLrcTRXj+(ISl+1`5{+3gPZd-#*-a02w zZ@qP;!$?Yv)1r8fT|KxJuDc@eFrbTXKVA-pv&9zDTV0**0)xKThf^ebCf2jRzU(~! zBmw2dnj4kv?cwUF+ z#n7_pw+3UWNf^GtYQoj+X?uJ44z$OzxK4t=s)kJ*@bmQKzu0vFeB{0_kIOsX7~z|0 zj#Zb5RuJX?@~)zQPL~V{4wiefZcL2VJa1{BN`(&H=4*@|7X&w5y()MAXcBNKSGzfu z6W{EN$6o;3oUHU8F-zG%FNkzSL0QvyG|-PNIyl>7h|hU7%Wg?p)K6Ovs^Y`|HDt)F z;<*Z3eCv2DQsGlu+Cwb|x3{-i3?Q3JGY zKzJ@(#pwdHlu`4?KtMD!Z{zJP8~m0T=7vBCNJ{8|l0%LV zHq9x((9p2Izn^$2hc8T{0?GFiy= zuxhsbEOP{#_<8MsWqSH?P~Td=wOE)&9VvciT;tB2JF%_fq2b|H(b2VjB~Z@ zY@g(v(3sc{6H{5GCL+s=WA~x8e(hI5;l$SQZwm|S9fXp|>+2R5_-!Whv{aGP${49q z53baI{db}PBd#g%cLFmEqTq)U6gyY0GwllRV;}*^#cl_L2+*7r!9B_^*4S5ku;l4L z`0La4$j%S_+^FKGmNdx^U&#iGxvxWcS3@-IPIzWtDemqFC3}l3HlXo=kfi=rzP_$G wk5p)