From 6eec1f6181ec0fa996b1ad931ccde9158d4f4eb2 Mon Sep 17 00:00:00 2001 From: Sebastian Benjamin Date: Tue, 4 Mar 2025 18:36:01 -0800 Subject: [PATCH] Player death and position reset. Also Godot 4.4 --- .../com.heroiclabs.nakama/Nakama.gd.uid | 1 + .../api/NakamaAPI.gd.uid | 1 + .../api/NakamaRTAPI.gd.uid | 1 + .../api/NakamaRTMessage.gd.uid | 1 + .../api/NakamaSession.gd.uid | 1 + .../api/NakamaStorageObjectId.gd.uid | 1 + .../api/NakamaWriteStorageObject.gd.uid | 1 + .../client/NakamaClient.gd.uid | 1 + .../client/NakamaHTTPAdapter.gd.uid | 1 + .../socket/NakamaSocket.gd.uid | 1 + .../socket/NakamaSocketAdapter.gd.uid | 1 + .../utils/NakamaAsyncResult.gd.uid | 1 + .../utils/NakamaException.gd.uid | 1 + .../utils/NakamaLogger.gd.uid | 1 + .../utils/NakamaMultiplayerBridge.gd.uid | 1 + .../utils/NakamaMultiplayerPeer.gd.uid | 1 + .../utils/NakamaSerializer.gd.uid | 1 + client/danmaku!/Board.tscn | 14 +++++-- client/danmaku!/BulletManager.gd | 22 ++++++++++ client/danmaku!/BulletManager.gd.uid | 1 + client/danmaku!/ScalableSprite2D.gd.uid | 1 + client/danmaku!/globals.gd.uid | 1 + client/danmaku!/network_manager.gd | 39 ++++++------------ client/danmaku!/network_manager.gd.uid | 1 + client/danmaku!/player.gd | 32 +++++++------- client/danmaku!/player.gd.uid | 1 + client/danmaku!/player.tscn | 11 +++-- client/danmaku!/scaling.gd.uid | 1 + client/danmaku-shared.gdextension.uid | 1 + client/project.godot | 2 +- client/test-death-noise.mp3 | Bin 0 -> 37632 bytes server/main.go | 33 ++++++++++----- 32 files changed, 118 insertions(+), 59 deletions(-) create mode 100644 client/addons/com.heroiclabs.nakama/Nakama.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid create mode 100644 client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid create mode 100644 client/danmaku!/BulletManager.gd create mode 100644 client/danmaku!/BulletManager.gd.uid create mode 100644 client/danmaku!/ScalableSprite2D.gd.uid create mode 100644 client/danmaku!/globals.gd.uid create mode 100644 client/danmaku!/network_manager.gd.uid create mode 100644 client/danmaku!/player.gd.uid create mode 100644 client/danmaku!/scaling.gd.uid create mode 100644 client/danmaku-shared.gdextension.uid create mode 100644 client/test-death-noise.mp3 diff --git a/client/addons/com.heroiclabs.nakama/Nakama.gd.uid b/client/addons/com.heroiclabs.nakama/Nakama.gd.uid new file mode 100644 index 0000000..8c40e6f --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/Nakama.gd.uid @@ -0,0 +1 @@ +uid://b3os1t7djg0c7 diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid new file mode 100644 index 0000000..37f64e3 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid @@ -0,0 +1 @@ +uid://hebdk7gx1u8c diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid new file mode 100644 index 0000000..44cf509 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid @@ -0,0 +1 @@ +uid://2l8y628n8ukc diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid new file mode 100644 index 0000000..a32884c --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid @@ -0,0 +1 @@ +uid://d4lu87a2trueb diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid new file mode 100644 index 0000000..e167f8f --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid @@ -0,0 +1 @@ +uid://ckcep8xcypxhx diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid new file mode 100644 index 0000000..ea52579 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid @@ -0,0 +1 @@ +uid://bqgpnrx3ym11e diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid new file mode 100644 index 0000000..1efd6e4 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid @@ -0,0 +1 @@ +uid://csqmo268gf217 diff --git a/client/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid b/client/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid new file mode 100644 index 0000000..2a3c8e1 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid @@ -0,0 +1 @@ +uid://do7ks66xlkvqg diff --git a/client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid b/client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid new file mode 100644 index 0000000..140a5ff --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid @@ -0,0 +1 @@ +uid://chnrd7u5qamlq diff --git a/client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid b/client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid new file mode 100644 index 0000000..2434a2c --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid @@ -0,0 +1 @@ +uid://iy2uo326og1p diff --git a/client/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid b/client/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid new file mode 100644 index 0000000..4094832 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid @@ -0,0 +1 @@ +uid://c8tfobve6b0k4 diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid new file mode 100644 index 0000000..2beb26e --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid @@ -0,0 +1 @@ +uid://dqyr6q4tquve3 diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid new file mode 100644 index 0000000..81b892e --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid @@ -0,0 +1 @@ +uid://dp68cesoggrb4 diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid new file mode 100644 index 0000000..0752a03 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid @@ -0,0 +1 @@ +uid://duqqgdiqwur2g diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid new file mode 100644 index 0000000..ec9c9c7 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid @@ -0,0 +1 @@ +uid://c35k2c0pg7csr diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid new file mode 100644 index 0000000..0bcdba0 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid @@ -0,0 +1 @@ +uid://dldoidphr80mb diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid new file mode 100644 index 0000000..a67d64e --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid @@ -0,0 +1 @@ +uid://bnxd5hq82ncyw diff --git a/client/danmaku!/Board.tscn b/client/danmaku!/Board.tscn index dd95b89..a34a461 100644 --- a/client/danmaku!/Board.tscn +++ b/client/danmaku!/Board.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=4 format=3 uid="uid://dsiowq0rnacln"] +[gd_scene load_steps=5 format=3 uid="uid://dsiowq0rnacln"] [ext_resource type="PackedScene" uid="uid://cd3tqt7hr5pqs" path="res://danmaku!/Player.tscn" id="1_22cjd"] -[ext_resource type="Script" path="res://danmaku!/scaling.gd" id="1_o1mqp"] -[ext_resource type="Script" path="res://danmaku!/network_manager.gd" id="2_b2dol"] +[ext_resource type="Script" uid="uid://ggkxv1cb1bjk" path="res://danmaku!/scaling.gd" id="1_o1mqp"] +[ext_resource type="Script" uid="uid://cd67rrch5h4t7" path="res://danmaku!/network_manager.gd" id="2_b2dol"] +[ext_resource type="Script" uid="uid://ddc5iqgtyv2ns" path="res://danmaku!/BulletManager.gd" id="4_ubrrh"] [node name="Board" type="Control"] custom_minimum_size = Vector2(607.5, 1080) @@ -32,6 +33,11 @@ scale = Vector2(6.75, 6.75) [node name="Player" parent="World" instance=ExtResource("1_22cjd")] unique_name_in_owner = true -[node name="NetworkManager" type="Node2D" parent="World" node_paths=PackedStringArray("player")] +[node name="BulletManager" type="Node2D" parent="World"] +unique_name_in_owner = true +script = ExtResource("4_ubrrh") + +[node name="NetworkManager" type="Node" parent="World" node_paths=PackedStringArray("player")] +unique_name_in_owner = true script = ExtResource("2_b2dol") player = NodePath("../Player") diff --git a/client/danmaku!/BulletManager.gd b/client/danmaku!/BulletManager.gd new file mode 100644 index 0000000..c57619f --- /dev/null +++ b/client/danmaku!/BulletManager.gd @@ -0,0 +1,22 @@ +extends Node2D + +var bullet_intraframe_mov_delta := 0.0 +var predicted_tick := 0 +var bullets = [] + +func _ready() -> void: + child_exiting_tree.connect(_on_child_exiting_tree) + +func _process(delta: float) -> void: + for bullet in bullets: + var prev_pos = bullet.get_current_pos(predicted_tick) + var next_pos = bullet.get_current_pos(predicted_tick + 1) + var interpolated_pos = prev_pos.lerp(next_pos, bullet_intraframe_mov_delta) + + bullet.position = interpolated_pos + + if bullet.beyond_kill_boundary(predicted_tick): + bullet.queue_free() + +func _on_child_exiting_tree(node: DanmakuBullet): + bullets.erase(node) diff --git a/client/danmaku!/BulletManager.gd.uid b/client/danmaku!/BulletManager.gd.uid new file mode 100644 index 0000000..59a0227 --- /dev/null +++ b/client/danmaku!/BulletManager.gd.uid @@ -0,0 +1 @@ +uid://ddc5iqgtyv2ns diff --git a/client/danmaku!/ScalableSprite2D.gd.uid b/client/danmaku!/ScalableSprite2D.gd.uid new file mode 100644 index 0000000..50196f8 --- /dev/null +++ b/client/danmaku!/ScalableSprite2D.gd.uid @@ -0,0 +1 @@ +uid://v6jris184o8u diff --git a/client/danmaku!/globals.gd.uid b/client/danmaku!/globals.gd.uid new file mode 100644 index 0000000..9fa7127 --- /dev/null +++ b/client/danmaku!/globals.gd.uid @@ -0,0 +1 @@ +uid://ca3lryv0wsoat diff --git a/client/danmaku!/network_manager.gd b/client/danmaku!/network_manager.gd index cc21624..8221654 100644 --- a/client/danmaku!/network_manager.gd +++ b/client/danmaku!/network_manager.gd @@ -1,4 +1,4 @@ -extends Node2D +extends Node var nakama_client: NakamaClient var nakama_session: NakamaSession var nakama_socket: NakamaSocket @@ -7,12 +7,9 @@ var nakama_socket: NakamaSocket var predicted_tick = 0 var delta_counter = 0 -var bullet_lerp_factor := 0.0 -var bullets = [] var current_match_id = "" func _ready() -> void: - child_exiting_tree.connect(_on_child_exiting_tree) print("Attempting auth.") await attempt_auth() print("Attempting to create debug match.") @@ -23,24 +20,15 @@ func _process(delta: float) -> void: if current_match_id == "": return - predict_tick_and_broadcast(delta) - - for bullet in bullets: - var prev_pos = bullet.get_current_pos(predicted_tick) - var next_pos = bullet.get_current_pos(predicted_tick + 1) - var interpolated_pos = prev_pos.lerp(next_pos, bullet_lerp_factor) - - bullet.position = interpolated_pos - - if bullet.beyond_kill_boundary(predicted_tick): - bullet.queue_free() + %BulletManager.bullet_intraframe_mov_delta = predict_tick_and_broadcast(delta) + %BulletManager.predicted_tick = predicted_tick func _on_match_state(p_state : NakamaRTAPI.MatchData): match p_state.op_code: 2: var data = JSON.parse_string(p_state.data) - # Set player position given server bounds-checking + # Set player position if server demands a forced position if data["forcePlayerPos"]: player.set_position_data( Vector2( @@ -49,10 +37,12 @@ func _on_match_state(p_state : NakamaRTAPI.MatchData): ), float(data["playerPos"]["radius_multiplier"]) ) - + + # Handle player death if there is an ongoing death timer if int(data["deathTimer"]) > 0: - print("server says u died") %Player.kill() + elif int(data["deathTimer"]) == 0: + %Player.resurrect() # Spawn new bullets for b in data["newBullets"]: @@ -72,8 +62,8 @@ func _on_match_state(p_state : NakamaRTAPI.MatchData): var scale_ratio = ((b["radius_multiplier"] * 2) * Globals.SERVER_SIZE.x) / bullet.texture.get_width() bullet.scale = Vector2(scale_ratio, scale_ratio) - add_child(bullet) - bullets.append(bullet) + %BulletManager.add_child(bullet) + %BulletManager.bullets.append(bullet) predicted_tick = int(b["tick"]) delta_counter = 0 @@ -103,7 +93,7 @@ func create_and_join_debug_match() -> void: else: current_match_id = response.payload -func predict_tick_and_broadcast(delta): +func predict_tick_and_broadcast(delta) -> float: delta_counter += delta # New tick (60 tick rate), broadcast player inputs @@ -111,10 +101,7 @@ func predict_tick_and_broadcast(delta): if delta_counter >= tick_time: predicted_tick += 1 delta_counter -= tick_time - var pos = get_node("../Player").position + var pos = %Player.position var json_string = JSON.stringify({"x": pos.x, "y": pos.y}) nakama_socket.send_match_state_async(current_match_id, 0, json_string) - bullet_lerp_factor = delta_counter / tick_time - -func _on_child_exiting_tree(node: DanmakuBullet): - bullets.erase(node) + return delta_counter / tick_time diff --git a/client/danmaku!/network_manager.gd.uid b/client/danmaku!/network_manager.gd.uid new file mode 100644 index 0000000..8d9b13d --- /dev/null +++ b/client/danmaku!/network_manager.gd.uid @@ -0,0 +1 @@ +uid://cd67rrch5h4t7 diff --git a/client/danmaku!/player.gd b/client/danmaku!/player.gd index 2e13b85..fde37fb 100644 --- a/client/danmaku!/player.gd +++ b/client/danmaku!/player.gd @@ -8,9 +8,7 @@ var collision: DanmakuCircle = DanmakuCircle.new() # This is temporary, it should be defined per-sprite when I get to the skin system const PLAYER_BODY_WIDTH_MULTIPLIER = 0.18 -# Temp -var flash_timer = 0.0 -var flashing = false +var alive: bool = true func _ready() -> void: $BodySprite.scale_sprite(PLAYER_BODY_WIDTH_MULTIPLIER) @@ -23,15 +21,12 @@ func get_input(): velocity = Input.get_vector("Left", "Right", "Up", "Down") * speed -func _physics_process(delta: float): - # Temp - if flashing: - flash_timer -= delta - $BodySprite.modulate = Color(1, 1, 1, 1) - flashing = false - +func _physics_process(delta: float): get_input() + if !alive: + return + # Bounds checking var attempted_position := position + (velocity * delta) attempted_position = attempted_position.clamp(Vector2(0, 0), Globals.SERVER_SIZE) @@ -47,7 +42,16 @@ func set_position_data(pos: Vector2, hurtcircle_scale_multiplier): $HurtcircleSprite.scale_sprite(hurtcircle_scale_multiplier*2) func kill(): - # Temp - $BodySprite.modulate = Color(1, 0, 0, 1) - flash_timer = 0.5 - flashing = true + if alive == false: + return + + alive = false + $AudioStreamPlayer.play() + self.hide() + +func resurrect(): + if alive == true: + return + + alive = true + self.show() diff --git a/client/danmaku!/player.gd.uid b/client/danmaku!/player.gd.uid new file mode 100644 index 0000000..aa633ce --- /dev/null +++ b/client/danmaku!/player.gd.uid @@ -0,0 +1 @@ +uid://bhwiun72wpk6e diff --git a/client/danmaku!/player.tscn b/client/danmaku!/player.tscn index e49a7e2..2017661 100644 --- a/client/danmaku!/player.tscn +++ b/client/danmaku!/player.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://cd3tqt7hr5pqs"] +[gd_scene load_steps=6 format=3 uid="uid://cd3tqt7hr5pqs"] -[ext_resource type="Script" path="res://danmaku!/player.gd" id="1_r7xhp"] +[ext_resource type="Script" uid="uid://bhwiun72wpk6e" path="res://danmaku!/player.gd" id="1_r7xhp"] [ext_resource type="Texture2D" uid="uid://bs3fntlmlqpt2" path="res://icon.svg" id="2_04s0l"] [ext_resource type="Texture2D" uid="uid://c3deywcu4du2b" path="res://test-collision.png" id="3_gf44i"] -[ext_resource type="Script" path="res://danmaku!/ScalableSprite2D.gd" id="3_u0x7w"] +[ext_resource type="Script" uid="uid://v6jris184o8u" path="res://danmaku!/ScalableSprite2D.gd" id="3_u0x7w"] +[ext_resource type="AudioStream" uid="uid://c5n7x6q67tp78" path="res://test-death-noise.mp3" id="5_poktv"] [node name="Player" type="Node2D"] script = ExtResource("1_r7xhp") @@ -15,3 +16,7 @@ script = ExtResource("3_u0x7w") [node name="HurtcircleSprite" type="Sprite2D" parent="."] texture = ExtResource("3_gf44i") script = ExtResource("3_u0x7w") + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +stream = ExtResource("5_poktv") +volume_db = -20.0 diff --git a/client/danmaku!/scaling.gd.uid b/client/danmaku!/scaling.gd.uid new file mode 100644 index 0000000..2c74b5a --- /dev/null +++ b/client/danmaku!/scaling.gd.uid @@ -0,0 +1 @@ +uid://ggkxv1cb1bjk diff --git a/client/danmaku-shared.gdextension.uid b/client/danmaku-shared.gdextension.uid new file mode 100644 index 0000000..2ca77c8 --- /dev/null +++ b/client/danmaku-shared.gdextension.uid @@ -0,0 +1 @@ +uid://b4v43qwewcg37 diff --git a/client/project.godot b/client/project.godot index 06843f6..07dd999 100644 --- a/client/project.godot +++ b/client/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="danmaku!" run/main_scene="res://danmaku!/Game.tscn" -config/features=PackedStringArray("4.3", "Forward Plus") +config/features=PackedStringArray("4.4", "Forward Plus") config/icon="res://icon.svg" [autoload] diff --git a/client/test-death-noise.mp3 b/client/test-death-noise.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..86525a07449dd8e59bd75f1da6f3c32297209236 GIT binary patch literal 37632 zcmeFYWmKG7mo-{=0R;F_QT0(Cj( z0stU9yG;pI2}h|B9{&W0EpmVCkgxyq9?ex?`GiPKfxwI_w94@5&w~+ zchIVApMm97@3MB5aFX=I5x3pE?7u)`4s>e zHEdLvp*L?fYf;GS+_HXe5Qh>H=Yne>(BeLr1Von0Y_j7RPIx(r#L43tdV!0~oqP)a z%~Xk1IOi?ll!@n)sO-wEuHJYs#zrUg3fH~AT#!@|IDa&ELn|c}ofo0>k_&~800f|e z0KotE75bZh0AlTHWmJ3RiF7l{7;_0xM#>Jt~cm;Pw&T3Jm$Jt_O{(n$m#(D$ZgBV zCec94ajp3Bg*cyJ!W=;weE-E>!hZ`j9s2l2F?FY3?WcRY`_vr)*nHkrC&W_v?EggN zd~y%!oyc!cpgVbjn0v^ zv@w79PkyyB6ZBj{Eo@bY)1nmG_+Y=C{vKB=w<%HTRsg`C4y}j8snL)Y$2@Ixw*sFCBlcCK(l^&iYW&TrJW0KhZcamw zJ2pGi$mDoOjp}nX1+0pA2r>y1B)s>=7FT!Y^-K0a+uxu~1=Ykw4V*ZlXNg zmp z!?Q-6_VB^#s$xBe5`P)ZKm3=mtgC_uVVd;%<{EQ<=l>@uszm1LA zo)Pjs)3FGBZ&AD|g}fRR@p{lG5-Z4R!)o&5TH#L3n$K~!(R~ONJ^JJ83k*aC?|aG* z@$FT}0!@_Iu~JIma}4U8zoh5aX0y4t^!oy3s0Q=G-QbTwvAE&bsK*h%|NnuV1FMVu z;lJ+XmbC@Hlon&BBB*#ca?m9K22x+qspjSli1cx2qL2fS$*r;EMGn)KHmKz5fMpHX zlD)i~ss)h{i8yC59_l+4$`1<_!l4@2Kn4+UN0T7vfR(V1`ikScD|BC=r$}SpQ7tw1 zSH`n46q0<5Fs6=S{x7j~{#bOUgeubJc=NiYDwRMwb|R>7N6QjLKmaC*4a$cVBL+faPFLA+i>)BNJB6`Yx%&%ddF9^t?c898~jqV_#$BT2bG}-8-qteuQe5kZP1JX#|{1c)I_~=WN%=x z(EQ>3y2f}2m9{b*z9Q#zGp@d3Fz%DpAt2Rr)xsMSujeo^Nn-?BpqrpX4zQnhtYA$MD2=dT}^zS_^`LTA|P$tt4?X_;d@$cDG|q70*4>NTJCq5 zkE<+u*ATAs)qmniXM?+gQWE^F>6R>iVHDS#ZL zjgtq3PQy47WG@@;=fIOcVCvRr3;I12cKW`oTP8L!o0gk41wRad3mI=GH!$8t;1y@vV~79Gsq?lm8zvrS{na*@>Lgti}0ZbQThJZ!%_m={Vri zbOcGQ`TgQ5%TOYTl%_W+(r+i2jS@azywgB?SCPSLH+~`|#U1A%IRjNiqC2apf~63) z=w}!NolxXaf9}eCSUOT~*UBo?xz+;8D6TJEve8BzwZQlBFyz)$HuGTocp>pc4%$1a zMm$h$n9J|4DF|Q7ccEV{gjI1th3hJpYRO_F!be9o^wJKCyV{WZ*Gxc{Gb^~q-TY}v z@&4k=;B;DHvWB#Te2O=6;moCZfAj}0M0o3fmSsT2>b)U|VR1~?ElNDxS=WN@Cmk2I zq%WfyBpzLEjCUZcy2ZsMwaKdO>jUz{?6sqIMYeMX?gmTq*z#Rj@I(1&R^#FE!}@Ca zQSR&^uMjE)1{sbrQy6qApH<>$-WlS6Hd%&(FF7KRfb0)dT0#ZznQ>!-uB_U+KLlDH zKE65heLICBs}I8CZKJj4pbWjW@??g!?v5GLtLh z()YIV#a;JgG5_4GKA0OaWP-ATuR1H*VXiGqY%lE zA~DbHZrJq{tIqBaA|8gX?`QQKA0zB?(!MQb2w4MP>##|KtUeT0 zCtH}Q55pZ6z&Z?7$Sqa1bcXmdn!{i9rVTbxxq6u6r05*dJn(f*99fE zr%Sbj{-rF^$A_;KJ_tFY1VrRWGRp#|hB4*R+t?f;#3~Ku=3%5{0H#8cr&b2@>EU6s zHu~K474pSYc?m_?hDOKAYFtoYVI?{2d=IeP!;rgF4O z8%G#5zKF`*oyXTvra&q3qL6Ii=tMy3jA&);_(6-0-(uDj!}=foCDIMPO%GCDt5syP zfC`jGW1OP$!uCeXT#*Jb$WB`Y$ypL>bcVuM4G$hy6*CxKT@gM#CzLk zPV>#1w5z#rCX(JR-T`O^OJPpX!E*Mjlm@8CEOr zT%CuytVLhSuZD7~zGV^-MBsS7Z`#*9UxwVt&1T#dQ9GfD4FI5f4*iOWTD7tn+@i*Z zA9L!FjC_C#*E?7+l5r6Wlkzq((~U1Wg;Q=}*rY_jG2AFT-0j+KYM+5tNqKLR;6HJ+qByB=jHqaiVP>;18*vv$IP0WoyVh zxotkk@$hABq1A@)jt<;|^99|HU&7?8GgacVSG(b8Q8wL6@`!UJmChhRGU+$6Q65r% zEyI8C?<2CQVnslp870C3+6o+Pvx$Qt8+lt)xHtzyO?hgG0mNDK`W8D4OVA`~8`~s| zzm1CiqTq^XXIaMx1Cc_Prb8zzw$&pM^!W~l)CvtUD$LK`pOPrJm|-Kmv+@1Bl4^-e zLh@EtBJ?wwgyZNiK$clvM{g;r@!LVd7lV9GXNzaQu}M^4NVB#|m>)lZ|=z0@u z`D=|&MyfNb=8Qm0sfOs+!@Sox(+{I3+|=P6*SYr@ueS4XkHcrGcSDyb6`SKBc>opP z7xW%xsZiY@UqW3>tc@!Q1%|=zWo4pQO=FCaK2Zm$t-pdWpfBQ+vM2N)W%U_fi9+Xn ziIPGa{5(n|4ZH4EZBw=BBr%}im^ZXU#4%SVnYzB8f6~|F|8Q==@yrt?1rme?cyGQeOG-Tm6)iu zTVE%#=uM;%_X|YkYd%$xS3s>j>M?Ut#7vqpGbPXv1r*d4L>CNCDp_23yxWgtNvN}!7c<}1xEaFtG#AKG*eg@g?{U4F|4 z(H~YfB*Y*&H9CEyF6;BQ28Qv7h@&X%#)cEOf~nMyG{wm<2MaVe1C(MQBM|ijhmiueYf{qD=IH2&b@um8(zqN zp@l5<%%f?GC}GIdAXTaW0st!EhJR%nP-W0x+aJ_+gV#rj{Epw&c{6=^^@gJlQWum; z05Q3R563 z1Otey9yRVpD|(R@CuigICV_(kF(>x@j){3PnE9`c(7*LR=*j;eJWJLHJBDH*Hb$Np zhDd?*R;4Lre2AP$EXGkgbmGt@s52>ju}HzwvUCpVUmf{V06 zol9{3GVK#ajBH%TD5vY!PBHWAvQH*!CZknX!UI{>*z1s@hp*-8#muTR!qq+}D7CYuB{Rqj^`8Em*@y9=vJcMtY?lU(g9(A+0d9?Bs z1FBq8G_{^uCFstjp&b`94Vk|2x!dU)j&2K!sL!9Rcc0qX>^_=~FT*UeCwAC(@I}l1lmAD|6xlpL zp)}WEd5Zsn`OzKkAW5K)u4N6EHD@fQWBzv{(2RlzaEEBh_9zRm-T%|Acm8957>xsX9EP#59gM6O3L+3OnG2gc-v(nu-5c##Fg zWOc4jN7KW&1`UWAI;iCVUq9>CJh`@-#L_6{ElL)9rmiYc3INaxcdSwZ@2hHX=MD1j zvu%)Fx*dvChDtJ+%*}UYID1un4Hf4FiXkZuN-kbje9RvXD?b)1#LdnzuK+!B9N>>p zb4rQZ%k8mBrXVsJ;I3&z>|Ht#x%f0xr8Ejnz=ImhS?Gd{{(&6S?`wpGE?W{!^ky>0 zY2QUE(8kw`ID_0R zpuhkr_Wz^*X?mtA^b}eeV+v4EVftu^T`3QdUAYYefT1+Q$xTsJ%ZVVE zkFJ15__6)V5S0zZS5vMJrIS~=QjyI2X=3hiK3DI&)mt{s8SNf)6(M)w!t8!)-M^f) z2%yEkW2y)u;&puO3V1eUccqqmKl@~{LB;GJL@9@F9Lg7VU zJf@NYLxkCvXA1{AN*jK|Ic2_2nA-)|W4Sl%ex!$W&|Hg2Vqnx{#@+~l>PI8m82&^^ za-!PzbXA4|p@EcynYp1L(F<*;iaQUB{itLfzB(F{=nJnfB$32G#hi26c0>L{_x-7Q z&oq_r2zo+pilvsvPvP)x?iIsS4JhOSYdwY0_lBC8u4o}xu?>5(j7Ku}+h-^EiDLhE zG)XTG+Y(~rvdd;y_-coPSye`M>G4?t7gI5FuWvn4tE|g~>El&65xUKw!}f_#N3v z&I7T|%8bLU#b0mF>~C@TNZ{YjhJ;C_>w@U}MgvZtmgdW_2p4R(%wwjpd3d(^-DhUQ z7F<3Z8Jc0qK=fB0aj~Z9^Bg)8LFgLQXWP6(Tu6; zwtUV%5BNdD4bw9?K(&zojh3q{zPNh{ena558e49m$0PXrTy3~%Dx<fyAFX zEXpq5-lj_TP?18!`9R{cTH`+k^l$zVxGVKdZ4ihV)W_aqUmGq<;yCN*g>KaHn?v~2OJugZ0T1$RD_mv^y;Dz(=)VIvcKM#g??RV13e zEOBND#fddgL+${oq#$|ZG| z(@8nT+KxMLBY|2vd4sW+W;A5ByjE_uu)~pYqN0l%l_~WW^oUZY^dA zQUnZ(Y{sI(`7$6%-n|F`5rN@ue0|jHDhyfpV4)azKx=_ewmj5 z>N;VU?NG?1q9S}3G-;gUQv1Slq|~~;_xtJn&50D3oAd1utRpxz+Z+b)M_{<7;b92) znyk;w>pxH%89GHW4xnjx@#*}xf96MDFc;Zbr8mt9x3xF5cB#lN`Vu*kmwjev6dM($ z@YzNa7RX?cioscznGD;CEMj1W4w96ZqmY=yS#fkHuV*$k$CWj;TE$hf{d7Bd@qXOMm$1Z`H?(ulA1P3Q<)hbMh*aZ zc1UPTPHM{>56mp6Z;CqKhSz*WZ}{sh2}eGvJrdI5xZJCXEcz7{Arf}Sy|L{!sS=c7 zlOdH84^fHiej~i_+)Z$QwJv-dJjOn*shl?IRK>DUz-%v3=>v7r&G^Uu{L6o4XqfKj z6E~}lMw3Bkug5gKW5o095^NcNHkxCpYt~27fuVA1r@Ybq32ffD8Heck(e4i86rc~a zcSfb*Y~ITT4SQ^F40X!ws4V$R6ce2JT9}kMk`%^~XiP$DetM(L3)r6q27`=M-et$1 z;mO4Chx-GNQ=YFK5q?&Osl8#)?k5_#?hB|mls9(ds!0zQyeVAr^O(B5ElNGuHPa$k zAx6@74L-l+9q-|0AhCrL$NOQE3+Lq{kQT+4<#-5)=F9kX5OaU_i28TABdokL#O>^6fXTtx`eGT9ZV9_^#w1Hk_2C}Hgyk`k_%>q@)s*aG z#YrGJ^Z3$u%~#u&k+%u75!Cd*IizS(Y35Q@$ANe>g4G>JtM`w&8$!kLNoO*F@U3)1 zg!QCP1(^!s|L6~5y7rc}6+V&5;jtcp#lYx&scpONhuB)`tvzo?ZyAt|TAX^P=_DswYm@v_ z|6IMKAWGk?!us0uW2U?3;&rkQa&}({m8WYVWw&34&?>Em{L7T6`D%WHmD#nlTQ}Ko zkUbRIxw;=^utg%%EsKn7_wh??_q!Jr7CdhR!q7hZRog)mNDpv>UKJ~b1ast+9%Y;- z=u-X6(KWDQU8hXx_C1v2*|WL5YADpv&qLJcBUq=c_I}dHq!glb?-q)W;<8g8-WvJE zbNN0l(zmq(5_!**zt7D#vjTU*!AFY3cPGCiBZMB)dC9y03(vANwOhU2hBz_IZxWLv1!sdS$;> zMWTuO+%8t{cND9!1nu0&-?170ie>(C;Gh%o)S5rgmMsaT;r0z(j>nD9i$Q?=e3sqJ zyzvpAbAP9FjH|(D8X*4s%aw~YP3fhKcE<$LYxW-n!~|C_TV5XPG({GtkhKc?_$2u~ zufk>5_DOlDUk(gFAmyf0n4)n71%PNUfrTO!;kXp!DuD21hhXsMGVb77iHa2fkiNrF zEhgE5Z8WRg9C?@9fu{Q=_Ne;t;hCNcyRdhy=k*Qsn=v~n`IN6ae)i3fJTV!C2z;L5 zTu=ZOOQRD99In<&P=#5By7JRAN$dBEUy6@_zZT)&`tPM_x-U-LtR@Fy0hz{*xtK)c z!%RutD!F*MaQYntvc&wK>R;)XIsHL|Tp{yw(Q?j2vZ29bRhHAc^Ar9v*+)8U zZ<>KwcDy$}m4@V^m|g{b_x%;ubB%IaCyWDC#Z5!~r!sw&4}R9*P3-rXAet^>I^f$9 zEybuScOb-HE%7ju$vHT2o_DLDOG4%$FrABF^4oc*-!JYs>-sw@vGl@(j38<@1)g;S zIa#CXrodiCNT)hPsD2^T*3!CP#}b{?*sXikj;U?y}*>6|k4rMph^1K_{o9}zK9 z@7w+`?zN`8fg-3NarF4cS1bl)M)&zorU2{{6w#piwz1*%eKSXZ0@di(l&LPGP83 z;b;4Zxk)Rv;rcwWNsn-^g*j^d#ua<5^Mt$}Klu36=)&{jM8BWmM;I_VF+KxYS;`=C zmSRY8f&YK_pCp!zX-o0}b=FQ6P~VTp8M`p?W?wDZTUeb!=)}*$u{=u@I-}UL;O&#^9^4bo5^)qp&C>c@WNJ zv>dIIE1_O0hGd?#3Ok4HtcRu)0A2BGRnM_R`Q@I(BjFYTnf|eYsNJ)eiM6v-XL8SvWAK_c)IT}Kvo0}jb*}Vn zGxEKCY-IJA`-f(-iyEwz600*=BDYNY8yTCc)8Y>_?Bjs*CS017Zh{Vjb4$~OgFTS~ zI4LG__6fW|dPI%+aDxPrkh^dz-SxgMsa!dGW;x_Yg7inJeZKZ%EWN$C9D>}vfl8nF z7mTKG0X!<36hrx~_*Wnx+r`u^dw_*S--P~9Gjs_3$QA}bgesdcLd!gp zbAFE#v#~Ls+sR5M2VsvyPN)okTF~Apc%QoENnm&%$FfqJp}2a|!Xt1*uZG*0k%kK< zsRrTjv`6%aTxn9XZ?Oxo9g&4jJ55@fs=wR+f9=Bj(VsFAV^t&~e$Dbu42bFQX!==L zUS44Wl;j6*d>me}rWm4}(x!+q!Ic~9`;{@1h7V}m#pg_muh==mWpV(NrM9|cW3@x; zymKfrbiQ4g2YO@hqfh1ca8nC>=!9cP%FdJHc{!G-;kl)+y*9}n)Hq#Ar zvl3l~RKeF{zsHFOJQ+=4xK}E8MUUg;MpIg!E_rP&qHB`w+|6^#+S=9)ut>pc+tlxX z%e@|X;;xtDjal?A6ZWYHm%1;QDR5GTL{qQu{?N(Fl7OjU3KQMGzs>lRl7?n>Dv}Tw zs3g$?DP15?hzA3Z*B2zVx)?3m%GW%Nw8@D&p|Ux#ES`cdqJD>uF1_q&GIz7;p(trB zuHjb83v|WcWoj-0joB0hDbZQ?JQrEOetfbfxe5jS1&q(F|J{B@{^NgOFTdF!@w%%p z+EbF4eu&aF^phe{*0o@*bvKRUaiArI0MNM|?dsR+TL*B*ARH55szJTldYJz-BhoP6 zC#$sH@D>SLzTL|UNC{NAF$&!e?vf1Zk}t@uJUwZ6X;w3TM`tV;~YedW@U4$cjDFP-Y>frYe8(xTf0)@2WlGo7Atp(;VA2tWS*#jF> zPqL_d&xj~&&Py2@#I(QkU@0jgAsODJ1;68ry6OSoL9-@(xI4H^ttv3zM^Y+{HxGMt zbhhSw=|@0q=KJN=R}{W&QL0^tJ-dX5Ay6eaw zXXW8y*;BO=A0w|feuL-Gm=6G>Gf(WijLa%#;H_+W+@s$(O^4xpK1*BXuaB0_N>HM* z?BFjnihhuZ5cZ)Qb#WYdR@sT9(ahc2N9 z8~X`iop00C$t)6lxrHC;)Si+LU5T??^ai>*vOFPaD{YEhxDvm?M>+on1SKtv4rE>~Xf4}dVb zcUan8C_%Hsm(9gAVGMme#$u;uNK}_;2*w9Nn_`bgt{;S3mu6RnI^fjG_UbXvmq2t}J)q(E^-DoN(4aRW;#cc}4A1Ai< zpn;9OdFIy0=@<4$NXk!-!9Vd&#rlTLlz?baBc$b+G!<$VTgi0`c~sWTxbpY7sEl+ejpXBeOYfsFfjjaSuc1EPUM zOl>d88;8G=jYtR4Ub{s-kIq9lwx1xh(0zEe!U9}mqJ>QP!W-kNhwZr57uIg%@pq~5 zdg(-bSLa+{;t)hRH)ncU4~1CB#LCJ+URg9Xk{$D#A94GA`Y5m?56~sqNkJ>Q?#s(1`|jmtDL=0jC0aez<_Jhzo36;%iU!FP zv67p35{U$APIOHx_(TTJfJorhkMEu4aqK|dJa-caNo_>^aD@C)VpOWb^4?IqK0Z{? zRT19qnBh705CyYL$iAX?=}i33{OT-v%VO=cgwC-wF>? zce+|^uw+C}H@=)5)OfE+A~{TKz=f6`{c2i&({(TEZb%YO9s&~79F-TycksOw znTF7w`5uXaKYSsffS?XP*V~k$7MgaT4krfPNw+yUY$F@fKD%)|QxK08x|)vIRub43 z8%2FkCOqJ%4su3l!VIaCaAYV%M&3Ou9uSqFM9ag3%j`Y&j88qD2d->a`6{dvmlNzS zx+~lh$VF8YT8Hn}VT|I_E>s^cNWQwcIl;AuxuuN2A2@8gN{^;T>dps~LzqSs9SG*3 zPtlK}NBMP-d*Na__M!m>S1pC2GphK~YL^ zm`n+)Gte+7LpGaqr?kY50lV-ufzp)|E(UH}EL#A}UoGf=`A<~DNRBtEsFca77~>cu zrbDB@Zhc?`A8@VNYf1xfYz?238bfLCl)#?GXJie7!tF8OTPF{P@i{Hh+7EvRI=uBFghKldN)!zvPmAAC*8to7U1`uR_ zz5e1-hk1SUdjh)UfsAii^Q&}%(JdI=4x}3<-*J&8t(AhthuEf9wTnU7QLM$GEi>ck zH{Fwfn;bFI4Ie3ZT!IUia<#o}y6K;XB<>95n_1p_Z&g^Aq$L>yStW;{NvTI_3L?0` z5d0--#CGXs9u_<-)WAoo<|XbN+8VXkiW@%50nq`SlN^almB$3mRo2`gDzWpoXR%>& zn9N&!mzmyA6rsCb1%B0Ie@hZ-KYu>SqDS@yRSJ!Um5bXYz*a0CNWagz8&~%Ng_))-)If7>$O+!t1lgS~3Ua`5!-N+D~@FpRNZGdv%yh zJ7sXrP^^3BQ%EVihe%a+MLMB3eFBmXMmDIgT^dnA-ww;L$rnmw+cEw)l()wpZS8L@ z&5l{Bi)ShlOmpI{Q_W~`Uy@BRRWP<4(fAAZj>M<1(8G9{E5}?@Nj?ZfhZr||zre^6pa+4C7oiU(vGziPoGkW6Rtei{-Hd+EM z6^&X&!ftHcj#xpwkB3HSFzeKiArasi|2D&t4r5bka{hKgR{2(JE~5|C61BHPRMt1< zAy}@i$q)XG7sWq#68x(781dlfF!3_}ZWxc@W}BEEpwJz5ouy$9L5 zIv`gx&3=I6d>&aJ2Kl;8lg|0|*sL87A=PWM0!pa?VX>)+EHOu2QO)3+h8D-x^_vF* zu`=7ko zVp!{#O)L2*5?fOc8CC2_&&QR;;XlTkTQ1GFt{iz6@}~PBzX)Y>)O7t;$2SUG{12@6 zzhVC>egpiGg8_(t>Mw4Kmxd}fuq-+folgns0hOmhRTL1jC7oiR1R>3)*Fs9asY#2P z3`=i7*`u=Yy0Wo)^{Nu>d5i-ZJJ3}CC7b{k!8Wvy#&4f?WwzJYPF^eNUuEvop#T5N z^|$^YASR2h9WYQ8$nENNJSh;mS3ueG#4*?N<>Q|} z(!DX%%UED3KXn5+q9kjXq_!~w%i4cuWy#ZbSZWL03zX;Bczu>2U1@Wq;tpRG)O|j{B+#-kjrI?DaHIY*k2lNzUs+f#bi(Q<{Gz1g< zq$rH;`GGHu)mGkVNGQFgNh0F@{o|`6Q;-9y1&4}g;X#vYFpU@l5j~)-s(rupi*%Dq ztWAH0bO?Z{!jrd|9$S;!#KfStg1+W!uG{3BF(r}MSbVCc6G0#Q!+GOhEobXBTyrOD zQ)T=wPTKij^|6)|mY)0-AP8FimSZPy?wxmu_pImMZgr!$J!&EMiX7Rws(tx$pQ@g< zDpGKL4Xrcu8bTE!N5G?R>1f0vo1_aEuGnX@j6y-(d=Uexj1e@pl_>p})&rj6`2W2A z)L-$h)(&W1B&t}g=FnMgLwjxYMgGeJ`Z|PiEV`LFVZO zZPOcctClX4GSI_2V%YPQ*>oKtlZnVvMxB=nuiIYB^Sj`9-qu>pULIJ+8L;Bfh|A7C z%a*n29en``-R8K3Q7~vT<;A@nFn>aI4TukWng$BA_#{okwvhY041a1vAUV)5Cz_2- zrh9ZZYK`MO4yF`WBjrQ&=x8oZ(^5i;ZZ^UUB>H z&d;Am;*bA#eziJnPd=@T=JO7V_Pspdr;SvndxYG_=lhmqxYC?p0Nz_P&E);iNP*3v zXu;<<^mj3MO_3{(9QMPD<&Y|Wb9fu4(t~F zt79W|Y}GtVxFMxaLz)1*bObXU(72p5T$Kb+=+E zlau4=)>X!R>{RVH9%}3l8<{NlbK~aX$Ed6KluGZGVtxzx0j*l3Qstc^S= zYrj|q84?io`;~iinyD{21)`S0U%HG^-jdRZF!&mN=}K1D(vzCL&0q^s`1-)S8;pmjK$Az+3G2r<(&HZh13R%-Crp4Qrd5@I_!x5i&5~T{0UP#!_;= z6hera$rvzwn}H#xP#LUQn<(P>6$c z8S!6H|Mvp?!@sNMt5ubLqFR;Lt9noRkI`9T%s}#vR4&*4TSAtumMQ?Co{q(|^p3p% z>evy}C-R8v0--@xmrFFq!Nm~KClhePk`iNc2pmt>cO3LZMDHaz{Hy@u8&>?~^iIyZ zFtfHkLMtI1LxEB>Qk|I$4TAMRsfZQ+tm&5z{9O z5shWVsKkqyih+H#J#}f05l~>_J`jDH^Vej05Y}3Bej+Kl{}8XfG*VEUih~$|P62-m zuLeIY9q&Z+mc+ON4Gj_=I!o10Wi8dq5FG(Fxy*St!@}`x;}(8_C5GkMybPQETY`+I zQLBGBp1*1T_m4mEAOCVikcKKz9j)U9o#iaF^wvr$-LqFUmo2+DOZO=usplb2GbCQo zekRk__sjOLw;YwqX2>rX4Ic`4IwSasjv#y_BClM=;#>KoswWp9nCt#asKu8W(gJnr zDBQQ2S(PoBZhxJA<(*$gr8AtfyIahCYBG3 zXB8q-DQEM2pFuO_nJ$J9o7?u1=Y#)8Z*7Q!warEA$F}zAxvMYwjb=Pk>qaa~-werl zpT3fs27+2SXQ$_LeFs}LDJF?rf?N=Y2hNY~(EBzMGh^q8P zkRKT{Wmle$4gqh|VRq*2!0(;JjW+2hk~d^K7nlLN#@|OtzYXNbe4tY&P`rHjcrExN zt%PVi{yt%iLE(+bU0mRQ3|~Fn^56U;OB4y|j6_h_4qvk>fRb6Lb5>*E3?*1IHgH7@5amkAcY0ogGGfM@{tVm+q4lxIJF4_U&? zv7sgn$A~s9Fg8E`e%OFRSUrQ%Z%MmJvjMQgVd)oHiPl@q=v&VFc4{uu^{^x@niM>~ zgC9eo5UH;kR+$Gtc_F47a+tc1pXiW`Yrs95gXzX@!vzT+B@kCZlLbL!WMj&(OBVT- zQ*b-esNNgd!bY3OA5QJ$Ob{i}cBxuV2-BF$Gfu5)W;>2Gc#bami`1-XNCuNQPEqW#S4M3LG+J zA>k1hD$HCJtYY2^(WEt&`>#akZ~hSxBSdrsY5D6_tudjbv@rAuq`&w_P|dS{I{!<8 z`?O#Cjh^A0B!%wXd0mTPd>6h2tn62$o-0xyEQDk}LDrXx=_@;1fkL546RKE|3)+mCp!0m6rB`}I^Dt#RSW=N^w&JP!Yj__&qr2nj(sH& zI8dyq_^l@hs}64hU2>3!8ij5al8P%eSGZTm8;PGeQ+d15ox@rekR4-HvhdzH z^;9e)2q5fbM>^Y^GaCi3sxVQy6ruqUtvz3fY8G8*Eo!j`AM(cFeYW#^zA^ghV8Svr zO1OIrzYki1XWbY>&{<RtGSyaNo7ZN`F{$iBuZ;7zU8o}eRf&RCTIlhx`u38bk ztmzCuU;#96{wk;~PELYt*YzPzvvAi2fAD`*V1p-lF2?XmVm)Q0uO;F2VYZ?4>95_l z*eXmtVRes_6-8~m4qnOO4fHZOFvUk7BSa7n15gh1WYaPH@yJQ)5(}AWFkXO4!{gOn zvd9NBjO6=2#^4Qp@6n=?)Jc(Q+|0A4WS217cJACACYDqUAme*h#PD-pi$B(BrA#nE z&e;MOgFX@CW)eyzVEJD!z(4#;_=@RH_w!3Af4)~F=?};Fefvg|&Td5c8S8RC4rXL9 z03e@@SR`FiJoQ|dYYQaEDI726PlzQn`kt}(v%i>Sa4!x0Jf@ivHIlEwJutH_)q+^R zH6*|F7|AbMU-e@9VB4F;961$_oG#TQD+87DY4NiE(1MbE!Fe8Zzl6=19)gD z^_9HJM_tQ3gq(ojP5S-+M=AD4{{uvqNyf>0l=to#xsc{Dz;H8I@H+$zs*3b}9CQHU zM*sknfi#~c^k%a}vzXd@maCPGCM~;(&A#5aa}%D;!9~(YeH9mruS(G^L&NP7s8W-G zw-xPc$Z9Yu`dOr`Wp>578~my#+!d9RQyU1teg5tLYVR$h+UmN!@8GV*izc{xakt=Z z#R)E@xLa{64#AE2%i#u(hK=GH$&NmRB!)H1XC)J}YF|jY;_buq-wlrN<>smJ_^;&ys$=WB}s-@?5&| zO^MI$i!`(9icxv3IeC?)v@fC_;y-#hSziyShSDbn0~jRmtBPQp$;T^+R#6#5Sj8AZ zws|`*@ueKC($(Pzdio+{}>5< z!7Y9X4f!!LQjoo(|F!u$+ElWZyy{2K zi;^rnkx5RqEYPEg%f$I{aet<8T{ES~uu=H`>Xb%wo*Exc){S%JQPRnf126eHNk7Xm zaNV&71V2TTV)zm2?VbK35JYaB@rOa~SAzNi8dejy!f8XgNmAf*4THle9=z=)55x+R z=lR=$lJ@I9*6BpnvZOD$`zlGs=Bv^gJ7`xp346dX`L{g;S+0s-9wgT4MVOG7)L zRPypj9}K8SU&fi(2FH+#7qV^snq@52RrK}nI~zpI!3Zh$njpVfsh;rcxS~3vIrdQ0 zPN;fPQcH0gF6~~z05SkjfskB5;2@WNZbGP?4Rg<%B!2xoYWcH3RNpoO8Y94;V0P0m z*=?8MwTQJj(MSJnh62EPkEIZbbyZ6>g!P1+Jp*JfTjCx+pSJor%9z)dr%HY9?^Dq~ z^*=;xX_|&IRI}89nG_VIaD3||lW7T6$UnAC!GS}2S}Wt{z^$+#7vxlAHGAyq#B6)& zFgD2*7-GA}q9n6-AbJ+WE5PVD*t~KjAW*ofh{IE4YfYcDj+CdeX*wW~W9r9hI@xzz z8Uk8!+==+7YmZFpfSLG#7!i`aBHgri@&?B=8ZdAj@XW#iwNe%cruPmj!iU$YbX&T| zA3?4FvdApmX=!i34-4;>?ZRqY$RQQ~9{qi%wOqnuHd zNr1)xNc>233GHy%+qMaV=M~v$%zn4_1beH?RVm1M+uG(wTt{}5@N_&zxRFVS0@`;F ziKmF}0+_Z+6N*uy&LaCkyELo`#7n{NB?|`G;BM<%;b}%oUYTz|rrz|~@saJX;rozW zXAg42R&9m)PqxMGXk=XCU~U~1sOLjYIF7BT_QB*S6ig%SdIo-yBld6Li{8a&I4Vdm z5u5r55u)njRkc}Y6*b6=v5rbUrzp4Tyt)~!o8(@e@zk{tVOtLtV8NNacJpz3C1c1a z(M}&rz5O(w4Mv?nf~{sNdX6dyJEM}(aCWVRqPHj^TU$Nvb=6W<(6rLz&v-w=&*;*? zp{^v1Nt?rk{dFmu^F7LI86O(5l&c(`rLzhxFshH-W@a+tvwxKh{hdFuRE@BnAQRb{ zM&!yB7YL^4ePRfya~R>LsIpDUrg_Shy(}=Zi~Pj!YVOo=QAiG6j8+cNBlKL(NrR+H zTSr@yg1`VKar0FOU1q;P^;t+^*ntIgq!-BnqK&UTVEn7bDti=K+5) zwx5VoEz&kEk<}jj9#XNk8<&7Ks^ij=}VE%}A#Ll(sN2!oVP*E5riPx%3 zXjBxv9!is!8^N#9YA$-%Vb@#FShi{N2`_beVDlrtpo?1jPsHlik@&(ALyAwsXbzw_ zo1G@Au}I@A6v6&fbk98zCYuGKEUK^_;kLqLpK`%RQ%U9dn#9_tFqeM)3R;TN>fne< z9Z%j&?M}WzRjPauPEgv!OYpVVF%u4{9bn&?ewfcPF$bnosvy>o$+dywdp@%b#KlD; zL6DSY3x`W?-;JY|$aV&Y9rO}$e7A-as=tZF75uzS%G@EScsTS8diT11!^g+07X!CQ zV+aL420)K+ahd6Pv0hFguxezu=_=6Gdoi{L$3@)|k<-p8E=@$k8uk+xM}{H<2@o~c zvp~t&L!uhXZDz1RiR+2VR?OQgC#LG<{pw^gt?ZJJLj;M7W`F#*dfWf*yZ9ac)6*aN z?;0VYHyus(RTIVckk)!wk@w1CC$7gphU&=d63b`%>2pf%fC~a#gSFy!4dsK`eVj%Z zwB(1$Q9^NtMOscgup}wgO#9S95eyc9OJwitYp9q6km@?H>P~c?$XGs z4kV3O{rbb~=?l@O0O{DW24%BOp7Bt~4WZ&22)rhqAhc74B)UXblp7_NuE>f}!KREW zn>kX6VnaE#S$nv&c`5gNlhB|`^I5_nctTMqjyH9{N`1`v7eLV&fM;Naw!fs}<4lDP zbdj@B2%+9|w#jBPMYSs3bsgyo!2f}{Q?PwC->J+A`>{jig~6Q2P7sHC<9>bdQXpY@ zZTE<+Ta-`gt7JC2GYzipfFJF13tC3rv}18$g=RkW^p?&rhJo2_?yLOo$N8VoZ0R2k z=ff|&=DXz)5jA<%q2wF+GaLt@;$craLu*ApW_nj zwh#=>k~B5W(8)f@t*;gKJ1xp83Rj4JdHwHyF4ZCGp^wr|2&v|%k1NntAx+%BL4Ddy zv>k41;>G$QbiSQPvDSK5rU;pkqTw|Xnl+G8qPE6yUt^1QE}G<`KyDLyY-Q~1Q>|)Q?s2)wyvMfG#Q97nN4%x!Pz*(9xx@o28#~YO zPKkjPz1sq-=*8G7OvkyD!1AMuVp>F*QK zKm1=ROl)DAo)TW2A#0Dee@cn`QFoWb8%=@Qfjk9!3XR~&UDe@wU6t%J?Vj70eVhXm zvh|+H$c}zRG4sItC`h`shs#_IzeO~@!Udq$-XBd!ZjCh61+u;raIT*~R;5_ZD)cWB-CO7Jnh>XjO13_$Gsm?;h zXJ^Fu32}MzkvVIePzQ~HO8FdQKPrqYnI3GS5@qYs%Dk_o&xM~1kqZzNvr2orB&pt3B?&4A;gILnW*TsU-x z{O=I(JAX{6aIpoF5wcB{$eTr4>jjKx1WlVd*ItwS3WX>A0U)je;u4)KuGyxKBo6x$ zSvyp4fpGBlhr>7|W>K6P7$KUPlnvY(YSA>&Xy3oh&3>IkO5e6R3_>rS(%7wIrlVO7h%7C^4!E<4Zbh zjmyz~Z;&-MZcCzw3C9HR;)j^{>(jge3#7e?66^Ap=Y-tAt;#TL^G! z#Ng|MNHrBYNHC$nl^w`TAJd&3Oi@VTnig_bQ}T+lo7VOf9h}!tVNEL|fXL8zy|AqV z6_a<}Pm2}tU}27Y9GW|yTsV=D0UHPwl5u8$9DzG0;%GSqy$`%A8 zfr0xt$7^4dMJ|1v=n!gc@|L08T)=*vaHb z3T5B>-k>%-C@OsI$4z@VnPFBiQ4m#LWAIx+QLq$=)HgaZK0&d0h3lV~f^@RBlQYq& zcK86qHU%a{QFIK$@yU#crGw{bK-3~orz7n6jKHTNt@ClGy^T!j*X6SWQM6o|1v3DC z0en5pY@!v4^!B9=Q)MaWtoQr&*Pp@8$F;`pi<};=t`*9tp9nDIS>~n z2yNq7%qF#3C6j}u&J1E1Oy)m`*$ecJ)9zto!@-&LqAPmpO1t>cPAY@(c&coqiV$Q< z@DuG^fl{Hxas!zB2C^9eiDHgU$5v^KFC|~|uugtWhCsqpynl|EqK9IZlt5&Eb)LRV zkfP_u`Mc&kMZf=g{LUZwEJ#$R4MZfRAzx9%1!@>+y3pN*IrQ@9R|F@;YCZAS3`RvP zMBdHHO9#p-i6@rWDlloJQ3LsrT6}QlMivTSBSz71P3+X_T=7jcRTl4{1)W4mg4PhD z9}QGLZki^3?LstcV6g$##N&Ry0IUe=O^YV;Ipw}P3n9D7X4HDR0PpvqWV!42qoa;wY@Q`Y2(7A z;a%K=_jLyyYDlBP9j;@y6xGE7=p6SJCgRDXH^X{arAu^nM)#xr8kb6hB{xWgM*el! z{X2g|*GLgv4dmzUYNab5pY$gda;v!=w=%6%a5$HgZHW400I*_6PhwlYiOI6j*6=X# zwgI;=qLdtJDb;``q=44(ohB4ftDjj7P*J0D@4Y1S0mX>ZUV$&c3VY-%aa$)gU7RDi z%F~i-ij}T=Y~2h1NZs$5+0WEz3~(}jwnYnfSEmw02c+S7|MoS4&7Fbkc5SFEg$U#P zQubutz6|9~&Eej*V@mBo4!cG9rBT&h6Vwb$qD5emhl2BEB`Nr61R%?fu~g^cuqr}6 z&+q&9g(8L1BE}Jr;E36Q$nZbpNXT(I$#eKZA)p!7MujDfG5|wlvvP=sXp{C6m&q>B@q|nr8e2|vKQ+c=* ze(DxoD!2NIf>F(p*s*hwQ3wXJwQToH9bP#;1qW0%Y?7mnD-Rw2&J?{6;`H%-0> z>AxnE)ZYiCjLJ{0O5UVTyM;o*v9)@(;Oqhe<>LI@ktFgmp}iLqT3=$X@3bx5HQ}&? z_Dcp55u*C>bLwc+JJF3n1=IjAcnUd$G6_+GJ^fL0AvFyS6;l@+pJiGtv#HW_Y;PEi zo*VNSoS`C898-7vvLTX{sXW7#F;6>ljE+A}ecW9Tb92WtMlNP)2E9E z*Esve1bNI%PIWw9B_eknFW>O6p89ttq*fMHydpYjxbJ9#0gTD1D#YQLDt)rT!oo^? zd}^-zH^vOygjg~WE_guK-4@?5tPX4>m6vBX4)+|DI|?H@8J8wx5!Q)g8{K>uvGIh2 z9fxAikyXg#AQLs1Q7X{6-_`?v%|GGORX$C>Bf-}=XuA>VGf@oQqn@bi?WN4Mq6@)< z!+PpI$j%6s)hff)#lR0EFH0VJQ?MzUP8h;Ofy*KD1i6V0qwgN&l+bypii+ zd1kkQPEI3%{L!Lpe5I0*-oCOKH2w%%<*`6ql=M~?2}VSBRYm`WyXwYHYA*G*W<^F* z#@xm#zGip49JaNS+hSr-lRvA@Vnfz6=ifM~6L6uUqr;P5KW|b|**M{#=Sl-=<@I|g#HFAHG=J6e>rqvN zcc|vNEnV#1lhna!-d z;=p>$i5xrTp2W4b4uxZ? zhML~^bj@RsC;>a20xbu#R451x+ zw7uE+@9EBe>Hc2_fAQy#d{a(>FSGSaQ=48jMg;{`HOz9vwuU;qFb)n4Qm|>(feHO& z<^5VQs5B^Q=l#*gMLM@Jc{bF%yg)hayn!3jJGKTztZ}#G>$Z)12NZ*hSM~d;{Y`7g z^(>C}?0akL1c6N<+?*xet;0{bXA+lezOK09bNbusUE5&KG& z*e*ryNTA-${rQibpJm@t0;fOu$s!R~DRVhf%IwF83AM~2D-rLCPJ#GU^3UIHblyUD z&9?~P>iqR}n39vZPNj#iV~&?zXr)sI>M&<5L5>t3v9GU*nbFtxbYEx(B}_f_0_4o$TqzxA6k>CgDd_bK_j@wt9^axseU79((7DOUDnyi8;! z>GX#DPPL2Lf0i<7g060oNsU6#!&a1K-Wnbrg5~BGayr17`oX!KVO1$}6^Gptn>(LY54iY!- zKlnW?y&Sr&_V%j|x_S(nYxMPaaceoSJ>^{!$SD7Y`0iy~W-Dy1RaaiA&kE2z zf|PbZJGAs}fY^PsOi8WJwU#==5n24p-EOQW{G-n@Pb)L9lha{_(R7CGKND_AQ+vt& zR`&3h`~Q*uL7l;V-ak{w z>Sh3dbJ$6TV^x*>=5<9uL(6c3Utns={q>bd=hgvQZ#6NL1WNh{8w)y0_v^U#veRQO ztYuO#M|9-j?thJ?YC^In~KkLMJOkZM8WxqEMkZCoCD1|gwiG$ zoGqkV4b-Wh-7~X>%St+<_F*fDKUW(K_uVD+q9Cegki+2k!DKR2c>#Ncm54vbf>MYaagDr6kv8>GQjqQ@PQI19VY={ZCa`&A_FO`i+^(m&^j(NV}Mo@qOc> z|1b*v)E_+rDU4_mc(su-YFiMzV!aL`JEGZ$e8Sfe$znkX|% z=jTwcTI9Jj6YuKmRQO~D3cpMe`aIRtddQ8**u6thAp*j$gRB7?<4vIh@$>8c-CfjA zvQ#TXGS~#!>&lN}0!^Pc3HxXCA~j{~JqM(Ks@$fGa%#+^Sh?Q*vK5dHtDwndx$@Vv z&{8!BcfgJQ0! z7ja9#X74k3|DJ~}E{oi!MmwZ&&_V0=t?Z0FX27Cwqw!@iMM-gDRG*R&mK&9X-^rWU z*ImCFjn7;V9k>Yj+<;z!pX^f7xLfCh!vLJY$gE?ar#&-J7^(5lqmSFZwVHblAQ1J+ z))7jYYxWgbHMAkGMe@XMrrqxiAR*&Et3GL1z!}Qc7)T$QILcn0GPJeH?<2&yZ5OBU+-I$L_|KIekp1L;DG zdpR)34|a4GJVr(hz-W8rd*C1&bg$HDllVnSP_C{w%7m%>SgBCb%VQ)^zLUM;@vKJn z%e=Oer_?=_>nZW8cfOU*djTwM2M7O*`oA>ie;)kd|9!7jdt(a8T_uqAE!2_qF`Nt# z6=AL;?u=&P?=S@<0PPB|o@?q{fFPtwCM&7SceZ$6Lxw4KyjcadM+e_wYe%G@(Z5SO zt2?XD$luV*{q~%7UH>&p?kC=eXKvy0h=F>|&zL^Ie@ho1d4Fii2;jPUc>MOGGq1YA z_$GxOjdd``QuL9Ef0SmtoPL_pljlWmy9q(1Shz`g%t2C&Wd1+c)a$;cooiAL5;!KMal=(A;LFqwm^{&O~sR<9R zKSqL7O?kGG=)fe4B9|}p1X8w~!xLq?yr-Hfm${#3D~&0n^DA}$XFYQe_MC`WrIKUCxBcsrjYAW8DU9Not_=N zP2)HH0W0QGhe*T=N&qlLoEx>v9+YV}%W_6?CKwLl>nw}!mzBSn=Co?Pc)?BWHfa;R z)628u#B0-OU_4|wik!xk0=iR4unOS2_+b*jYjF2s#WY#)!|s*V%kKar1+kg4fyKr= z3(=e_6q=e*3`Kk{cxxi9+LU+72eGItwm?LV6oH;!9H==&B+S#o!)3vh$;jk=Ouj40 z+D*-r6WgDSc<&M&UL$>+I#PP2T9{Vm9*f8_XoA zCNMRVl&q<4Q7C}5-76N_p<|ISIi|3|{qrIcqSLTP>44jh!peCnPQG^wkMAkbHP2z3 z3d=TnyE(Es%f_g}>dQ}h$-wa?uC1QURZ=_NowMwt4lO)kmulN|!i9N(xx58}J9-32 zi$I-^JyxZxP^z>>B3(5r`isE}VsUl)wYh&97QgdHn=v+*!Z62mRf`?Q)dtll+Py)Z z2r~--R}`X2qnm$6SucW-HKb4@MrYF*&n)QL8yIve5)A0?`ws{Uil$X%ui?Sy_NBzU z=o8Khsa&<;8J8>3Exr7EM@y=<^l9u%$i5PZ}VEww`Cv^Fo ztrwOU8a~>oaD;sDEFnau8dUT1OTKdLz1T}Eiw`ipZ_D%H7F`lMp!;LCSFbP@S{K^z zXspr$KcM2{H6a`p=!@1W6g10R24~pwg||IqE^U?nc5?Q zZ#V2Dutum+mP=)ZvuQweT+j5B+JzZ>bZeL1>K5Tpm_du*O7ZIg-Ck+NBMuaDYRb}0 z={Yj9#+n)~>!QUcAjuA+0ya`2x8&gLL}Vvf8vuwduO`mpzesV&8|`lEuDglm+ba3m zhC3v6LLaZ$gYwZ_Yp!^dS2rQ~YM(a_uqlfB&aSSn`|UXBGUBWSJkqsYoYmWbCU%BV2b>-Ky1&|UcDa<=gsac5KQ1MkbD9ByUrCM68Y`IO7T(CgZ& zuL2*Xk@@Q>TWqLo ztYpZd0SHGF) zOU425R8cK^De_O~hqYRz6Z@v6aZVpqBrGg1^^nHeW#%r`z=9u-kDkbIjm*XhT<-+} zy4BvhfdunS%+H9Viy{E{>|`hq)5vdQkGp!;_Y8vF-|iSF=>qfA$K!FJ+ipJ3T`7gF z4T4ao)#y$}nyG6Y>-yecVA_-wx_&J#svr+TY^vROQi zuL~iav3{XSxv-K8?RiX6vT_*qQuCnd%07K${pNR4l0#DwEqNFHER=tNzZ6~;if$!w z^0E{?K+pKdP-$TjK7$7g_o^q#l1u`gXGp}_6T8bFO*)b1ScahljYrsBFU$^aQ>+)$ zCSR8;;IaE`{Vx1G*c?cs8Xp6F{=G!-^P66I#Vv<+l%e6ZykMLj^u$y)mzBnq)h6}4>Ux&7wvZ6JJ5_B25KG)BS8~5;|@$k zp+HgmF+SU(=_mu%cn&x6%q<9g_Gstuw8zU^=>le%-s}|2=hlm(YnEB0W5p>;`>1mn zb76J7lIoskLQRbw2xQTg1{L!ZT&Nkh>W&l1NcN4W>nyjnowNq5sySw*CVBs(-B%{#=Q*gZ`QS4@$vi-Ww_NQv20fwiBWLD@O3|>(BZ1mY{OF9SQc+xP!DH*-F z@?oIAp^Y{^W6vM0$5I1!-=Bx8btHMtO0{XZL5+&=?w!tFNYxCvNv8u7XGQgzrcD-Y z_0($fz9k#oXiuJ(pkCR{=9w;LPK`=`d5WRF%(A5`(X>^6A32 zdWxppM(w&3@RRE-qnx3^>S)oYgDP5}gcp2^MH>42%k=4}W zqcfuIp{C3^LhOC&Mq0Vm1sbBY()-(7M$(k_SyS)e{8yjuqjcJGs)hdb+V?j@{Newh zZ;T|-L&#jkEn97rkts2lUc)mJI4oOmd_^n3gp+`K$`4k&$IE}nEx48Pr3m7#SE*{& z@fkFX(ahH?uJQ16%8Y(H;`xehM9dC9%SfaAF=>Z3nYlE;fl+(dNzhuZASDM(9M>jH2rvYLVet4tv%POPYbumF%JcScqoP&h^wGiE*T^FbM#2$^2Y#fZ}`mW(T1pxn9y% z#y%Ih>`f0>8k&HQt&e0Zyd!=g5X~{R4yC*6ON^{GlX#xdmdbM-0@;kz8b&TMqZOUe z??`NmpY->`eCfY!M@PC9*!2|~fpDLHGsbMp{}`CJvl`Dr%=<3oQ8Efd`Y7&*(jd%5 zF>+5e7F4{mu-0O=c*A@8D!y+q+2}aoAtj+`z1)F+n7Pm^H7?dTUO$FPS|y(?k_XCV zr5MdP>G<)j&e#`$2>Cq!Jn}e#lW9pZCJa8$tT+%_+#3*vU$kEajG$Z_s~+1s*f)Y6Wd&mL`Bma zA7*7e0_yhk2hP`Q?W8;cs1O~`rXo$ha$izs!@UOK@&WfOOm><=J&2T-n`&kQ7#E1~ zzoWt*{-3NuBpC}w;tB@Y@+*VHl#HVR%p|YzOgN~)oN=TQX!o%O+V`Z_m(b@|V#?WG z#0wPDmy|9DV*OMn2O2<4jD@JGb8)R6w)3cqVHl-_s(^CgQGPT6s@LYm{pw<(Y*y}R z6Lc+qA5IJasB}8oCsLXpBJoYrRhsMw|R#XC*`gC+m|?CPVS4| z@)44UDxi+(vYox@5fsuhP^RAhKxkhrXkC|8&c59`B$cLr2`Zm~!Q?;loaM6otfHF> zft9sY3>7~weq;nGxe$JP@9TdRnaub;xTQt_#LLM-qu+ZWg2QY^w1^AQ@G^a#x_@DOxv zX2A9stSHsHXZg)qbhf3}^+S`=o2tc})yCgmZNZOUxD{>UEwJN0hbP^{ClLBpqWgg} zy6|PH-H?cbY*>~V6_GmRl57~VFiuf-2tEmb&g3^nLLCOYkSS9uWywFE=Wi*e#71>Q zifgRCdbsbmIZfa8U39~%p6VgY_+3G_`zK2v1#jJqttKZX^V=L)9wH5_tz~;-Xh`6t2BSQA5nM6o5+2Y}9j5K-sbx9i1*zc^QG$~73xNe1{rw? z^E|#*KNElu<0h?v8rfQLwfFI1@{$M@z>?DL3(CloBGit;#s$2f$BuOA=J$^+?B zonH_ehQ8r6cj@IJRq%ps5)|SB1$-(I(BY+|!U{tQ3Z#QWtE&7N5c|>of&O3dH`(c> zL?|4|nQHkko&rcr@xTgMKFl1TEbB*Yr=Z*WB>6L{-Vuw=35}_7V3{tAYJa4i z8$g)v*!Wq&qC-tZ#y=fLJ&Q`=#${e<9!qcd5h(T|iRxM|AN1TO-*@QLKB$i;yqF%8 zJaXMws3#GG^>h=|svOv1vT<~~j#-E2$|w#Rd{%8UkMxS@=%*W-$=B`P%3CV6N)I!8 z5P|7FE@7MG?WnW&MR_}xOqG#D2Z)HYY+Fo`ywW#&!~(AD5ck9|#R$w$QGvTdQhfKM zy4JFKsqi@hd<*JgJoJ;aX~*+UBA3Kx>q^Ejd0#BxxSEZ#2LOo3HGwq!e z?8!L4v%w#UG)3j1EG@`t*dlXie_l6Z@?+3yUKxfjSGW2l`X6vgiSYmEja=%jYUx&Z zuEjFP^!TW+s46=dzW*_kG(MNk>V1wcl4{zJzw9W&Ab6Wn+L6*Vd zJ==%1!9`Hx>gYEd*S5A2pzaKz`0Q*yDfX_8mH^RHN zFs#mxVwtxe7VYRQGlC%flDAEsBsQ`e7)9yi2MByZURz9okv_7w-DX^g_p@hkR3wU!}nNbyBg>3cim1a`+hV|TY?xQbt_q;Bq#T~Q%X%f#3k)=CxyCi8>eWpG*9PKsSVFGxOb*N zZkdW^RhMU3spA=bj&FsO5+R0tzXxaQSjGj8w+}s5Hj)j^b;g6Gf`fyj7vmp>sywtA z^XgN0Ui2?#l`FzNQ(UO`XhW^wNaM8+q>Bf2G%uckp%oQb*K&``Z;#N$hZ%`pjh*?^ z!lN8j+t=K#w+Gd@LZZZLiygGs2(liQJ@daz{WP@sF@P%DLe-J=EXrS!I&w;ZGz>MI zj{3M*6@tx_Bd8PQ5spcQEu8ZRflH32h;8O;*?-%C$^j{tw@e=q=G_w*K3 z6(v)W7vZV@4-WS9mgX&SA@6h@V#&y5NnJjNAKTG`zt~w+1)SC4l<| zgYYIDwq?aKuKPy9@Z;F1 zPTc(MIKg7e#>v)dear*+;bMOEP`vYB``Yh+`2Vt} zKlo4m$zKuS7Z)%6(@*?^{~ecU&ot~cE|OhU@y?CB6F#lu_Lx6&EH0+~Bo{f%8T<1a z@=-Rsyl=?tY0vORaurOBHh#dCimELrG>{oKdj~yQKeH37d@)vdZLxo0VL1w4Yh6~# z%U1m6hcr=(M((=xFBA98dhTgmT24dzHbxcgaJf8>J>othSuNM6Jgaxf9J6*dv83(K zfeo!B$Vh~OBJuScB!1wPCYO2lX2MuYi{S|@Bu2fEhH{IrdeUzu^r+9?K3QVm0E0cw zYtnS`9YL*|Gk(|}m1)1|CCNWV!GE7oe&>%iBMPRA0#f!U9sS~Bq|XDxVlKZ1kB3-r zPy>Rqd=f?COJ-GgSL{l%rQxF3=(Fr#vnpH>v&*j-q zwN3TjjW03+8COPfAP!T3wdc8Laa+V&zm8na&$pV&6k>9Rbch$)xqe+s-fAuB+no)F zZPc+XYf-S5M(;h#9q+?BG82EUU;|xwNrY=^Y`SItO#i<3PQ7PQoR2+VsRPL;8%Zzq)x1WZyTGX$y<+ zxZdUJ=xEQi&Q)p?T1M~DjnKNF(MV7&17&4H>$6A)YM9X@np^i%8~ck>@xygJn3+5H z5ajDw>n&$mqt(mdi;H!G_w7ZEdB=lRniNGn9Js{qR|W6!JemHcd7BbKd7K zWBj2%XNWXxoYZ7bb)Hz%PvY~Z{yZJ>20wL7062yLAcnJs<;O9bN4AWrf{-_;`w$0l zUG-i`c;pasb8{_v+3@A+?keq<-}Uvib(tB#KQx@A9tXph&k8jpA^)8I`0Ho<|Mv9{ z{@?%9|9{Q@|3caP|BSu= 0 { + v.stageState.deathTimer -= 1 + } + + if v.stageState.deathTimer == 0 { // If the player's death timer has run out, reset them. 0 is a special deathTimer tick that indicates reset to the clients. + v.stageState.col.x = C.double(STAGE_WIDTH * 0.5) + v.stageState.col.y = C.double(STAGE_HEIGHT - STAGE_HEIGHT*0.1) + v.stageState.updatePlayerPos = true + } else { // If the player is alive, check if the player collided with a bullet and kill them if so + if slices.ContainsFunc(v.stageState.bullets, func(b *C.Bullet) bool { + return bool(C.bullet_collides_with(b, C.int64_t(tick), v.stageState.col)) + }) { + v.stageState.deathTimer = PLAYER_DEATH_TIMER_MAX + } } var newBulletsToBroadcast = []map[string]interface{}{} - // Test bullet spawning - if tick%10 == 0 { + // Test bullet spawning, only when player is alive + if tick%10 == 0 && v.stageState.deathTimer == -1 { velx := (rand.Float64() * STAGE_WIDTH) / float64(lobbyState.tickRate) vely := (rand.Float64() * STAGE_WIDTH) / float64(lobbyState.tickRate) - radius_multiplier := 0.03 + rand.Float64()*(0.1-0.03) + radius_multiplier := 0.01 + rand.Float64()*(0.1-0.01) vel_x_sign := 2*rand.Intn(2) - 1 vel_y_sign := 2*rand.Intn(2) - 1 @@ -287,7 +298,7 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger }, NewBullets: newBulletsToBroadcast, ForcePlayerPos: v.stageState.updatePlayerPos, - DeathTimer: deathTimer, + DeathTimer: v.stageState.deathTimer, } v.stageState.updatePlayerPos = false