From f8f2bd3102841730fbc4b9907f049e23f23dc17a Mon Sep 17 00:00:00 2001 From: Joachim Stolberg Date: Thu, 6 Jul 2017 20:05:17 +0200 Subject: [PATCH] some corrections --- README.md | 11 +- booking.lua | 45 +++---- door.lua | 233 +++++++++++++++++------------------- init.lua | 6 +- introduction.md | 2 +- junction.lua | 26 ++-- pod.lua | 2 +- seat.lua | 16 +-- textures/hyperloop_skin.png | Bin 1558 -> 1549 bytes tube.lua | 1 + 10 files changed, 168 insertions(+), 174 deletions(-) diff --git a/README.md b/README.md index 01ccb1d..112443e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ * It can be used even on small servers (Raspberry PI) without lagging * No configuration or programming of the tube network is necessary (only the station names have to be entered) -The mod includes many different blocks for the tubes, the junctions, the stations, the pod, the ticket/booking machine and the map. It also includes a WiFi tube block for very large distances (admin feature). +The mod includes many different blocks for the tubes, the junctions, the stations, the pod, the ticket/booking machine and the map tool. It also includes a WiFi tube block for very large distances (admin feature). Browse on: ![GitHub](https://github.com/joe7575/Minetest-Hyperloop) @@ -22,6 +22,13 @@ Download: ![GitHub](https://github.com/joe7575/Minetest-Hyperloop/archive/master [Contruction Manual](introduction.md) +## Still To Do + +* recipes +* better formspec for the map tool +* performance improvements + + ## Dependencies default @@ -29,5 +36,5 @@ default # License Copyright (C) 2017 Joachim Stolberg Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt -Textures: CC0 +Textures: CC0 Display: Derived from the work of kaeza, sofar and others (digilines) LGPLv2.1+ diff --git a/booking.lua b/booking.lua index 6df3129..7307160 100644 --- a/booking.lua +++ b/booking.lua @@ -23,7 +23,6 @@ function hyperloop.update_all_booking_machines() if dataset.booking_pos ~= nil then local pos = minetest.string_to_pos(dataset.booking_pos) minetest.registered_nodes["hyperloop:booking"].update(pos) - break--------------------------TODO end end t = minetest.get_us_time() - t @@ -50,6 +49,9 @@ local function formspec(station_name) tRes[2] = "label[1,1;Destination]label[3,1;Distance]label[4.5,1;Position]label[6,1;Local Info]" local local_pos = hyperloop.tAllStations[station_name]["pos"] for idx,dest_name in ipairs(get_station_list(station_name)) do + if idx >= 12 then + break + end local ypos = 1 + idx*0.8 local ypos2 = ypos - 0.2 local dest_info = hyperloop.tAllStations[dest_name]["booking_info"] or "" @@ -114,11 +116,11 @@ minetest.register_node("hyperloop:booking", { meta:set_string("station_name", station_name) meta:set_string("infotext", "Station: "..station_name) meta:set_string("formspec", formspec(station_name)) - --hyperloop.update_all_booking_machines() + hyperloop.change_counter = hyperloop.change_counter + 1 else minetest.chat_send_player(player:get_player_name(), "[Hyperloop] Error: Invalid station name!") end - -- destination selected? + -- destination selected? elseif fields.button ~= nil then local station_name = meta:get_string("station_name") local idx = tonumber(fields.button) @@ -141,15 +143,16 @@ minetest.register_node("hyperloop:booking", { and hyperloop.tAllStations[station_name]["booking_pos"] ~= nil then hyperloop.tAllStations[station_name]["booking_pos"] = nil end + hyperloop.change_counter = hyperloop.change_counter + 1 end, - + update = function(pos) local meta = minetest.get_meta(pos) local station_name = meta:get_string("station_name") local stations = get_station_list(station_name) meta:set_string("formspec", formspec(station_name, stations)) end, - + light_source = 2, paramtype2 = "facedir", groups = {cracky=2}, @@ -157,22 +160,22 @@ minetest.register_node("hyperloop:booking", { }) minetest.register_abm({ - label = "[Hyperloop] Booking machine update", - nodenames = {"hyperloop:booking"}, - interval = 10.0, -- Run every 10 seconds - chance = 1, - action = function(pos, node, active_object_count, active_object_count_wider) - local meta = minetest.get_meta(pos) - local counter = meta:get_int("change_counter") or 0 - if hyperloop.change_counter ~= counter then - local station_name = meta:get_string("station_name") or nil - if station_name ~= nil and hyperloop.tAllStations[station_name] ~= nil then - local stations = get_station_list(station_name) - meta:set_string("formspec", formspec(station_name, stations)) + label = "[Hyperloop] Booking machine update", + nodenames = {"hyperloop:booking"}, + interval = 10.0, -- Run every 10 seconds + chance = 1, + action = function(pos, node, active_object_count, active_object_count_wider) + local meta = minetest.get_meta(pos) + local counter = meta:get_int("change_counter") or 0 + if hyperloop.change_counter ~= counter then + local station_name = meta:get_string("station_name") or nil + if station_name ~= nil and hyperloop.tAllStations[station_name] ~= nil then + local stations = get_station_list(station_name) + meta:set_string("formspec", formspec(station_name, stations)) + end + meta:set_int("change_counter", hyperloop.change_counter) + print("booking") end - meta:set_int("change_counter", hyperloop.change_counter) - print("booking") end - end -}) + }) diff --git a/door.lua b/door.lua index 32d08c4..c175ec3 100644 --- a/door.lua +++ b/door.lua @@ -18,142 +18,125 @@ -- facedir: direction to the display -- cmnd: "close", "open", or "animate" function hyperloop.door_command(seat_pos, facedir, cmnd) - -- one step forward - local lcd_pos = vector.add(seat_pos, hyperloop.facedir2dir(facedir)) - -- one step left - local door_pos1 = vector.add(lcd_pos, hyperloop.facedir2dir(facedir + 1)) - -- one step up - local door_pos2 = vector.add(door_pos1, {x=0, y=1, z=0}) + -- one step forward + local lcd_pos = vector.add(seat_pos, hyperloop.facedir2dir(facedir)) + -- one step left + local door_pos1 = vector.add(lcd_pos, hyperloop.facedir2dir(facedir + 1)) + -- one step up + local door_pos2 = vector.add(door_pos1, {x=0, y=1, z=0}) - local node1 = minetest.get_node(door_pos1) - local node2 = minetest.get_node(door_pos2) + local node1 = minetest.get_node(door_pos1) + local node2 = minetest.get_node(door_pos2) - -- switch from the radian following facedir to the silly original one - local tbl = {[0]=0, [1]=3, [2]=2, [3]=1} - facedir = (facedir + 3) % 4 -- first turn left - facedir = tbl[facedir] - - if cmnd == "open" then + -- switch from the radian following facedir to the silly original one + local tbl = {[0]=0, [1]=3, [2]=2, [3]=1} + facedir = (facedir + 3) % 4 -- first turn left + facedir = tbl[facedir] + + if cmnd == "open" then minetest.sound_play("door", { - pos = seat_pos, - gain = 0.5, - max_hear_distance = 5, - }) - node1.name = "air" - minetest.swap_node(door_pos1, node1) - node2.name = "air" - minetest.swap_node(door_pos2, node2) - elseif cmnd == "close" then + pos = seat_pos, + gain = 0.5, + max_hear_distance = 5, + }) + node1.name = "air" + minetest.swap_node(door_pos1, node1) + node2.name = "air" + minetest.swap_node(door_pos2, node2) + elseif cmnd == "close" then minetest.sound_play("door", { - pos = seat_pos, - gain = 0.5, - max_hear_distance = 5, - }) - node1.name = "hyperloop:doorBottom" - node1.param2 = facedir - minetest.swap_node(door_pos1, node1) - node2.name = "hyperloop:doorTopPassive" - node2.param2 = facedir - minetest.swap_node(door_pos2, node2) - elseif cmnd == "animate" then - node2.name = "hyperloop:doorTopActive" - node2.param2 = facedir - minetest.swap_node(door_pos2, node2) - end + pos = seat_pos, + gain = 0.5, + max_hear_distance = 5, + }) + node1.name = "hyperloop:doorBottom" + node1.param2 = facedir + minetest.swap_node(door_pos1, node1) + node2.name = "hyperloop:doorTopPassive" + node2.param2 = facedir + minetest.swap_node(door_pos2, node2) + elseif cmnd == "animate" then + node2.name = "hyperloop:doorTopActive" + node2.param2 = facedir + minetest.swap_node(door_pos2, node2) + end end minetest.register_node("hyperloop:doorTopPassive", { - description = "Hyperloop Door Top", - tiles = { - -- up, down, right, left, back, front - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_door1OUT.png", - "hyperloop_door1OUT.png", - }, - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = {-8/16, -8/16, -6/16, 8/16, 8/16, 6/16}, - }, - paramtype2 = "facedir", - diggable = false, - sounds = default.node_sound_metal_defaults(), - groups = {cracky=1, not_in_creative_inventory=1}, - is_ground_content = false, -}) + description = "Hyperloop Door Top", + tiles = { + -- up, down, right, left, back, front + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + "hyperloop_door1OUT.png", + "hyperloop_door1OUT.png", + }, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-8/16, -8/16, -5/16, 8/16, 8/16, 5/16}, + }, + paramtype2 = "facedir", + diggable = false, + sounds = default.node_sound_metal_defaults(), + groups = {cracky=1, not_in_creative_inventory=1}, + is_ground_content = false, + }) minetest.register_node("hyperloop:doorTopActive", { - description = "Hyperloop Door Top", - tiles = { - -- up, down, right, left, back, front - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - { - name = "hyperloop_door1IN.png", - animation = { - type = "vertical_frames", - aspect_w = 32, - aspect_h = 32, - length = 1.0, + description = "Hyperloop Door Top", + tiles = { + -- up, down, right, left, back, front + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + { + name = "hyperloop_door1IN.png", + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 1.0, + }, }, + "hyperloop_door1OUT.png", }, - "hyperloop_door1OUT.png", - }, - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = {-8/16, -8/16, -6/16, 8/16, 8/16, 6/16}, - }, - paramtype2 = "facedir", - diggable = false, - light_source = 2, - sounds = default.node_sound_metal_defaults(), - groups = {cracky=1, not_in_creative_inventory=1}, - is_ground_content = false, -}) + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-8/16, -8/16, -5/16, 8/16, 8/16, 5/16}, + }, + paramtype2 = "facedir", + diggable = false, + light_source = 2, + sounds = default.node_sound_metal_defaults(), + groups = {cracky=1, not_in_creative_inventory=1}, + is_ground_content = false, + }) minetest.register_node("hyperloop:doorBottom", { - description = "Hyperloop Door Bottom", - tiles = { - -- up, down, right, left, back, front - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_door2IN.png", - "hyperloop_door2OUT.png", - }, - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = {-8/16, -8/16, -5/16, 8/16, 8/16, 5/16}, - }, - paramtype2 = "facedir", - diggable = false, - sounds = default.node_sound_metal_defaults(), - groups = {cracky=1, not_in_creative_inventory=1}, - is_ground_content = false, -}) - -minetest.register_node("hyperloop:doorframe", { - description = "Hyperloop Pod Doorframe", - tiles = { - -- up, down, right, left, back, front - "hyperloop_skin_door.png^[transformR90]", - "hyperloop_skin_door.png^[transformR90]", - "hyperloop_skin_door.png", - "hyperloop_skin_door.png", - "hyperloop_skin.png", - "hyperloop_skin.png", - }, - paramtype2 = "facedir", - sounds = default.node_sound_metal_defaults(), - groups = {cracky=1}, - is_ground_content = false, -}) + description = "Hyperloop Door Bottom", + tiles = { + -- up, down, right, left, back, front + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + "hyperloop_skin_door.png", + "hyperloop_door2IN.png", + "hyperloop_door2OUT.png", + }, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-8/16, -8/16, -5/16, 8/16, 8/16, 5/16}, + }, + paramtype2 = "facedir", + diggable = false, + sounds = default.node_sound_metal_defaults(), + groups = {cracky=1, not_in_creative_inventory=1}, + is_ground_content = false, + }) diff --git a/init.lua b/init.lua index 89e82ce..ca28106 100755 --- a/init.lua +++ b/init.lua @@ -3,7 +3,7 @@ Hyperloop Mod ============= - v0.01 by JoSt + v0.02 by JoSt Copyright (C) 2017 Joachim Stolberg @@ -12,6 +12,7 @@ History: 2017-06-18 v0.01 First version + 2017-07-06 v0.02 Version on GitHub ]]-- @@ -23,7 +24,7 @@ hyperloop = { change_counter = 0, -- used for booking machine updates } -hyperloop.debugging = true +hyperloop.debugging = false dofile(minetest.get_modpath("hyperloop") .. "/utils.lua") dofile(minetest.get_modpath("hyperloop") .. "/tube.lua") @@ -35,6 +36,5 @@ dofile(minetest.get_modpath("hyperloop") .. "/seat.lua") dofile(minetest.get_modpath("hyperloop") .. "/pod.lua") dofile(minetest.get_modpath("hyperloop") .. "/lcd.lua") dofile(minetest.get_modpath("hyperloop") .. "/wifi.lua") ---dofile(minetest.get_modpath("hyperloop") .. "/robot.lua") print ("[MOD] Hyperloop loaded") diff --git a/introduction.md b/introduction.md index 78cf7a9..b174b20 100644 --- a/introduction.md +++ b/introduction.md @@ -15,7 +15,7 @@ Build the pod around the seat according to the following picture. To get the bes ![Image](https://github.com/joe7575/Minetest-Hyperloop/blob/master/img/intro04.png) -The pod shell is not important for the function, the minimal equipment for testing is: the Tube line with Junction Blocks, a Pod Seat on each station, the Display in front of the seat, and the Booking Machine. The Booking Machine has to get the same station name as the Junction block and has to be placed nearby the Junction block (max. 20 blocks difference). +The pod shell is not important for the function, the minimal equipment for testing is: the Tube line with Junction blocks, a Pod Seat on each station, the Display in front of the seat, and the Booking Machine. The Booking Machine has to get the same station name as the Junction block and has to be placed nearby the Junction block (max. 30 blocks difference). ![Image](https://github.com/joe7575/Minetest-Hyperloop/blob/master/img/intro02.png) diff --git a/junction.lua b/junction.lua index 62d592b..44065df 100644 --- a/junction.lua +++ b/junction.lua @@ -72,7 +72,7 @@ minetest.register_node("hyperloop:junction", { meta:set_string("infotext", "Station "..default_name(pos)) meta:set_string("formspec", formspec) store_routes(pos, placer) - --hyperloop.update_all_booking_machines() + hyperloop.change_counter = hyperloop.change_counter + 1 end, on_receive_fields = function(pos, formname, fields, player) @@ -98,7 +98,7 @@ minetest.register_node("hyperloop:junction", { meta:set_string("station_name", station_name) meta:set_string("infotext", "Station '"..station_name.."'") store_routes(pos, player) - --hyperloop.update_all_booking_machines() + hyperloop.change_counter = hyperloop.change_counter + 1 end, on_destruct = function(pos) @@ -107,7 +107,7 @@ minetest.register_node("hyperloop:junction", { local station_name = meta:get_string("station_name") if hyperloop.tAllStations[station_name] ~= nil then hyperloop.tAllStations[station_name] = nil - --hyperloop.update_all_booking_machines() + hyperloop.change_counter = hyperloop.change_counter + 1 end end, @@ -125,15 +125,15 @@ minetest.register_node("hyperloop:junction", { }) minetest.register_lbm({ - label = "[Hyperloop] Junction update", - name = "hyperloop:update", - nodenames = {"hyperloop:junction"}, - run_at_every_load = true, - action = function(pos, node) - if hyperloop.debugging then - print("Junction loaded") + label = "[Hyperloop] Junction update", + name = "hyperloop:update", + nodenames = {"hyperloop:junction"}, + run_at_every_load = true, + action = function(pos, node) + if hyperloop.debugging then + print("Junction loaded") + end + store_routes(pos, nil) end - store_routes(pos, nil) - end -}) + }) diff --git a/pod.lua b/pod.lua index 08bf981..3b9f864 100644 --- a/pod.lua +++ b/pod.lua @@ -28,7 +28,7 @@ end -- to build the pod minetest.register_node("hyperloop:pod_wall", { - description = "Hyperloop Pod Wall", + description = "Hyperloop Pod Shell", tiles = { -- up, down, right, left, back, front "hyperloop_skin2.png", diff --git a/seat.lua b/seat.lua index c6da8ae..7319290 100644 --- a/seat.lua +++ b/seat.lua @@ -43,15 +43,15 @@ local function on_arrival(player, src_pos, src_facedir, dst_pos, snd, radiant) -- activate display local station_name = meta:get_string("station_name") - local text = " | Welcome at | | "..station_name + local text = " | Welcome at | | "..string.sub(station_name, 1, 13) hyperloop.enter_display(dst_pos, facedir, text) -- stop timer minetest.get_node_timer(src_pos):stop() -- move player to the arrival station - player:setpos(dst_pos) - -- rotate player to look in correct arrival direction - -- calculate the look correction - if player ~= nil then -- player already gone? + if player ~= nil then + player:setpos(dst_pos) + -- rotate player to look in correct arrival direction + -- calculate the look correction local offs = radiant - player:get_look_horizontal() local yaw = hyperloop.facedir2rad(facedir) + offs player:set_look_yaw(yaw) @@ -105,9 +105,9 @@ local function meter_to_km(dist) if dist < 1000 then return tostring(dist).." m" elseif dist < 10000 then - return tostring(math.floor(dist/1000)).."."..string.sub(tostring(math.floor(dist%1000)),1, -2).." km" + return string.format("%.3f km", dist/1000) else - return tostring(math.floor(dist/1000)).."."..string.sub(tostring(math.floor(dist%1000)),1, -3).." km" + return string.format("%.1f km", dist/1000) end end @@ -159,7 +159,7 @@ local function on_start_travel(pos, node, clicker) -- activate display local dist = hyperloop.distance(pos, dest_pos) - local text = "Destination: | "..dest_name.." | Distance: | "..meter_to_km(dist).." | Arrival in: | " + local text = "Destination: | "..string.sub(dest_name, 1, 13).." | Distance: | "..meter_to_km(dist).." | Arrival in: | " local atime if dist < 1000 then atime = 10 + math.floor(dist/100) -- 10..20 sec diff --git a/textures/hyperloop_skin.png b/textures/hyperloop_skin.png index 9c478a1c77912055f9af697f424f56ee3020a5ef..037d72050c92b06489e100b93884fa702db13aae 100644 GIT binary patch delta 1489 zcmV;?1upuQ42=wsDlz{6{{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G` z2jK?>5&;Te>rM)hTPS~ZNklR{P`12)8IT$)LIcjz;PU~)*|POrfGj*t;KO1&{_ikR7#0* z&T!7*JWp6_<)zL!q?Dv6B7(IRr4)GY5o3hb8rE8@>xvKp)^$bGG%&_MYYk%zj^h9k zK`Dh8BgSzA5uw%!9b-h__sBV;)(U>wwgmvG+Ix?-ZIMz!-}g9<16|j_Ifs4UF^(f{ zw;P6GfKm!6B>;awj1k^@7-L|q#W;>gDM2X(tu<7Pk(_f-N}-ej0CGxW406s;NuiVmoYD6^_I-zQPBy<>E|}*TecyjWYc14l+a^@Qb|6l{z^ zN(mtZEX#j_Qi@O+LJ%sgwIamZ?FMz8Cuyw(V&ujUd@|9a2hg&Ozmz={OFwZHwbLq)Jw*QVQOCD5a!GZc{D- z0E{t$(mc;-nnn=ip67xYl^7%JU;q4#-}z|#=I4J;0e=d>5{C5kapN{R0GJC#!Ce!o+#mCo~|mzNhJB6@y)rj!z;lxUu3A|hJXm57LP z&XjYel+uUwiryX&kqn_yip+c-qul?_IsEwX1D`*CMoLMh-#pJ^#~}m&kj%hYpp+sm z%D#W0a?WIo!FitIg#4X1J&pt3doh#ulaMDR5n)*t48wq&vrIgmhB;>(#~~y8dcD3K zE~P}q7{nMqBqE+-TpR}$V+^KglAHiQobNnOv2_k3F-AFvF-9^+N(pV-Lh~PZ5x!HQc7}40D=s!0QOY^PdyjqJvF|%- zt(fN-0I)2J{O5MN;p^A0f*2oc7zW($cZ3k$5)r@5IR|SkYOTmQOLFnvLu(DrjsUQ( zE4=rj2c?u~g@`1&?fZ_)<$}lKA*Qx%8=jt?-ZXHYC#95NtrZ)7e|olR7AXJ5VT6A< zB+*U980r6Z*|sf8DdOpz8Cg6WR{7-I0Z&g)vN;#%y%$Qj zIR4Bz|LTHT>#wN%pMp6H^270)Gaf_A)V$XQcSh4R66-iQZQCZa>YOw7eV4vxG&ub* z%)E|c_%I9~VmG(wzVD)TMku8u0^EPLO`=?_71}u`1A{xk7=y0sJ`5n|oMis%^(q@M zEiB$)7+{Qn_g)Yi$5FJx^n?)J?gh)T5D%R9S1sn8uUiZ=L)!QKhuhLLO)?)CIo1m6 zhLg4T9^1CzdcFS2gzr_$bDLZ1{UqZ|$EnwQFPrgwfHz`oF*-bHnUeE7p`L%ApXuAT zZ(XTDgN z?^E!$ZRomAG{Ptx$02C(#kB8xp_1FuTKj7hmr}4Si)_L?S!-o;{$x6urV-;|r{i^9 r*U9G0cL)KsR%y}K*H?61hkq3Tk|XHFXu*Io00000NkvXXu0mjf3&h01 delta 1498 zcmV<01tt2843-R#Dlq^60002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G` z2jK?<6$1%mlA}D4TPS~iNkltwkvXZQCNoh;QG%VVWj*@6mM~ zVvGnOAf<$17ytmrabOq*{QmtLZQJ5F4%Au^LcqT7aLys;jJAJm;he+1?`WC^0APrS zbI$PI<2VjD=k%lAdk~Q(#msQdp_GEI>#%Jbnx=ts4$HD2gn(sP(6%kCwP>0K)>`cQ z4rYci2HUpbd_IGjQENrBZ5zgMM9vwtR>&!})v$5y2RPrfFcdZDa2}j4>#s0Dzv-T8o@B zj4?3Apw;r)LL=7 z-SBuk007L4bzQ;Cn5GE;P)bosa?TjX5h*2j?=|?#%L{+zdB!-7XqrZ;S=UvmlyAMi zzaxZzpFe-X#2C5nd#vlKqG{U}wN^OiKtw2|fQZ1%`10iorfI_Ea=~#N$T{oD#&N{; zdd2yCR#A@Qh-Fz|j8TDx5HL*>US3{c)^%lp%X^PnD~Jef+oEk-Y}*Fs9E>q496_Si zstH?bK}3HDAt1(xQi@U;LQpE5b1KB^^$PdDzyHF&xf=Y->%Rp4OTZ8j%N(%Qf|Nqt=QTqqcf^XIYkK zqL0UeYpuL(8`oNyh&aZ`M8x;|otc@5h_`KHBI4Wa#-$X#-ELfK<>NT`{r#Ppncv>t zn24B&c%ElwW?q(snVECWoO5O(`n2EhbAg$40F_d-=Sv&)-s|A`_3IbDe*LPa2_ayf zXB~gXAp`(W&mf4EQgn*S+%P$379M31%5MoOyvs0=goR%jVlhUXPABA?K|~ma0i_hx zk?4kqaJgKbha)1k)?(YXPmW0Df+Y7*sM9p5PY``b;=;9N;bh7&vwmDmh)hU9Dd&u? z>yT1HN(r@At(F+0zH_}^@%{UEMNAHMI-PL4-4H@}IwJX)_a4qU)LOM1Q%dN%4o%ZQ zydwZC%Yv@!R1d}&)e1AKbxSGX<>dvB#{*s0!5D*eUGe(*`qaR2985%T&cRyysd;}0 zj2{Im{}VGJoQj!|{jyi+IF18N(_k0|?KyqlKM@d%EL`_}k5US1t*Q@EyI8BXZ9jFl z7$aU^Up2TS*ma#!BFV`!=lrJ%YOQ~)@;?d|FDOgww|G2(RH*r=4XF(2m~z*U7B93m zIcKDlwC)9secwM>FWDzH{B$~fvfY1DqA4X+yC6hFD!_GJ)ymac(RlB*VMrxdYcUMN zrv~J`SI>XBTr_~tBH}%rPO#RZ>pDg3d_JpIgq{$>^Ii~RWNA1buUf*nT(<;ffpi?l zPq(FMnzTO%a-tQ{jX3MB>#(jXE|<%*70dlimh^2Yt&g27=ZsQ{y56qqG?;(f2PBYa zOVE){E0i3^0rU3u#y@`i(BTkc)N+Y2>g<=hg7gF7H-rFVj9yx0Kna3UEu|C;!vG@E z9#d