From ea528fbcc68216c5558d06e56bb0aa0864378957 Mon Sep 17 00:00:00 2001 From: SantaKO1 Date: Fri, 22 Nov 2024 10:48:21 +0200 Subject: [PATCH 1/8] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=8D=D1=82=D0=B0=D0=BF=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8F?= =?UTF-8?q?=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=D0=BA=D1=83?= =?UTF-8?q?=D1=84=D1=80=D0=B0=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/__DEFINES/~meta_defines_include.dm | 3 + code/_globalvars/~meta_globalvars_include.dm | 3 + modular_ss220/_assets_modpacks.dm | 19 + .../_defines/_main_modular_defines_include.dm | 2 + modular_ss220/_defines/readme.md | 5 + .../_main_modular_globalvars_include.dm | 4 + modular_ss220/_globalvars/readme.md | 5 + modular_ss220/_globalvars/tgui.dm | 1 + .../_helpers/_main_modular_helpers_include.dm | 2 + modular_ss220/_helpers/readme.md | 5 + modular_ss220/_modpack.dm | 86 +++ modular_ss220/_modpacks_subsystem.dm | 41 ++ .../events/event_example/code/m_crayon.dm | 3 + .../events/event_example/code/readme.md | 1 + .../events/event_example/event_example.dm | 7 + .../events/event_example/event_example.dme | 8 + .../event_example/icons/crayondecal.dmi | Bin 0 -> 45619 bytes .../events/event_example/icons/readme.md | 1 + .../events/event_example/preview.dmi | Bin 0 -> 15270 bytes modular_ss220/events/event_example/readme.md | 13 + modular_ss220/events/readme.md | 2 + .../features/feature_example/code/readme.md | 1 + .../feature_example/feature_example.dm | 7 + .../feature_example/feature_example.dme | 6 + .../features/feature_example/icons/readme.md | 1 + .../features/feature_example/preview.dmi | Bin 0 -> 15274 bytes .../features/feature_example/readme.md | 14 + .../features/feature_example/sound/readme.md | 1 + modular_ss220/features/readme.md | 5 + modular_ss220/misc/readme.md | 3 + modular_ss220/mods_icon_placeholder.dmi | Bin 0 -> 1285 bytes modular_ss220/modular_includes.dme | 17 + modular_ss220/modularization_guide_ru.md | 493 ++++++++++++++++++ modular_ss220/module_template.md | 20 + modular_ss220/readme.md | 21 + tgstation.dme | 3 + tgui/packages/tgui/interfaces/Modpacks.tsx | 361 +++++++++++++ 37 files changed, 1164 insertions(+) create mode 100644 code/__DEFINES/~meta_defines_include.dm create mode 100644 code/_globalvars/~meta_globalvars_include.dm create mode 100644 modular_ss220/_assets_modpacks.dm create mode 100644 modular_ss220/_defines/_main_modular_defines_include.dm create mode 100644 modular_ss220/_defines/readme.md create mode 100644 modular_ss220/_globalvars/_main_modular_globalvars_include.dm create mode 100644 modular_ss220/_globalvars/readme.md create mode 100644 modular_ss220/_globalvars/tgui.dm create mode 100644 modular_ss220/_helpers/_main_modular_helpers_include.dm create mode 100644 modular_ss220/_helpers/readme.md create mode 100644 modular_ss220/_modpack.dm create mode 100644 modular_ss220/_modpacks_subsystem.dm create mode 100644 modular_ss220/events/event_example/code/m_crayon.dm create mode 100644 modular_ss220/events/event_example/code/readme.md create mode 100644 modular_ss220/events/event_example/event_example.dm create mode 100644 modular_ss220/events/event_example/event_example.dme create mode 100644 modular_ss220/events/event_example/icons/crayondecal.dmi create mode 100644 modular_ss220/events/event_example/icons/readme.md create mode 100644 modular_ss220/events/event_example/preview.dmi create mode 100644 modular_ss220/events/event_example/readme.md create mode 100644 modular_ss220/events/readme.md create mode 100644 modular_ss220/features/feature_example/code/readme.md create mode 100644 modular_ss220/features/feature_example/feature_example.dm create mode 100644 modular_ss220/features/feature_example/feature_example.dme create mode 100644 modular_ss220/features/feature_example/icons/readme.md create mode 100644 modular_ss220/features/feature_example/preview.dmi create mode 100644 modular_ss220/features/feature_example/readme.md create mode 100644 modular_ss220/features/feature_example/sound/readme.md create mode 100644 modular_ss220/features/readme.md create mode 100644 modular_ss220/misc/readme.md create mode 100644 modular_ss220/mods_icon_placeholder.dmi create mode 100644 modular_ss220/modular_includes.dme create mode 100644 modular_ss220/modularization_guide_ru.md create mode 100644 modular_ss220/module_template.md create mode 100644 modular_ss220/readme.md create mode 100644 tgui/packages/tgui/interfaces/Modpacks.tsx diff --git a/code/__DEFINES/~meta_defines_include.dm b/code/__DEFINES/~meta_defines_include.dm new file mode 100644 index 00000000000000..319dc9cc366a0a --- /dev/null +++ b/code/__DEFINES/~meta_defines_include.dm @@ -0,0 +1,3 @@ +// THIS IS A SS1984 FILE + +#include "..\..\modular_ss220\_defines\_main_modular_defines_include.dm" diff --git a/code/_globalvars/~meta_globalvars_include.dm b/code/_globalvars/~meta_globalvars_include.dm new file mode 100644 index 00000000000000..7ac36818047eda --- /dev/null +++ b/code/_globalvars/~meta_globalvars_include.dm @@ -0,0 +1,3 @@ +// THIS IS A SS1984 FILE + +#include "..\..\modular_ss220\_globalvars\_main_modular_globalvars_include.dm" diff --git a/modular_ss220/_assets_modpacks.dm b/modular_ss220/_assets_modpacks.dm new file mode 100644 index 00000000000000..0a8944116db828 --- /dev/null +++ b/modular_ss220/_assets_modpacks.dm @@ -0,0 +1,19 @@ +#define MODPACKS_SET 'modular_ss220/mods_icon_placeholder.dmi' + +/datum/asset/spritesheet/simple/modpacks + name = "modpacks" + +/datum/asset/spritesheet/simple/modpacks/create_spritesheets() + InsertAll("modpack", MODPACKS_SET) + // catch all modpack's previews which are pulling icons from another file + var/icon_placeholder = "default" + for(var/datum/modpack/this_modpack as anything in subtypesof(/datum/modpack)) + var/icon = initial(this_modpack.icon) + var/icon_state = initial(this_modpack.id) + if(!icon) + icon = 'modular_ss220/mods_icon_placeholder.dmi' + Insert("modpack-[icon_state]", icon, icon_state=icon_placeholder) + else if (icon != MODPACKS_SET) + Insert("modpack-[icon_state]", icon, icon_state=icon_state) + +#undef MODPACKS_SET diff --git a/modular_ss220/_defines/_main_modular_defines_include.dm b/modular_ss220/_defines/_main_modular_defines_include.dm new file mode 100644 index 00000000000000..ce897fdde8d842 --- /dev/null +++ b/modular_ss220/_defines/_main_modular_defines_include.dm @@ -0,0 +1,2 @@ +// Put all you modular defines here, it would be pasted right before TG Defines +// So you can easily use your defines in TG code folder, not only in our modular folder diff --git a/modular_ss220/_defines/readme.md b/modular_ss220/_defines/readme.md new file mode 100644 index 00000000000000..7ba8436ebf5121 --- /dev/null +++ b/modular_ss220/_defines/readme.md @@ -0,0 +1,5 @@ +## Тут лежат все наши Новые Defines (определения), что мы определили в нашем модуле. + +Все они добавляются непосредственно в `main_modular_defines_include.dm` + +Сами дефайны инициализируются сразу после дефайнов офф ТГ в code/\_\_DEFINES/~meta_defines_include.dm (вот такая вот переадресация типа) diff --git a/modular_ss220/_globalvars/_main_modular_globalvars_include.dm b/modular_ss220/_globalvars/_main_modular_globalvars_include.dm new file mode 100644 index 00000000000000..2c8bbe790abcd5 --- /dev/null +++ b/modular_ss220/_globalvars/_main_modular_globalvars_include.dm @@ -0,0 +1,4 @@ +// Put all you modular helpers here, it would be pasted right before TG Helpers +// So you can easily use your helpers in TG code folder, not only in our modular folder + +#include "tgui.dm" diff --git a/modular_ss220/_globalvars/readme.md b/modular_ss220/_globalvars/readme.md new file mode 100644 index 00000000000000..35b678d465628d --- /dev/null +++ b/modular_ss220/_globalvars/readme.md @@ -0,0 +1,5 @@ +## Тут лежат все наши Новые Globalvars (глобальные переменные), что мы определили в нашем модуле. + +Все они добавляются непосредственно в `main_modular_globalvars_include.dm` + +Сами глобальные переменные инициализируются сразу после хелперов офф ТГ в code/\_globalvars/~meta_globalvars_include.dm (такая же переадресация аналогичная как и у дефайнов) diff --git a/modular_ss220/_globalvars/tgui.dm b/modular_ss220/_globalvars/tgui.dm new file mode 100644 index 00000000000000..d2a271f9ffd5de --- /dev/null +++ b/modular_ss220/_globalvars/tgui.dm @@ -0,0 +1 @@ +GLOBAL_DATUM(modpacks_tgui, /datum/modpack) diff --git a/modular_ss220/_helpers/_main_modular_helpers_include.dm b/modular_ss220/_helpers/_main_modular_helpers_include.dm new file mode 100644 index 00000000000000..e39ea81bfe7ca3 --- /dev/null +++ b/modular_ss220/_helpers/_main_modular_helpers_include.dm @@ -0,0 +1,2 @@ +// Put all you modular helpers here, it would be pasted right before TG Helpers +// So you can easily use your helpers in TG code folder, not only in our modular folder diff --git a/modular_ss220/_helpers/readme.md b/modular_ss220/_helpers/readme.md new file mode 100644 index 00000000000000..7956681058ab32 --- /dev/null +++ b/modular_ss220/_helpers/readme.md @@ -0,0 +1,5 @@ +## Тут лежат все наши Новые Helpers (помощники), что мы определили в нашем модуле. + +Все они добавляются непосредственно в `main_modular_helpers_include.dm` + +Сами хелперы инициализируются сразу после хелперов офф ТГ в code/\_\_HELPERS/~meta_helpers_include.dm (такая же переадресация аналогичная как и у дефайнов) diff --git a/modular_ss220/_modpack.dm b/modular_ss220/_modpack.dm new file mode 100644 index 00000000000000..12a08029480510 --- /dev/null +++ b/modular_ss220/_modpack.dm @@ -0,0 +1,86 @@ +/datum/modpack + /// A string unique ID for the modpack. Used for self-cheсks, must be same as modpack name in code. /datum/modpack/ru_crayons -> "id = ru_crayons" + var/id + /// An icon for modpack preview, + var/icon = 'modular_ss220/mods_icon_placeholder.dmi' + /// A string name for the modpack. Used for looking up other modpacks in init. + var/name + /// A string desc for the modpack. Can be used for modpack verb list as description. + var/desc + /// A string with authors of this modpack. + var/author + /// A string with group of this modpack. Choose between "Features", "Translations" and "Reverts" + var/group + /// A list of your modpack's dependencies. If you use obj from another modpack - put it here. + var/list/mod_depends = list() + + /// Is modpack visible, (for "Events" tab ONLY) + var/visible = TRUE // by default set to TRUE + + /// List of updates for modpack + var/list/update_data = list() + +// Modpacks initialization steps +/datum/modpack/proc/pre_initialize() // Basic modpack fuctions + if(!name) + return "Modpack name is unset." + +/datum/modpack/proc/initialize() // Mods dependencies-checks + if(!mod_depends) + return + var/passed = 0 + for(var/depend_id in mod_depends) + passed = 0 + if(depend_id == id) + return "Mod depends on itself, ok and?" + for(var/datum/modpack/package as anything in SSmodpacks.loaded_modpacks) + if(package.id == depend_id) + if(passed >= 1) + return "Multiple include of one module in [id] mod dependencies." + passed++ + if(passed == 0) + return "Module [id] depends on [depend_id], please include it in your game." + +// Modpacks TGUI +/datum/modpack/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/simple/modpacks), + ) + +/datum/modpack/ui_state() + return GLOB.always_state + +/datum/modpack/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "Modpacks") + ui.open() + +/datum/modpack/ui_static_data(mob/user) + . = ..() + .["categories"] = list("Features", "Event", "Misc") + .["features"] = list() + .["event"] = list() + .["misc"] = list() + + var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/modpacks) + for(var/datum/modpack/modpack as anything in SSmodpacks.loaded_modpacks) + if (!modpack.visible) // Временное решение (в будущем будет переписано для категории "Ивентовое") + continue + + var/list/modpack_data = list( + "name" = modpack.name, + "desc" = modpack.desc, + "author" = modpack.author, + "icon_class" = assets.icon_class_name("modpack-[modpack.id]"), + "id" = modpack.id, + ) + + if (modpack.group == "Фичи" || modpack.group == "Features") + .["features"] += list(modpack_data) + else if (modpack.group == "Ивентовое" || modpack.group == "Event") + .["event"] += list(modpack_data) + else if (modpack.group == "Разное" || modpack.group == "Misc") + .["misc"] += list(modpack_data) + else + CRASH("Modpack [modpack.name] has bad group name or queued for deletion.") diff --git a/modular_ss220/_modpacks_subsystem.dm b/modular_ss220/_modpacks_subsystem.dm new file mode 100644 index 00000000000000..98f681090ce6a7 --- /dev/null +++ b/modular_ss220/_modpacks_subsystem.dm @@ -0,0 +1,41 @@ +#define INIT_ORDER_MODPACKS 84 + +// Subsystem of modpacks +SUBSYSTEM_DEF(modpacks) + name = "Modpacks" + init_order = INIT_ORDER_MODPACKS + flags = SS_NO_FIRE + var/list/loaded_modpacks = list() + +/datum/controller/subsystem/modpacks/Initialize() + var/list/all_modpacks = list() + for(var/modpack in subtypesof(/datum/modpack/)) + all_modpacks.Add(new modpack) + + // Pre-init and register all compiled modpacks. + for(var/datum/modpack/package as anything in all_modpacks) + var/fail_msg = package.pre_initialize() + if(QDELETED(package)) + CRASH("Modpack of type [package.type] is null or queued for deletion.") + if(fail_msg) + CRASH("Modpack [package.name] failed to pre-initialize: [fail_msg].") + if(loaded_modpacks[package.name]) + CRASH("Attempted to register duplicate modpack name [package.name].") + loaded_modpacks.Add(package) + + // Handle init and post-init (two stages in case a modpack needs to implement behavior based on the presence of other packs). + for(var/datum/modpack/package as anything in all_modpacks) + var/fail_msg = package.initialize() + if(fail_msg) + CRASH("Modpack [(istype(package) && package.name) || "Unknown"] failed to initialize: [fail_msg]") + + return SS_INIT_SUCCESS + +/client/verb/modpacks_list() + set name = "Modpacks List" + set category = "OOC" + + if(!GLOB.modpacks_tgui) + GLOB.modpacks_tgui = new /datum/modpack() + + GLOB.modpacks_tgui.ui_interact(mob) diff --git a/modular_ss220/events/event_example/code/m_crayon.dm b/modular_ss220/events/event_example/code/m_crayon.dm new file mode 100644 index 00000000000000..4c4d13e78961b9 --- /dev/null +++ b/modular_ss220/events/event_example/code/m_crayon.dm @@ -0,0 +1,3 @@ +//ORIGINAL FILE: code/game/objects/effects/decals/crayon.dm +/obj/effect/decal/cleanable/crayon + icon = 'modular_ss220/events/event_example/icons/crayondecal.dmi' diff --git a/modular_ss220/events/event_example/code/readme.md b/modular_ss220/events/event_example/code/readme.md new file mode 100644 index 00000000000000..dbd11226302709 --- /dev/null +++ b/modular_ss220/events/event_example/code/readme.md @@ -0,0 +1 @@ +## Тут мы храним код для нашего ивента diff --git a/modular_ss220/events/event_example/event_example.dm b/modular_ss220/events/event_example/event_example.dm new file mode 100644 index 00000000000000..bdfb6756b8b2d6 --- /dev/null +++ b/modular_ss220/events/event_example/event_example.dm @@ -0,0 +1,7 @@ +/datum/modpack/event_example + id = "event_example" + icon = 'modular_ss220/events/event_example/preview.dmi' + name = "event_example" + group = "Event" + desc = "Пример модпака для ивета." + author = "К*дер" diff --git a/modular_ss220/events/event_example/event_example.dme b/modular_ss220/events/event_example/event_example.dme new file mode 100644 index 00000000000000..a228c02b5acec1 --- /dev/null +++ b/modular_ss220/events/event_example/event_example.dme @@ -0,0 +1,8 @@ +#ifndef EVENT_EXAMPLE +#define EVENT_EXAMPLE + +#include "event_example.dm" + +#include "code\m_crayon.dm" + +#endif diff --git a/modular_ss220/events/event_example/icons/crayondecal.dmi b/modular_ss220/events/event_example/icons/crayondecal.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b46f2180fe08defe497ae4d9737d0070ac654ab3 GIT binary patch literal 45619 zcmbrmWmr^U+cr9cg3_JRjYx=uAf3|E-7$0vG6G6>r-XDjg3{gH-5rwB@!dSn`ySu^ zvH$F2{}6|nHEY(o?<>x@h9E_GNwk;5FCh>JnzWSICkO=Y=hHvr=ir$Gt}0Ck1b+RC ziiWe8sgtoI)ZQ6tXA6P2rG!oOhU2hfcAcDUFeVeV`h5)$_3c0m+hRk&mXca1tY2JA z<8d{v$KjyA_=;uM*2G!{vFrGTgLGb|O?AI{Op=Kbf+-#nhy)ni2SuGdHDv9v#obc(xLe`FP9#m#l8gob2g#nJT*+}+%2aT3et?~+_GDehm*$QB|?j2a}E^o)rt^|FuI7?^My zq8d{CG4R9#+A?Q^*~M3{x_+)`V=JCBl%D+(k5aUEKxNosz-O@ixe`3LX{jN#G0z|S zEu>0Ip6DMtPd&5J=n$+%i`aMtt&!(UM`_+H3Qx{%F{n(gZ_26aH|}95_f~Sy>dJFg zs@eR+<(%r2(*ZiQkKJ+ZOVWJ*H9$LYl7I`IEuS=CxDzq@cqS!3vA52nWLrVp3TQ%Vtb!0*KJWUV|dnT=SZ z2>!q@ltGv^6JkUjjKF}Np&{y!*!~kOHGe69;TN86;;+$(HEr=CHmB9{q%U&shmP9%>B7}UA}3%;J`;rkPPPX7P+mCIomG`DId|8cbcinFJz znZQV`xA3i#TxMxzK}n{R`I-(zOUP{N*&Q6)StlnGq_6?Sp&$3I@*Z0wq*DV{= zF$%H*Z?D@nm}Bf@o8DfxZqSpr$kEj%E|@t6^y<=GCN7wtdiRRbMI`c@o%;4F(+wx` zo1dk1BcqGu!w|0unljCOOQ$6yWM=MUiTccQ#*Aa^tSz-MmudDwI~W8)sq zXE8N3)!fY0)$?qCpDzmw>%e}s?HTn*s}Is)8}T+02HAu)^=F7e6Mb{y=?Ayt6?9H! zcq4Lw$I&zL->8-4vs{R2Fa=NcO&OurdUOGTNGn)rNYgE!JG}pWVL4;AE_6i1l3rx> zx|T7LO3hm|f3s=+()_X^6(?Q2lcmEhWm{X|Gwb2|AE*7oO%4jHM|Cji#P3__%%4>k zdo-Jkq)0*U(H=yAE3JlyK<_JtJDXlf5*xpVOGbYuF&t%s6{kV_!a{ zJj3a0!<~9|?Rq3BJ@uz20V^!gPBrKA$^F@;^*mPJi7{?E-ln$Szm#nb*QCfWoaV5- zSfW}>m^i%R!e{I}6gS?M{N0ig zkvEZQuiFs&&7u&`{fYEL0@7s;HAM)9%1u;%${sI$SY+`@3|@XJOj83tLHSzEloEwH zoAOYTp2kF&Yb9L1PcSQ^UbZj2xyIvBja-86nxKy|<|Z$gFDv2`tjx7sMkf4C$!qJo z#Ynr95EthxZddtJ^OJj1$JDBRUuzSapD{;9)I7>&v|)7mziY^|XxejmRKn`T*VFNb zGVVD4aA>06jWOjFf2)=<*79?t(x1FCQ+hBmbxnTM>DRx2<9p-9JWI5Z9?@Q@$7^rv zH`u`&G|e(wc_r=O+!=Hvlh2&XxjC{EJgmK`6dN^t$4C2felaNNoo+wz3)L;WOVw3X zhyD$jUnog98=Baeaj?||riMyc6ETKdIefKAA+sniQ_c51%^bQ7Fm@K~YbxvIX5Gaq zM*5iaEA%U8Q)!_>^A669&Gi8edF~DE%^1NtT5US!Lk7xlo&MRtVQG@6VSYYZ3RP_i zSc2_jKuYRlz-wmIZq`p@qLl0c6_hyZ2E#qb;$uRsR02y1AFYk3nR-JiVIBNMr%-x* zSXX94Y}^!`fN7fP=AL2`+>cA>jYjdtd{ox6iu}Fw)Gqonf}jb9#o8cd67vM~Pbtfc z@&khUUAg?`qiR7%`mQC>SuiejdTOk=bUa%U_)n%1unbo;iqEMM@Re3dO@-d3zFE;M zo_9t|xY^V(v|IZ8eH*<@g^7B3r*UDlIihi7I2{cI8`^QrJ0>(#?h>-1$uO$6?p+t5 zWbTUBDl{~h#9q}0&eCHxs?)FA`64O5+K~i8t=&>?(c(!AI zpjbTpW`H5BeyB{KsAek*Q8=iT364kfI>kK-RbmY7&LuD>@QXqsMwuFQl1 z#nV20Fm;%Vi>S8kJ!N2KrbMszPsZJz?V#<-dT&N081p;jeVnZF!W(R%iqu5GU3UHJ zT1%_WBk`%GAj7k$OVqJL9@~6uMymm9V*7#`$_~5W2roS)`;VFSW52cD#TT9zt<^A- z2sB)!s3tL<+Re6{P8U{dWcPEh1e?eCCQJX3N@0A&M{}9`%J*HR(=Y)kARKr5Pn{9= z&lrB37aS~mdHba;r=*{K5jUc1IeVH+nKv>*Q#vT)1J=?(~+pmZ`Gq?+cDj)tGkz=tHb5o|HDw>quW?Q^St#v7QgC zx>0I1UrG=wtC?F9g7B`+8P#H^AefiovX|&&{8D#&6CylvpWak*o)k&{=nL#*j2ZSz zn0h4;rlU2sw$f*qRZ3XQTi_!eh{BdoU+_2N^rats|G3FAH(zs%x>)V4p2>2Ra;*J! z2QgMt)W#jEVS~X*vP`7qwY@4`U=CcH48Pm)&y&+6SrSQ&GPYz)L7!O2HP0u{mM>Z6 zJf)eurv!g18Z;VTcW<&SLWfY<^|l0VMOsCTO@54GOB&gDcBSaA6ZF#u`pNUx zEySvlAHE1zMOu1SRtLJToV9iKe=ommwNi$C5l0IwBXQO2A<-E@eu>s}@G=dZemT1T z^g3L=^&RUAStw@pzv4bAQq|QvxD4eYkvGXfh-30a*&l|G`Qc6))fl0H#D3Yr>=evG zMC^fzi|W=5*+G|xr!u0stYX+{xDS}fVo4XMCMRtEttzV~?LM#twOxCyH#*y*d&7AI z!G?;n-R&Di{==lv9OQcM5xdi6#wcAauB=W{gfHL<(|XN~b)5%gEG$sarBj(-R@#go z7+em%cNds9XZdM;rraZ&%Tv)wg2h0`Du*K<-OD|Rh=1ZKABsszh?P&bo_&fJGot2R zo~%>t!yxHm%iU|9L^jz`=Xc_*`XG1u8kJHiPyvcc=^hb(%CeW@5Ic#d`o6=_NwF4G zJJQZG>$K+7)Tjs%5;AKc`YrwI4~tzxZ!k6}LMJq}5DJJnHFK0XIX^inE#Rz6$CQ=x zC@sEKeU1c;Q6roEmAZabKt+)RtM7^#u4>E4X@@>^R%?DRb}@k}^(N$~MkYwG1)mc} zMy0yxe=?8zmnaxrw=kZDcSiqb=n7#ma40i$!Jc3ClorY$L4*_4AZIlFCq8D)L$NLA zvdut8+aW-i-sW5-EHm4jo=#EgSZ(8Hq^YC`)Q~nzb}l6HU2>(rlAGFGKJT}}QPoZT zfwDw8^ff*TO&*lB^4DH*>ALO4XHDb8C`m8@bP{a5vw~tfwSNln7{fL^lF?4<7UORg zFKm4zl9Uex7XSJqU^pyg&b_4bWt~? z8yYgm!jo`Z{VwU#rcIZrH`T51VMuQb@cGX8=h^AIGRhy^Og9U1bde2qawnd$I7bWy z0;Xq!;vK2BL`lmHFHnC>t*b&w)n2pgc7F6|jy9*{Pl$iBs+Ie_Xu`1j^x9l?I6noa zWJvl!lTfp4dG)Ji-K*Y&&XO@6zb{77Wg{xDH$$$YCZ6R@SJp8WQCE_sjax;NiVN0X zao5F6k(cV2y7zx3JM4pr#+GEW^!0lz8xSk%r84@sq6HMQW46y?#ar`q6lTb@B5$iS zMLy4hpXp8Dx4H^)EG9P8!^fJegHkFP3 zRr&Y#vmt|0##k@IC`$}dO%}er?uKHLq*-sye=x?w&?lSiNr)||Tnpw<^^FXJ(Qi<_ z7*6Yb|9GII%G&Xsl96P-0e1vX##Uf_Fd?UyEV!Gs3a4L4mCh(BH?GzvD>Z$wO56!b zmK2TYQ#?AbwO;$uYIH{P0^Kq8NUWY?jJ9hv_D<#W&$j92iiUBoc$2OMo%)5RV;t+2 zGOfBb?)5eo zns(>lS~n)nT#rMgOKFa5`w7~6#vtOe8?W_Wd$A8EZFM@OF=raDlnKX9`JY~P{Ll6E zk;n^Ls}k04AEr#6sa4Vv3omV=T#lEfa)$otBRK2tchPRzNaI9%#^jX;9gu3;=2xeX z6f=vC`enZG$9Y0@bo3+Nvc)ToFsa6g$b*2?Ux%Kry-2XK1djUTd?5Vk5@pS#JH9S`icpT1%!(7@ff)WJVnNj`;6?r zuLoqdP*eQpRYhDk#EqvnkOjj>ct0K9Dyp=C75Q|y8^cR}_|yNKPW+pskfxD?LzJ^F zJ%sba2e}isyZ&QIRy1^UPomS^R#7^NCE6Hi*R;@dLn6r&O@xKvV`O09?Rq%Z*`GBz zsTvy}kMDHIPE1B7p{`D_ARDrLM4vt!Ff=58qLy)*re>Hm%8;9zJ2ExpKR2f}J&qO8 z%}5rJm7DuKCnpEuZNl*)eOSHc(k|9#`AAYhL7}zJ3kqc{Nl{l!9i8r+>)nI9%5aDUa-8hd(@{ot};%D)>ORRSkzk7v;q`y-)%`o_wjz*vbm1uVnI zO)L}?l(m2z%VZ4&L2^-YUJ+{~3rELKYV8nrz^hRUWjZj1|xC6k9${68eIi3Op!S0l4$AZZ1f_v8eoE@wGDa4#?Jn&ukT~_ zm|}jfK4jmx@$m5QxBHn{YcCSyJs+QvrY2*3PKE!h+`!;qg)+E{20A^=r_FJ6#D*@}{)VdLZ5uOpQk zXXEA$n3_`K;jd3?R%e$pwxREQ2pNcpRjBM1tB9R!8Oxu6|+D`2==bv=<^}SSXmRon;(iZ2w%z7Dl950 zTGsTqIA>i+7ngq$6#3tc+}sjB%E&0`>V|D?nQa}a@nV)Q;e>V?6|dDZVzdyD!$3)T zEW-f-o&hBWM_xZt>2!i~=Lp;wX=fN37(nV>55X=1c|BdHvpf?cn6+Lp(4 zeDelBM9I{YHgl8#{u=}o#g((`m)H7QTAJPK4qAG8TPz%lQX#@@1yFR=-=QXaT!Uy6J3=b_2$QgWGDs(A6M8h&|tq{Iu+!B26h_7O3?%t#Nll& z7yJ1=>+4>=sztf;6pV|DGwcq3^;tGeocVWyh%jeb?pmwp!|GS{&jNpce?G{?f)A2F z%2fIen*HhXqx!lthwjGp`9s0~LIeANmTWlP-}Z3j*UJHZtL?c%C7h`uKbSn85JmtR zx+p@O{VF^u;qz=3k)E{*$HV8eO#c|KN>hGYcc5@6VBt7>UEi78Xe1 zep+z$P0>2P0`ZIJEg>l>w@>LWLTM+*j6ufXYlVo5`ds&xVlk8t=jz@QUZ~X|(#+f( zYGX6_&p8Nfd0;?xw2ulqNRfv$SL3zB`ucilbW4rReD&ZDZY%9tTXAJ&91tBN3k#uA zG~bhxiDl*G+vKhxPc1woBm|_+&cVUPWv_^_an}CnvyqJr8n~Zs{PN&L)#c?BZ*UKi ztqImI4B&noh5kY+ZAu3nN>w|-VbSTM5N?ndM=$QL>S_~A{)`p%FnH8D9VZlEWRJkg(`^DZl=Cecn8|##mNCVf^60T5r6y>23D5-B;t< zf1Npt3vu2e8JH$Ei=YGp_{R}_Wby9c31?Vai;0W-#RQ^Mutw&Dc6N#vmX!GALT$6Z zwXL!d6BB2j2Kf8u*Vk(&)xdHLEiGSyHcAWu1>%#H6|=v;e>teUplkl7;{*E)@>)$@ zy~1kxO;XDDhDVScoLpReHQzqVxl%>_JUw-sUaM_CKj*xL34j+)j}k%JeEIU_WgOi{ zx){)I=-fS<)4>zoRGybazP%c@6qqfiryKykCgC3ebP9lwVShY}>kpSLmJcEeFlxQX zFaLwm5^^Z1ufsWi*3@vRsHm_}e(Xw4Pp5~3KGPL1M0zc*tNY5RB9X+1kr1`g2`Ze| z(`{n`?xaiH%BtwO(AhOCF)^`aW#uRHhf#3Vg#`t|03-#&e(t^6##yybL~}p+d2-^Q zz6loF*v*YF@*rrla0Pd6ZmwGH2Nx8EWDHgYR8~Js3`k;9k}y>iLt=w08z-k2*yJ8B z>SECLED9Xu$N4|adA|U=4m!KLd>6qk5h4i0@e16We#g^e;;6za!^Ut*UZD3OpyD8KE8DZ{Tfe0Lj!`P6m0zhG835}lKHE& z^i613m?7W>y<-p%g9N5}*^P~_bn<#!td|-z=l+0LXBO*L)|>*_G%-6Hl$%R8z2qe) z7q$zwYtABs%Wlc~3du(p&;UtI%_J*mL17`skfel!Hx+c#n&C|+3Q=C`uD^4`7jb}W z&NI!;%ozT`bN=Q>6?p=8boBHUAOS#jO#lcf^3R2sH7p|H%=lNS0U!@ zFS5C^QiPV4wiNvX!v1n%>Ap4L&;*^lft-USnbP3Z1vvZf{%?gHCz5}2;YRB zNY1ltm1D3gmUeal%SWH(%s{=V4)6fH0f$mWO)ayubm*5eWS^LKpT`FA6wbS{lEW-R zL&}=as@jb|e4;#Zo~`i+fL~B1*27qJL_Iv}yCUA$Q@*q4<$}T}vSr z?a$QYBoqe+=isY#q6AM494@M@r#aDgODii~k{djBHa2f?#qb6e_s=-nAXWbJ)zBDe zGaBp+F{;(M$Q^KQa80fSaQp2;wyyjok+O79DD(vj3yWq#V+ zoUL@Eo-rd63QXjmU~}T0j~rbp0|WsRNVhnIH)uSaoSqJWI-&TLb1L`7_R>NCqNn@Y zbZa|%-(cYBnFexP>7myo8YVzao^3Iw$Vr?7(9aX+JWs`)fk%f}faC>O%74IwLmEKb zkL|HAV^`PsMEnLo4JIZh@m@ckxTS#hGztjl1SOPFj+O68#Hg^wIOWt<8Sq0|X~*7DB=cD%KQ~0oH^?o2 z7H{6ODLVp3NKX36Y_&9(5%;AM0PxA7*VpcI)leL;Nfe3x`0#-5hna9_B2x!)&K4Mt zn$X2a1A6*Uxv}`kmLn+}aK(axnjelffB(QwQ7%^)OUiTD1gua+A*;~j?76>YVad@9 z-9W%ULrI?BKp@;2vaqw0l$MsR=*deQ={U8suxJ6C?nEK_M`RR+0ji$`ptw)5x^J#w zya~586S)E^?%Vf_7OxUhQaTnEbSTB)3-~K3%Rx(g&B{vh@JwE^qKUU)VRm-bC=vr= zQIPTHJDTL>`T0iYz(Uk>zE$cI|&`j9qiStP`Qv8vu$%NsmmYbbj7_{|YahfeREfOh?@te-*493DB z$4);WZnrY18z5j-z<1I~Vvse(N|nefV!~r@!&Ui>KmJ!#@jw!8e>Gxrpo_! z7GTP?-sUWGzb<1EAbinjm-U{ky1L}lrTFV}``eX)q_>EW?g;YFE&v7UlbO_)1zx3S z6ma^b>%!#Q`_r0n0w-qRVh+1*R|Cj1fe501J2wq7D9n4nYq{wwXqkY+tp-`PZ}7$7 z{czFE3yXEd&|f*->~~&ifjb1J2Qlr3fP`-HFO47G5nWYy5lS97qj)wgA0!Tw*h0$g z$6D`2TnjI8J>}v6c?w9aPU9E6+lxa4$kT&S3*6p_Y>G9haJO# zT!-5y!Qi0UJvf+9Q&Usa(keWh`y~@1krgXT$CxCGJ?_fTZiuh9zPqzC@~PdvcCvP7 zth9jy9<=`OyOb4I4w0U7zpb&I>|Q`B+M0ts0Vlb0dUJO;(xch>(HL%^-G(DNy0bMV z@&|b~-gWu9zo#HjRsbx`FD><7K0<=P)=8Gxr2MA+i_5`M5?N)kSR1GfX-zb7bbZBU0y2d>+8=|0TmA1x%2KA?fM@ia#$opXq@x> z@^bv){uYQ%`QW&5QH&3W4sSqI0*Ok>=C?&#TN^wv3CSUfqtDNr9C2+8jh~=sqUj!0 ze!KDnTUUL3eO=sSr+`q|>W4z`peiA7phqSi)pl}O6~vn04B1w%d5Q^mY)X!-yUKIk5k81-+H=jF>SCihF@ASnn zJ0H%y12{G>IQXT`AIHv~={RO}i}75!C-ul`x^xu$8mQSbOG!YD2o|P&Yw(BoKjvk; z&e#?df;V^iMJ-SJ>V;UQLsx|s%?sq_^djVdao@_p`oe%WmKaI0g?1f!&99^j{ucS) z7guu#gsSqBpab&tL}_C22m#S&Bjn^v29jG25hd*LcDb8A9cDyGQLe`{?v^N?G*B6e z76;d|+8%JSo0o0`G9|ybn6s_Zh`lgBAA+?i_;@Dhei*26N5HDrLH@8x{(Y7Ms8!-r zScRpfU6V!XC-u#@^$d)RqkuT{_F1$J-LNcAIe`3diMF@Arl(R>15v`j!h+YY7fix! zI|8J{<0Ae0KCa?dZ}gjFfffd&$G2ZRJOFsMzc`rn0l5qj0+Mj4eslQJk{-wltLo23 zdL^LL0K{Tbvd%&LJ*0bGMNcmrC{D3}w1b^YOicw$fH@F3IoHt9F}eomS{Z`&D1>;U z;ckSxY-mB$8P+E6lXz;Gm6@&H(N|Uq=WY$TR+fyFRVL#6BB>bYaAy!n>oatBPx{oZ zRyqMdxaHF)Ha+|+%L2k41gJv@y3;48{j61NUClxRb8^!zIt^}E`j0o$XgJm<3zk_{k*2#h-!r`PCkPF+MR73q{WZI1hlpxeu2MqE=Q` zkSUVaSo{U{P6D~rr^#5C&B{YkQ9koDu@0#KRCT95ElKUUawVrvVj2iU$}Ci72-|NbW6D2c zmf`IN31Ya>ZnA|2a33)wMc`}l_f`tX%35d0)!o_f=<>21=D>EkS2(kJ>C=&^6Pp=L$}+OW#NMI594i0= z4cHN&?*K&B4cP3*j~`bqZ%4<+V-h$+vY!AaXfZ1{|Mnr?plyO$NRnGNkp9gtH7$+n zI-TFWGSSrXTAuei9J&0o3rKf_@}-}3i8Y+%lXM4nJc*ocA?5McQZ(2gv`bM%#c(6K z(i^}0E8KlAjtzmNrMP)9_oNweSg*~LZy*+H_^4wyO;` zz|&T;f%P^@)jMaw-~YL>n<8m=QCV4@6iu{TWva+JiG}Omv+K~Kz-x99ERypHe(W>j z6JeMha)Lj|bD2{^d|z7Ht=1x770IyB&KvR=EQ*|gx~Zb1^bJ(=^NWi$@LQLnPB4rO zAoy=W5Wu}#4Ms-ql&26m0muZr7AQlAT&_M*IH%UTx!+^Rnt7%(>g*&HKeFs5nWsfT z-$Dj9He$U^hV41H#_UaVM<6XnmsK|y5G_qj%_(=Ij&`8>BSGBGM$gTT?*HBhKQi~oqm2vQ*1$pqEw z7|H92NZPo#Ow!OtNhNL_oW9@+a00CZ(25u?aNqp1|1K_w&1S5T|AO!eZ}KZf-T}SB z89)sLaSF0H|Cl59blE_9Nfs#@{Jch~qF(1HlR!SRe?y3ljw*j>e9ic8L0I$9RfhTg z*P21l;6|20Ypj^pjI;UpcC`!+CM*DUHFK34z<&aL7?ygejxY~tSB7W+CuCxN-pUuw zfWJK^l7gDqCXk2(Km?!A4zC!13OK@RfuB}BvcIzT0t0bh6w^*m}g3&*?W3C>Cc>B>EMTUuJ0vJU(EXZ!ekTmeCYo;;1=^jTVmvu?N3 z@P`CdepwtQ7M4lE$in*1i!KC!@3f*4a6D^X6aF~P9u;mW?yHiRf@%(6!3aP%EUc_~ z*YCKvEZ6#;gIWU}4tZE}XCtK9>ez<+Su9(E0dRP*L{?WhzTn*K(o#4G7wG_TAd#P4 z+_N3}-_t*bjm`#kug`!Dj(VQA-)33}HwqM_Z&c9{8-nCGjuXDI?doceaGO|aF{kqF zrT3e$6ybo=6SPMpu_WE9FX+MieEZEUq#x<0#ty`2PO&0^~h7rKD3#zJ=WA3d< zLONSC)slgPi(2%Dl4$LO2jJf@^l!P)vFmzShx;2BV5vs_CS??5VF;(v$0^1 zEk%KIun7OmVX|k}2H?cF&YQ>&j{|RqErK(6Tb8~kOpX7tgsGVPa8XE-WWfD6A3;yh zvu4!M8t6xE7UO3s=CU`zxKlUezcaQb;IgN(G&cAXnydx@HNdUV*+`6Xfr(Bvv8PJ~ z`l)tnD<^L5{`4n8zn^P0jmxT6t*iq z?rLDNV$-&_%E~*+gcpqDzGa*|k=L4-26{aQJJ$p7&7=s}*yf7%FA6;!q}JXEq_V{&}<;r8-q+WX(5FHi-;{G03QwASBI z+vWs>dVTtLdry-*a^|_l>jgC7Q4V)KT8v1H)Zs0fvfjgif0x*VJJL-=Ti73{MN)o0OZ|^66(BP{`h~`~+#2ezU%z}a!i;Igw zg~A5?Z#PKRyabrSx^2T55ZuE#Q{j`K_2EV8WlS%mxME#5dayPzQxC|_)*q3uH+p;8 zg4x8VqM?$R&n-;7quBfU`pPRSjd^pO)DuU(3%VWn%NRu18BYS}Jj9-44SL4f=rKROBs;`kGmD2im3JH;0q@bs`=;C zWr!PH_;9QC8q(M>!Ze)}3t898+nXCwImNP)5~48DWeTuZmd%$Q&3Q0^d9;4OvoDU8 zBPIld=fD-h>7)KBz}GZI`~@tJwRf~h7b9b1MjCl=`x4I&Qkq?{CHS|e%d7gCn;9&D z-M}d~{`+gJwfb8?UM7Lt2wEPfkQpG=akdXs+uajb^>df6QWJBO%8O@958s#D+Dr5wUBO}BkF|d5 zY)pn#NnJf*w5X)4OcD(LoC=B9HZG${zUSER_$!IujB}=o2MoiYB#g_8i`+rJ@*ALZ zN#7B@)BaXTNm+NV#Mre`oN9{UyViANwEL4Wk8@;Y^$lM+ZMcgDR$VlSN!@(zBP7T7f4 z2A0qQXlhsEg7?Z{M}E`O(^&xPkkf1+%D#C+a$s)f;_67!a?kir}h!}c>wLjZw zZ~X*6|4pGQg|(QRl)oKbtg#^hV)fMYbRL+K(bU$_i4_>0QZn8-I?4mwW+r#>MdFF@ zUVt0$zbrG&+NJ0)kwqfT{a%UY0zV&g@RNo0o>84sMkdENzCE`*VgjI!Tg&D6y8AHRB^A_S&e;wwRcZ(R?A| z=g}8oBwwobdaM|YegB2<+em%RqgeKH(B=wMTM3u}MH-a~6iQJB+)8b^Ru&#j8$;3c zgV`$QgBf--4Z8!?tpPy!vp{XOB>#Gj6-10)-yObIrFL=wG$){B%hvDs~KDD@51wduFg{p|hs z03r@D6JR{^V@3qX*}~pF^2vP14hr-d9Rq3-137cSmmX5{;j~{iCLlt$&aPiiKR1^7 zI>U0^`tYnGSYg`b^cqI2tu3dcq}woDnzYMZZ?-zy%+!2;XqU17zTyhZq6OMiSgSv; zeQ{b@t@s*W)Wzi&5E5)6u^{8asfA~-sHt%UK{+tj)zRM!yXM%flRrR`bbW=4xC6u? zHUHJ4ir<%jPQGXRdOIEIQ`np&`By|pwb!F33=l6w$Qp06YPBv561#fu zDG1!wp8(*<0H4%rbKYie^YXSJBLfa-4`33&fVuV-D3lkq&Ej@;tYTD^%pqv#ALC*) zYTtI{%Khjd=YI?Tcr^ISYTDvxsWJICe>f9=dp~P)sB)hCYMi>>Xqx8@^vhEC zJq=~3OTi0R^OsVn5ckDJgAsat54)bsz2tH}lBGyil zosUmuV7ZVqwK}4!*Q%`13{k-Aq2at$CgIKKlWqtb;pj%*`8BQgcH!=h|N7puzXJ@w zVe?fwE$KJV^96f~`2tAUf2(E-1_6n|k1>k$s3bof0t z6%_{pfxS)2HPGF{{l|RGIkyhl3b&kR_`_q$3)$}A!?M}8l`P4Sh zopw*U$+_2}I$03rA4oLpFlN{cc}Te~{$aJBF~4EU!?h=&Bt`$R<#d%%4IuTPWoZCQ z_C7L`*_X%rOS#uwmmG;W?=q(KJi^@20-MeAo}KJz(QSj-@Dt4GDk4HcwtwNOA2z|@ zTUAw+zwv`C$<0C4@Xpz=;O7Mt`?W-~7$)@`x(yZOz65{>Xx`_toNT)`=8F8$;hZ}1 zysYo4N}0+)jWJKirPa!&UFQS?o;e?kxhJ}oaek2}q1LBWXh?SKLR4Q>dJOGpsuGKr zm_H2}3viO^6{r2;NdK5QI`nUz965mU;_%B}!`@uz42I-`epSXdaOiLK!8 z-kyk@92zj5$lN!uwqGNk`Ku+DDyQU}Q zWUFB$+<}MYj`gdpNaW5FF}J%4UY2#GU|hm=UzP?N=y!cA*{0=M^~~g#m9zSrv*zhJ zNMvr1mO61&CbIPuNzj&o?zsz8Qn!;%g0qvqoi{mJC&IEa63n8<{>Z3cLIapIQx|_1 zQCiC55z6(tU3BT=rf7(X*-8RCHxVQFPXifkCw*&nSOpSZ{AZNUC3#g$K_Q~cio33q zq#xxOJe;M4h2r1eeF1O>Xdg8AXu76HN2APO>s$vWo5}dJa1|LV|59??8X75w*6@bSbyNA{y`^m_ywr3mkZ6Mz@O<}B`fIavG9GAKIc{m6# z&XEJ!!9*3CO=6coJ{X|_&O4Ze0SY`3I2SNR8CY4l4}~AWo3I5KrTox-# z;>FV>A!qGh)9+6U3w$lWQp%xtyYy*p-}SuiUBU#909-R*Lzd9sZ8KTyvipYi&7LQO zhxEgjSddH!Rh@Y}Ds<7f3Q~7VLV$Dvi(wj!Nd1_32Vr|_r;1>0#ZF5R<0p~Y#p1ud zZoa*0&r`Cxd4`LO2*lrt?tR=6ZDYaZcVy?2(v=?z$>J&KYfYdt@iH9Xy*2m&>8G(X7q6pIysI$d4=E8ei+BY&FLgF6_r zHopA32=RVuTmT)siy9rxl0+Na1}Xj&YVs*OhLOV+#@!GIxYLh=g}}`KyGiT>0=v%x zJn9k@f8r1B?z|k02``>pIFRyS9_h);>y4q`-RO;(SXx4VNQqJaGXT!@KB$HV0$#EXt5jEgcb1B~f-PaXiJDn?M*aVOv# zv$C)d0xpE~?Ro@RlaRzC9Q)X8oeMJnG{Ub#VQ$z)q7FcQ<#9booB|BzX>Bt0D$da4 z>9)c3N^EZ5r*sXs;Dr3_wGbIrR`x|W?nF$Ti2+J(?|kIK6W)GnJ`|~)j5!YTJoX_6 zlThpDyW?(aLG;Hr`(^7pqgfDdr|lu4ZHWC^$BTxWy`rvn)Ok4c@+%-tYqaGe!oxGG zs>T`2)jYwh0x;1+%`;KJAH(?X-vv-bvt4Qksy}KX0mQefSpapqR5#1|FZ=NZ4d=!A zh$sfpD%qDE$}5<_g+kEtVmtF=@Z9Q?XV?=^#?7%-#kNLKYAr6p?uYZ?si?mK@pu)q zk|i0jw(!R({2it3@7IHY-ix*SgGJSOjUJblXJ=LU45Rm_Dwzc3nP1;jo zYj3$Pu!H5JKU{F51(bhOQ;Q$k2lmLM$-`{hVH^#ZjRW7-fGgGx2dWEAjC9s?uXb9H zs1qs`qbBGF#?H>1B^hR>eZ|K-*zB9jfTb;dNFPoxv*`xJW49v+hsR;f_xx*>2fchs zi`QMZ*U#qWWif#Q^3vA8k7iii-G<@+j&6&d3Kx$|t^cQ-vs zoi=hWr`-O${B;^RCd}V_o{D1)ZtBvPoSd9HY8b5ZFEG#r-@?Vf(ek(A4~6bDAhGmu zRn^rUM|BpknS&Y7{{ga0YAZ@xzfXMphJb`R?nu?|*tfNAKwkSiKa2p3$W_CL=>kO^ zuE)NEFSp2UTQdNciO5O#EDCFvhazrhRJT(#g%72hfJM2A{LNuId?6a?(BK)kC@A@O7 z#Z}62;*WPqHc^+7h{UkhA*$0D2=Sx2pj?OW^xJr4^ZdUGtoHRrUx*@)crK39FUM~f z|MZ12)6K@v-WC-5_~FAKvoNqq_BVVt^*T9PjDGfe@Rm)_clPENZ~459bG1JUT&sG2 zlOZ&|-c}@!iEvw_D&?)MN(;?w!~QRW8t`Xitk+o`Zz=C?S>@9PzFLY^AdcvWT!)YZ zt(32!=CyOkAu;CQQ6++!SDcWLQ1LNwIlMjJD~H2SXeH@NJKGqqzw*_8!0uOHH z+l2!ZN#ZbcK~@X;-s@-gOddY_Ym}|no%na)CWuB*(d<568xFQS^n+S8*BM2VD-2#2 z5Lny-RD#Ne?fF6+n>t$tlelzg->9J%Fd*rEc>0YTLjfX~Pa_FE&I?^?=g}y|Rug)E z@UMPz;ojxWyK~y<2Sa>&c7(}cqyXfTfAAplUK#yEQC3~!XJtJn4ApGxF9%<;0FCvTdVg- z<}Vi5;C{SF1>`HJ<8_=ClU#6niJ&30O|k1wI{gK`7}4vtx0UHCEtfuN15!ikDZ;mI z-}*o_GZ9t&b}{{FpU>6hd>^UtdcL^on(~gGAc2X+uEArDYs7OSWIxiPOjrNR-}@$( z&}`g20v>ruztd_z0)*$zH@00h834=xg(IIDd&$*$<1(kf#6@|9gyGg~L7TL(nr9n+ zoRS@z%D;p5EBuFA{WsaTdKjJlgC*E9s%N~F?KXNIvfQRpG1~t5T z*-m-48$^%7GBaW0oAjz-hAFbveHg#NMeBlxvj0O#`j#b z*tqeH-_dKo?mNx}$H~B&iC>lfZsuspHfL!4x8-FBb%J{qsy`BxGZBtAs(esNqOWQYaX|3XNovLtsXuqMQx1kAGd%rr)(JMf?0J9~H1%q;vF32*)e~*) z5OBwUr3U_eNTAV+lTlXoWcWoYX*edpCFm@%Dy&1}FB#-aIq_VR#Klt~4uxJ6>AG{!7Bh(i|9+j@0?{1^6-ZiMLYg_YeS30!n=H=B*_Bl)m zRdH$DC-;BBr<^*R6)kBWJbDyK?K-scEx`IK=p|AXN?4b??kslM7GFsbdnLN`|@6Rj<60yhLw2oGod zq=4bhO3376G`(|!5zGA2jXmeF)^@M(xfw|{kUhhhd7S^E8YoDHtu1vN z)FwisB~QX+(aob~04j=#ik9=WST8Xr;~(Yuov;n>18qZiahdGcj4NMx5>dR~{-LpbE~thr8xD4{ZW(A zcGu0OQ|`BPV!360L4}2DN4tDa!mJ+opqW2(mlhqu^~Khu94Wo=u*pNuP*1Pr%D#2i zsvY9Wn%bNk4b)P5sH!Dln_Q>XB+y zd;=ell;EQ)%Pg{Q)!4Is-1_SI;$ti3EB##jO z5PX2AHVD~n|?|Aw8S{aQ7{`!niD8LDL|)JQgo`M z&xE_l*)2gz!UZ@Xe0oT0MbW}Yfq(kqt39#N276CK;sRfc9bt!oU|O`@S!`8cSn(>U z5Und{6xjsa;NtAX#w?;)_aI!V>0SGIH#$1(i)AL`N< zn5%czFEC9DF>sn~aV>WrzYQ@FETHs;5^Rkl}}1rGK-k#UTg_Cj5{6 z_njuPe+C}is)3$j3V9Onf!znG{>s1)l4)(N2=m$g`lwZ#>j#F@jZ%#&Md3`Tn5Jv@ zVvLXZTyP~bvJ}$0i2RNowz1v7ho}gi$@!nBQEHg#vdt7xmfG6!;mGGnH0Hvu4mgbf zL4tAX7vfkZ8{xXM^hnfoPP%jVC2irKsH1aU%RdR&&yJ!@-Y9tVOEFIpfmuH1TJ?6@ zGmnRh4V5${YbvU$8I6t8TZ@8@xp=%NL)cQ|XKERBhhK*H^&GwLk=c~r8Vtp0i zb9t-F9pP(iaZj+bHw&)MGtYIm@0BVI$Ld9ApJn5%By`V$N8-*EU7eI{$EjzFo!3AYg`pMj#+3o<3eN4Grw5H4QI4JsRHQt?N|o|6{&VGR z0c(2|SLoN>Ie{mczgkUtIL!X-zA_o^Pd&Z-{d?$=Lh5Xu=-Lh*c97?li~{o;s8KXa z@G!0_IgR=(oWSol@0WMLME%v|Y=I(Ib=7k9cyMO?I|Hc{ui-~)=3)Pu_;RjUF@rov zcK^uU-m<(!`ua1KUM%UZO@U=W^T6?0hsjxp_G9IxHqrhs+&wGuNmwb#Zd1;TaXZgW zG>a+KScC4%JQjTXR*?E2Hd_*1+E(TC^c2mX;bV-o%!igMURdMgOi;{50X3_B738P3 z@_iecYEt_=L`#Y%P2B4p=stv+>oCY)c@$OdqW}69o@A3p{`ZEI_Xxu##51#bVm?7j zPVQ#WmUmz2rSi3nO2^?j=52d@)u^YYQvOnH)21-oFjJZDkQ3{WyI=caKillc)qK3r z!U*FePU@H}?p}rG={oAdp#k1VR;f^EtmufQ%kxbPY2!*pA-3iLK|BM7C5IX5oNr;N zWC%Oi_O>To4|B)6zv^up$_boRD&5s-`93Gzo|o=xUs!z4Uh1r2kl?t!23 zix+}v$el-LOKeiStP5^>g7Y5!aT4Jv{|vH8NM`GQAfyLF5#Pr6MwR1qTzS}W7>y>gak)s z{NvI2Aama=N$usel#xuztgjag4(|I zqgw8pSAG@40yXU1W#Qx5aH5{mD~;gjcUCkm0+jKAUc2Llxef{wfsTD}aVDpsjh?Qg zPXKJjPM0m})&=)$Xj ztp)*SJtRPP;>D$CJ3Dn2(oTL*VCenMo-L+G?U;q(nhT4)gt1Yx2Go^7{sHla_fThi zs%mOKF3$!o2V;n>&$Z!LLw=BAw#6t*?tP?Gfh zVpZx=gttp@hUL|V5Ln3=s+tqW*_xdb&qh4%1(L~1J5RYod>l#l)zzi@98mcjjN4qr z$~hwyghJdR{Zo#1gWKasHrwT=fS>_igAQG)(st2HN(P!0*q;;Oyc zISF}YWZVCvdj9|2i1L4HqyOJ@@-9z&RW7i?YAB zx75An+VCj@6;%eT*H{$QCNfm+0-OoPJ#^h!!l>6ZR4F}-2pH_!XI}A|B7-1#h8`f@ z@&kqvtn4a7kTQ-mDg%yX)f`$>)zvitm&LRk^dHOG7hph?YO%QfABQZpN=Qf5J~tN^ z7QP{NTwL7Oy1F+Y5$(=kJ!<>!whb!C-i-mt>AznNUJ^M0>X4czpL%#^9{tBu6_;F& zhX5-m_*vVCQc6~6!D)<1oRrbn_Lb8C$cfG_~wB?K~#Jbav6uM##W zBOnF`j#zM`Ip?+6AI=4uLj)8*#$+_65*rfPV474tR#9eUL}xr@4f*SG zuGl95o~Wg2TX!Haf*#Lwf>DJHi$o+BZwSfk)-? zMnFkt` zjEuY?^Inq}aW#;Dld4!ySVBTVpDETKY@l`-g|DyQ@DJ9cuGUf$poVmPT)_@=EG{_u z+WA;b8yypIXs8Epu6Rw*YqOly)zp;Q;JzlVyoSF3oa`%q#KJqw!8rR;GkCol!fidg zcGa*~!xnZL*-2rkK7<@UWX&Ns{;8pXwoI20?ohFiGy)EQJj~!S>J2fQ0r0G@;3>dY zE`uslS65=V!2bp{3JTUBoR|%+Z)kJ{fj6?ae>68E!0@Y0JPvCfu69Qtq#;Ui>&gy? z@$1F(&oPYVqa$HB!BIwa@z)_z(*;t0V-G)>omVqZ90hO-C>Z-mIg=kF|+w2oJ}L zYXDE;4bE!~g^+zrdduCD_n5uCOMQ5Fn9qyFc{6oDpmaAN0Bykvb_S7>41rYSU={OD zPF5Dy7<3*(MZ5KVmOTj8`n*G@>UvpkFD*CHprvbU%nH2k)YRWEw|_zUAM7!(7$Xc4 zaG*25!WDBz$3k|UXXYnU?~B$&b9$q#w)6Ji8HN6&?r}=;MS-0Ic;u^1M>8|VgY-Pg z0PbjkphHXeZf0c_0`eU~+QQ90v(S=nZNhL2{!lJ!=<9JKq(wRI+-ZR{H|TYhz=A@A z<)OVVd&y3CceYnf_v^lpy0;LipMb)aSfPH!wdTwepO>2c{@Xc2=J_rS6GkFF8`*!eELU?tP7=FojF%|SkQD? zy&D!7xHp66lqNOS=R$_A@y>trJf-aky5)T*o4mh-i1XS%ho>weXIAX-N+Q1?Y$$pg zNgajlB_4N6>&Tp*o<1=()>Gs3`5=b>x82z__rH$T+~6dOe0>Ztg13l>basDA1T8(i zxs8p&OZODx3JHs|yE>Y#t7$+$HUU-UE17_>u_Yd#pIfBjD(^w7D6H92jydu(7cX z3iL&-xF1Q|BwB7iHkpt;I_&5OIT{_>r4N@KGv$#k(joIz5ZMY5O}$Rfz(BViG_Ik= zNgjTBiwe=%b&JHmxrWF6y)P;n9z;<&I#&c8YYl@Bx=wKW!b9nom@a?C8=9I(EqaSO zY7neJ#wDW<^R-$b(=zt=k4Q`}pL4b9zBk>x>)3_|nw@p(H%nI}GEzPZt%-4JZ){CyU7U zS|YI|La3ZxX=Qo2V}mt+#D0Ehshu`zl|wS!h1n^BcuTbqt!*i37^l`3wdq;2Nf;K! z)5WlQL&eh6PAJ)2FElHxyGp-^^#^00LmWx$-H5fG!e(RRf^L*3J z39!3p>+Fp1Ebx(SrGC!hqDA}Sc1|zyV{WX$t#PTFxBYM6ceG{?+tt<7AO?`1u%CA5 zv!i7{X20iW%|Kxf%~~GomBs#7yClvyaL8U)yg& zT$7M;!fYOz*uw9~g?qjve|u1lD!L2GrH6-y;o7jRK&A?7P;xR2w#wIl^>v5lT3*F_ z-x|9UH1lsvPfgYQN^dchZs6Eu{oXdfq1^P?Y zzt7I5>`?-=*QYr(rFISPS-dss!R%Vka*5b_Ae7v(y1JTMKYs($A4oVp-V5`cWAUi2 zMb(!NyEYHuM@W21caLG2&6~+dgIh>h2F^z;q?pZf@8C82wHi;5e#gF&8rk&1=p^Dv zk>9MAZv5*nDiBJm`(*=Sl@C$`roMCfrZo``jHiWLOu4JAz zmHn=ks+B5Dj`-h8OO_i=d)^XXbP8%}YM`UFZ)|X32cgyE6Rh}NJq=HBIGQ!}^>L`z zG8z8RC@N2XZg}L6JAOaqOCeGn4kNlyGmNgyw-qlt2o##I2*Y2?cyJ`dRv*CgF0 zkH{FYf5I9!l)wOiwylmE3W;9NU-sb8i%VOktk$_9Mm&Hj(>!6)WZ_1N>Bnx5ur za1j&uYYW5DMJi6C*F*&qy16%^wS={du8hDJtcLfgGB4?r&%x+yifigXpZc-inf8zW@d8xp8?CN- zJdnjy9Y#dJ8aEyi(|DlrFh`&^>rgW!WxLJug_GMf>VkdVvQ zlBpP%6-~uPz)F{{ldYqpZg}y>poP5OioJNLjLSoBtpuplIuU!yG>eMo&wkyyp7aqy zSPlXnMLKP8YcZO|>7!dL7ckF#8R(<$jyK>YCVIflZKh1_6%{GcQTBV~>B-(d^nFw= zae~s~>irN(4uM5twTAK)<3CT|53GjNs=6rK=E{UYf|7csq}5fJh1-%6aXhxRxQG#} zDBIM@ncT;eJ}~KJ^8N6g84A`(5+)wal(sfj72`P%pA|g7A-MH(*17^OVKfB%u5HW9 zw+{{u{I8|Jk`R?+B>mzxr7voh%PlVQxaArD*^4g{1UQw8?)63)FNDW)kq!K82IQ=^ z)X`mYfny%<2EZN}MHcgMhGbqtHxkmfj*ms) zx$WuYCV$oEYCxeD*VhRkMm8L^zqpN$$GSN^$X^j${<1{P1(z=HGniU+3r zWF(}w>bR?FuE4_$&h>98FpAnBh6P7XO|pl*O$=FhNuTa8L!qM0fTHSlw5;Cb%C$yZYz#| zA{rWb3-01}B1iliiwgV*s(TIs)SiBRd3tX(*v4mPafI@)D&SRNJLlZHxKK+~du?oD zV&zA%-Qn-uEpfJp z4iPyT*)7JOj>r7Gns9$zm2j`Uen^Oo(bj|!fcUq$*@9zn|F}%gpCb7D{4`uxwq>N?~EKISJftgr^L&~CA9Ic_I#&pD9TjiHim*pSB@OZ44 z+0AOf8KfQ*6rly=%njp`m&LVf^YblRGvTjg7|V%I&wY_TKH)8aP_k=cL*}bxWaom4 z0zAd9$A+%X&G~QZm1UWYaTi`DjkusUA6599^05DVA#|6;I2eCYu4FU|dd1Z2Y?gk} z!uIx%M?_l#(jEpymFqlnEOCjm(() zsdIUE87WWFc5OVh$vnN=a~~3uj7<+jpti|h=b(Vs=YYC!p< zIMdEgg_oQdm<3dsqq~Bh*E*_Qw^tU^a1Ek6HN5+{lZh2^j0Kiqp>oaffH;>t~OS&d!XA>y87NERMJ5vx55!K~C zkM~WwpZF_Nzd-PpK;TF3uiP#*bzjPW`%%aRzFJV4&E|QV(A9w!C`b#XxlBYKzd%%^w-L45AahGc5fc??+Te} z#Hb;iFT~cXcMt0AR_vvhpC^4^E;sgTGA_BtR8;i`K^G`{pw~$$`$GQyBm*IK-PU|g ztWo*~Gz`^&Qub6empY>4_V}Gi{Fh~Pf?=JG zfq@^vNqe1!W4mwesW>6bw1{Q^fjz6c#Z}XrhjJ3v8(H|(pKMY$8b^D6IaM`+ch56) z#^l*R67dJ@t}kiagObb7YiC&zchU#e125Otdp!7$ILRhCRl5quIXPmg0+P_fo(?9P z3sCDheIRmO&o1m!(GI*(Ib>0mxJV%yuk@tWFjUcp`-JrR>*Xd9SSbjD7t4grxT8nb zeb187Ny}Z-HKRG`jXiSXVWSJjdB;ab%`-D;7Z*n$^o@?PC-hX&)$5fehD05u9bf8Q zd~*7U#N@{soSaNvK9JLR8{K7*=iFXY(_(YnXN~71R$E)kOA&;Q0A#tv5&!mG&vLHD zwuzx(O>szfJonS0&Z9W6P?Xn3c|H*s{Tgjt!p7mol%#@=hzlmd@XyO*v;DY|E5Gzd zB0(wV_r(SMG154(v8=lK^|oiEt$_V2KTB0>60TgG0xDkz8a~W>GcTW7(FD?b*2w=I@xB0w%_MV~;-T6p)a_iW4k9VEk!cDP(s2c~0YFc1%Uq>eJI#+cVeP)AI&aEdak0 zCK5J*A$mcK5OI2TCR=Mrjy^mjxh-T=KLc+dVho^jQrF@G*c%O`geRJaki(BA{PO15 zkVi#26-cyZ@H+q846*)NWL=Yl{(Ea{(4N0Qiv#AAyz47}{=C^Pwe;}v!W%ns4Wr&G zDJf}PD?}NlJzHz~uQ`4if z?d@2Ro8;k@72+>F?$I-Ea0pSDl`x#r%CHT%=Xqss5uGc=VzbzDSa zQxiH2t#V??NqFgQsuII1ul_ABUo|b)xKf%L)?mPydzaz30Q|8Ng2@J&3bB%~R2z*I z8jS&9kiZbNKhR+^?rvk%I*Dx^Y6jZ~y?uSHKjuEEpB5rS?re5Q#VdsfsFD%AA>*&` zqK=`NAc1Do$jRkGmal~mI&#cJo_!@geT|vJDd0#5_G?ZMMRuumn3KQr~xku-5X!X78SXJ>ZbPsn?I$B!Vu@NF*q#)Kr8Qt~UU33n8 z)K0brrEQ>d$l(8N2^KZ?@9>Ka3Pi!W6^yC7B$EG`n5nCBH14FNq=4;b;pBwcOp8A2u4amzJFUn1xbX7!S;X|0I-yA-BO{K5zE== z%u4NTO3>xRE*XsggxXdGEQ!K|l$hA?<)}au5M!p#Ft#?{GK!q(F~t(Mc0GfNu(;Bf zKe*^A&h5~uhMS&m%YRoY<;gZ3jgM3iKmoa@JLL_x0EgwI{}k{#XCHs8;vj6yYdH8k?Hu%aeNv`igo&VU zVB}Q^bY~cBs|CXPs=m4yyV~#5RfK95j65zGZ4p#6!u>W@h(&_E5wYB`>jB$44*8aYr`fw1u z)k4Xs$08&Tjvj58`}z5Op{k)xX;1g+3?Zt}$8Oeq$F(p1v68-GFb3oL(&^6c-?-pQ zO;1n%aF~w4D*S7GKkO9s>*HC;JA(cnn}`ILKs{H%vHk$hT%q}W?~cI77-C{#e-92k zI227s4uZgD<^yZfH1dOCU{6h@o5s8)l1;^PGsl|X`O&TmEXs;#CTR2LYh!x@a5pr3 zw*3GvAkj%VsBY#SJ&Tz`GSqN>tfE{yP;g$O{4U@4s83k>6pONlYL2j(#(A08Y&#Ni{)q#+&4E~=f8`uCRFn6;uF9ZU?1lH{ksmxFLWR! zEYKp-3~s>6^VE>Tifp{=iR41bq>?~r!HZRsgn(ToQ2okJYCTXj`BrN_H}SC+tYcrC z@{AHPH38?u_*xNlMUxT^3=CA^JcOkg^R8iyjS|o~JjKB?i&0}2h;_jq(wura@JEu( zG;`mzC5yS2%;V5ZoZdp1RxQaVl`*;R2TXari_*~9JnD-aP5?LRu-Z6h;^;I;uDB&C zviyd%3$Z@qLac(xs*vV{fiFOgcu=c8s0HDRVkpyf+^}&;8;t35wT_$QBuh!HotxOG zYPey(?Bkmd`Ex5H9%Vrj-nF=QbY!a_m2b$+OdZu9(FBfQ1pOo7-Q7*NPawIq%cx@{ zsf5BLpjSW5%8MHoN%;KoV_$A+tS&y;6vgWIV=nRtts)&@d$Ju)7#J8}gup?mgAvoL zLh7e$G~9_FJv{_rxU#`W$!I{^VZL*ySJT&}mXzYAMJ{UN^GDZzIto4W@!=9mIt`J} zx>ZP`TrF`AE&ryDOs9Q)8-Ye@MBrlgkknr`T!yWEc8`t764x}QgdP%g8T?+J2jGCR zNPR*5pE}FaJgN`)wG~M3_$~0hJeN?Ia%tn5$3Q@(H3J?=YJ2VU5l^ zDtW_*W>5AbrBxI18x_4S=z86WqZhqzeBEr%2PO_bk}XqS;MGlYTk%o#1keqR-s04F zYc`TJ_j28D#Wwt0ma?qwieY;q+mYp=tg43!~zZ=gp-`No4xhIZ3*g1g>t3 zf2(+U`Qo@Zq?dlz$|(sre%usb9M2b7?gr+6B~B(^>kT4oWd5slpjxb*&L)Nm5%BTv zX5$(-8g)5;tgo*tZ4hB&%$ruqic?PA5)LSf+{i z#N-h>mwO9Vhk5K#l!~k@^eG_dV4dDO6x+lcQEC8mnX`u@sBrB^`GI}TOn@XjF{J+h2fsZBcc{3bKhS~B-e-A4CS(tjCL+?C3c>QOi z`7QBZP4L>XlBgEnLaBugbTo@m5(1Ml{(_$HbpEilaF?&rDe2TN=>n*SMhgI9e{z{e z>12b|@+t-|R2M%u`FvY`KD&_sPWJ6n%N!4DJfey@JuPCVdg+q`0%U1&9aXj-1<4vw z-orQf;gpIS9p|JcRl#w651w|ghNPCM?UX6fkC zCKHz_#~OZ}Zd2yoaQ0MHf?ilJDQ88MQvtF55_a6|dU`~1y2EWoCf7I;5>@QyOiVQN zo%xcA`j{l=_jwe9q~MYZ3M-2S(~3&4RLR~}qTcAlKpeTL(>#+`p__~ZudlbbiRR$8 z^}Y=AN*qSw*S^ZhK1)89q!GgN7(u9u7EJ?AVjgjw7)(LA&cX`0j~KbW@H(#dnD3ls zMcc%VW}>$jZX}}aZ=O!>X~47{Vs5}ITe00SJ-Pn)CBjWxtAHcF0zRfqvT3}QI0#Mi z*o5NcI>TR0*pU9qOk?DaEbCNTaUQROW)-raQ0_F*%kjUaRu%Yt|8He#FQ|L@dHXPg zgujfgv#`kpW2`-`qWsUq?)rDleImXU376pW9&v)U)%qMHpn35493O`RqXYdLh{Qm~K|mehO>-alml=$Yg^1Cm!5$8_iO2tpf7`!``U#!} z3*F6SlmtDIfNC2+CvHy|++hK9>^IB74UcD_8#)Tz<*>To-&*<LnD(0= zyGHo>;R#HD!^32yNpIRw;k6;#z^v)rD4NpB`OOgW!@r8SHXG(0`AU7Pi@StJbfKG| zDu8T0snK~U;Mwxk?~S{ts$_aJ7!&TeW$o(XkshZQ;4yRrp1o3EC4yaSgBKT)or}Ei zl7X?S9(KPhV~=8ia`J%vMqs{iZ?wftCy5*QB7*B- zOxyR~*xMG^iqdm$Km()pu2eHjg6q* z-T!#8k3=FLL-m3iku_xdMMD4qCrs7di3{jpT!fgUq(_lz4w@4YCIag95$vGS4;q7d zl|EoI(~B7N6qhg4b$1H2;8!NvIx{SMRXA%Wfnb%n1gi9H+Ng1f0&I)_5|u8xIAecu zE{TO6u!&CC^y*TR_}ruDQVCndPBSeZi)bP)Sg>fpY^p_FCU+g%TFI< z18CN&Nu>>BKzRZzA345}3D=8_5TL#WS>p&0e@Quy2D>zUm4F2#$4`rn>F9aB+cYXhl z0}|A-Jwfk2>o}M_5xzz?M{`vdbv1S#aZk`~(dwp)C2K$lsCQwdj*^Gp3hLC|P1#`q z_}(J#v2D;Lcr4`I+}&9+-9Jc>0}>Dx7q=8wlp&H^_M<`wQAhE(FdCCRYK(dweX6(tP$<$tRTOw9xGl0MT^>ryyHg5#s_E_K zifsM$w9EGoUtM}~ut=fX}Qb8sL>+F_$z$_2}#L7UhG2OEKnd@;k7}` z)w{Kx<1TE-IO%ZQgwE#apLQmoK{0P6 z^jP%-ssm{TP|#rF?DX&+r3HvE25X0{Q}F|7fx>IV@Zc?t;sQL zX|sw|*~)+v#1RoJu1Io7ml#5X;Gt0otP`Rp#NJ*VU^qBC>;17N0Fz9FEK{LIewgIi zlK^lH6zX!eE^e19vSh9jfIMWL?l#|QGjoVLm2wRMuT~^m#|XeN4}1m;CP9iVllOUr zEu6cC1aNDXt0Ns6`X9>PMM3E++C3FC5e4=s-7XqiW9 zlIh@<5!|o$ZfyF;TeS0viAi_A>ksrCIoV+6YKN}l^cTK)P01>(6c_)PO6)5|9sGa{ z3JZg*Z4?NW-)uj!UKrKi+xA5Mxu0v(yX7UC%F5IVWFOt?%+{9?d;Up5m-WL_T+>bTqHW0Z2&SBi8HvhulyLxzFn=YIg?{&fCR=_!Ex$tp-DY@?*Z2f*& zCHB2Ri|zSIs=Mg6sbd$yMj-decqTbp^!l{L>B(=T5`!hO6OiGs3HJrgnCA za*{tW+R@-%FW3In_$|VH6wk+$XTo?o)c;Yr z+C_|5D}0SZ!RR_urcy9v1<v{^Uk6jeHZ&LWADW=HdP&h3->17}yE+y}}1ylzGO2HKi0r7AB@Nsp|+z3Jy1t zQN>{ag#0Axri?WHyo%+3#j{thdY~QY0xJ&M#EwEJwr9UgmCElrmFh{Ob{8glBPwH7 zsjnke8SEYZ;{}jh>h88Gqh0-FWKqS6dA5py{YhQ^=~_Zfm*r9LYZw`C;)m46#&noJ ziS8d3h-$|q6j#zl-b?6-Z4g)j%}Libq%O*-@68UcY{tm?i{Q5TkdyYTukDkysC+FB z*akIdUkNy>gYacM6)tGu+&^=ZsE)^M#pxf)&I@-}-|g>cfsv-NHY)Iw96IWn6;KSXm9KUQo6UkB)fh*Cw!o7bZox8BxX9#*Y z2&u&2@3a;8MIs5_0|Y!3){{m7_(o0k8~DXU=%6CN8)Il2)e55;nrTDg?8Mftl|KPU zc+lXX7P1GZA$`8Zh|ER9j6 zH!b*^tnQJGEOFNlpmTx9E-Q{e{FYI`Y!eRn;ZNShQvi2+54uwDqc79W7Erpz9d*W1 zsdz{j32bGcOqm^ERZcpAv-BYdn;GwP2rkHT!IA{dLoi4|f%B?r;G{`1iYP=t4FeqG z{>o2)dSrk>Dnac?nvj)ux6%XMm#BB|RpRNvf89imdc%w4TSa~@J-T|6V;U>;U4@QB z>zSB?&s8K?K#U|htyEMnkN@6IsyYldsBSe!KA*Q%5oJm9`48GB(@zFDzj%dzLQKBZ zRC>B_@QGh@uA3%1&|T{JW6p#gShB*Fmglr7g=QKdIXl^ZpNzcgAKPvJ4;J|ZF#S>J zo1p)`u8JTt1LzHf=YcHn`K1{@a*ky#mtI@PuFw5^_^{USI+!bU=Bw;Yc!~rOQl}Fa zxY>wchC6?4K1Gv&InLADo76>{xV5U;M!fkxiNhH>_ltR$k@ru1fMPT>Hh%LdcymG@ z6P;Bgvu5^>f)r0gqTuZ8tlf00uM}EZB5AW<5O(yJv(;#yHlz)yHm5(S^xak;l29iP z!YxAwbVNNisTWBi-K`3Pmisb_*KqL_z%b;FME3eWYL1}kA-<4%kC>3LyoFiFDh#cG zganYR?3mEj=C$t?&NF@+!Z#;F%mNoJ6L(N=Nu?(V(4;xyTl@O1!HfL7c8W=4_V@WY z9ax(vufKktCbI9(e&e9@@#CQhKErBn{gh+w@`fIR% z6tm!Hv-^^0wsvyFM_v8=2^I1EHZ%k@c`y;f<{RUqD2g>cwlyuHU>P$hC5&>sc)WR= zW^knGNv4Bi2;1Uncmld~=x+YUayyp}rDq`v43-~0mGwT(NR0k*W%X1FE4(o__;+?q zHVJ?A2{qU{b>9HS^-rsh31c16o&JY&U#fs=k7z{!_mf6`wliOdx$<0WytnpTiz%bM zS?xhel|I);?J~`6wOt@+-UqNc53D;h&`P132NfIWwg&Y7DouupE&5aK%*cSXaCb1cK3L=GT{yv@H`ewwx9&G>VDeho^{9fvb}R&O|K^OsE$h zE>NEE-LYXm##OWmmO9oP<#5wXd2A1h{V)MuPi~kC)3&be0)ftuHPk&K36_)5Nv*M- zs$+kk6En0pzTL4QY)S_T2lmd~Om?X?%mo5oF~>e(=izNqJa11=;*OTb`C6Y^N3qE1aY`79gG2Z z3P&Z6NFdl7(|}+)pKO3@id+t`AmpX}YazRb#d0?meIP zcV~yxU!rocVw^KaBj054Xr+teAuwB>ijv^;LjRN=2cJ}c3T*1#uTngVf419%(_IMWxCIAH)&?Wr0?k9kPI1N zxI_f&N(~qcnOcDgDH(MuA{w%Fw^;XYujoqeQk}2*r3zsHP?ox_DpM6It00B`|dERzc2{=njxIVdoVXyM`^27_jSdq(%!G}8n3MC@H_%HTSX&W2}eSY^1fXzUcWP0 z&*UTxFlt;4QmVtS`$p8M-em8k?qc=b5Lbf@W*{v*#XEtsTg<&ZbPpz2@GlW2TE2dN zM(-f^XKB|9zL6*Sq_M{;?5QZWSf2~QfyJ8dPN{XzEaw(s;$#+lgSc`3H1F97#2MIckYe4X6MSvMM+GYUWQ0M# zK+Cw3xtF)M{-4tk`;lJxIe^v%Nq@V;l{7G3Bk`cX#LSgx?QP?f~Tgs2yc$Yc$H zaUym-01dP|_;R^}^#-ux;EdSs8w?kw{Tm-3y zL_OG?4F8;7&lxlQnCrx(AFgw)1BjSkbWKbIyneUnqcIX~6XwRlB z&Ch@%ehVy=$Uqr{Vxa`T0-kdAuh|Wc`meAE@;&Cp zzBfyDLlmbItKQ2p_#(h7R;Fa{mGAX-10B#jVC5i4AV80Kg8xy&1NVH)^8hRKTOB3m zP!rE$A_^5F^2B~r2<-WG|5s_>9ZqHZ|9v0x93wMX*_|XrD50z)vQi}D5E9CsNmz|$V0#B3 z7TCTo$wu0DGbiZXcVT`(iw?)=U$9m^Vd^+;m= zSYxvSEdZ8Jbv9cw}6FOo0$AbS>dg9MF8Q z7^&TO{%2<=95m$(jg97PcZit<=M7k%sern~YasI&x>P;DGX{Smp|3?-JZjWz%+3Kze4TZ&EBtf{({N$Z&&ACF-olWzfZZg0R zs&H;UJZZc*z2~sfeqLBCVE@=Y>zgO{fHa1+>m{|T_&Z|3(~1M@d~Oz_A1`#-sf!e6 zenhs98xlH8QmDz>wkQKo>vmmLlW%MBQSb~VlBEmEiVK+(NZzH#-r5iff8tbWL<>;{ ztT5|6KT~YFphB-y8gG zi^i8tt9VgLGe+9?`(pY}Y)>3fwPCnF$?kJ~wZ&!+WJlF##vh51l@D>5Ag!&aP&9}h>eiggu}AhNLTBX#^8ff$Y8OerF1(+c zo>p|cLskWxC#LyakP@#=VwIbP!Kh1XAv-yRCQF&i>B1;HeMB1P{;A9k%TSw@lHV$s zCa|Z(8?e!6R%E$*`uMP*$RqkH-fP?mJ4r*qR^9hAfW}uuulQz=3pf#9w)NE^`3VM2_3m&jkMA$pwq<_6+xkqYEPtQ;g52xBvAE{xWprNJX zz7zKp$A`LRPdZY+_}WPKu6bRVa*)Y~H>V2QO3Qm{(=g@eHwr=ITcw)ZIChp#M;Zzk zr7#~O#^)!_dm9pZ!aaI9uMJ58Zv)E9MK%NmBDmI4)6z=S<$c-r4xZ-Rr|7X3V-4LZ zN|=&K>6J^%z6(~2nWpwasd|}f!2cd`!~IJ`FAJDFG}X)h{w0hxx|th; zuM!kn@c94Q2X3mfZM3Y3CO zO0bHK->oE%O6sZR;lBTfhfhEm2iJ!E7j^{cD_5>UD-6;ESkW9D9eajlRtB%+R)H*? zF)eVxz}~o2i!bxm?ZolRvMKfQ98d98T&XFj7E22yoQ24c%s`QbZt>_}LCGYE96=Vd ziR?2^)A;*##@u{}z$Ks`y`N8<6K8<~N>QwJsVgKBM17 zwx>K0?JLdsQz=9|CpXxink3Y^?S!Lm+oQwNH%Qb<>GgT*OAR6l_P9K@eeL?w{q8(S zd-&X;T7&QzOXfEl!cfEb>YI(E7%R<4zEQy!Verqf_`B3{zR_7oP-GZ>a2#$DV3_oE zwdzS0LHo&@O%t8$&9@9Bq|88m9;lSzUOX(buQR$Gl1Ub0!9YV=YvA{B-<ezn4B=Kg=k|>CzW0CW320gY zH0{>$q2y^=V9+61=!DBdOGt5K9ij+`A;hR~Nw=ubzQv0=7Vza0xLzPP1(`KkM_Z7= zL}{%d*d;86m#NN{I3=EG-<+8&y9aK;>n)$eLlx6daXJgMFI991^ORYV>4*K-1C#sI zkDZ%iJ2p#Xom{IEg=Y+VKlI7XKCOvR1$Qf9(hot~P&{7-marWqS?4G)&tz=hY9>-L zEIwO+AYq^r#(nid2#RN`bIIbAGPZ~9a$ZcmQp6L`Jt4NDGZHYhl%K1tUpAdr zJxtv$d>h9{_Ns3_;KcdfEcD!tDj0x^tgRIJpIo*|i!r3fh^Vq$Aq38E?LxdH z4SO`bi#j#j*X9&%UO!L@q((e{?F9`8AK&2UNqDAwnc1I6025?Pj5@PJi&#<7m9t22 zMGq{xxvg}5UtjPMFfU_D?01p|8yXnWW+7iBAn|X}dt@<&JH49>x5VWGIrXLY)n|O@jSEp-Mo&3YAjYr0Q*Z7b$ zas9AhReB_vcG9(~vbur-e4O{?xRyw-+!sI*xF zYV6|pg}c^c{~Wi`@9PGm>ag7mEd7PcrQc`w>uLc27EU-? zmS(`=U>qU`90e*Oto{b954b-6FeB+;jW@KuD0Kv6qIg%Y%WUTPks@_Go1SvIV|130 zYMW;;8@zOh21|HA#7VG52%bF<%GmXV=197_6^|qk%;HxxK=@!<9HJ&rw%Emj@=J1e zhL-?c086;t&etWdK!6w#EFPCckUXP8rEt6y`Viz&2xm&5M&GX9@fq!0iOLC-m6oSI zqo0OW>pz7kkWNujAWU~6LzkcT+_oijTb-Mh7neBO=C`)g%~XcN!L9_dA$ja2KJ5#6 zVPjEZ8C~YXGu28ZAN*-%F7l0jEBJ2k3S>Xf;l@x0)*ekdvw-`74=NKUU*kpRz3#wm zs^;~x~z)nDKyF1VzCY%JEC#9fM*WG)>G?@;z65 z&QXw-CupM69tG?}qiG{~9#_@X{8}pG5#bh^Yb-<{^Y5$~CW9Q4(I^BcN#F<*nr!y> zaMXEL8X&i)g7fTIpsJvcqv)cU7lK)nWHUn5D42XUa%}ra@l)a|@hvj@l@2AQ7re5y z>vWB&`l~z`yk@4RS_Z1-BgDxx1E5K=+0GD@tqGZV5iLVtfZ# z*&%tn4a*!4s>SAMoQ6<5!zt##`OxJlxZzn6Ea{j0_IfCk zKrD{zal%@7q$}q$ISs5Nv{n4M3@+X{_1qyhclUdf%U1HC@^(p_k@w<4Y<<#QY72Hb z9yfAnHlcH4-=$}cE-hkrX!Wk1LUsZtFoh5D^&UD-RO zYs_-2G~m?WWu^JKWCBmoeFf^W^wA}MajPUYhgVLLs(E4dbCq&RejN})cno(J zJ?f#Q(zW%RegDR#>EebLZ@H84U(Q=X$Ds*1azBH3Ks{Rtx+H)@if{zXH2;XPe;)OenUxQS`W38Di_>E1}?)H85uLm{frM`ICs)s@|K7if3T`QP3JSv$oP z_ROdDB~gWMf95`xs)DE^&vC`t{0v7-UTS173=|v5!AcU@Jq`j=N zJ+MMRWx+@FVcoxGe1!lOog!y(1WIRe{CRQ{JbvMDLq~7y+^isQvNVglL(9Q4Ooi$-S*G70wSo(Y3wMbI@ zZFo0n)_@xB{h0IJ1%KP$Jm)5pBv{b1enBcyw2vl0UmlR`t@vgS4%4n9YH&6;yk#yq zMr7H2UCDb%#^DCo+pZ;#gU%Yv8F})7-#94|yIbXxncbfKv#l*!^EGIt7RZ&&;UBzk zYzrgP0UH&V*|O~ITz5GGx|WugH7_*Qa5`Y*$cYuuoTm6x#JuG%bc<>M3=w9j!_<+p;9_|d2PzF<<5-n4{lg}1Z=2zEl^(~E$84u| z-4P2iAQf1>3({?%M}?C7rS;)s6@>8dL510BXj(3Co+tk6RcX$udp$3nX*+`+2S!1q z5hb$s{gx@bXyEMQ9Rn1R^L%_kblc&|L zsMU*$#P7t$hjsg_RpXOB**E=Fc%C{?;5Ww%jF1G`_tp(n+%?7|=y)_Q-ynG)dga1y zMR(=X*{Y--EbCpE2lUudXzNUL++|KS;sXj!L7@Mb`!BpPMx=t1;nkV@mk%q}rtRup z>@>7qMZYCFoy6x3KVY~^1NZn9iS-EI4cV^w4akzop|n8b4M?SQw?q5>5n&l{SfFtI z_|3)1sRhP{={r^JL-QzoruYETS)bkqz;ZyS2{^egPIM78v;&2#%UQ-}3(!{Y=lK}5 zmxnm(97r8#Wk3h;T-qu&yfc!1O~jld%2l7+075Gam2ZBbK*a>nnHdGNlSgOy@bG>E z?}kaWbT`A5r`fw@MHI7wgkQ2;(X_|TZFq6Cu-0Om4@GICbM6bUyz`Jb8Gta(N%P8C z>L#x-g>B;4xfpgt0MwF_j{T)tG40k5W@8<@v&JS-(}07ARqQH#rzg=xrw-VVrS(y2 z4M7b+%T8gvh%!Y}8zLw%wDkJr^^@koRAeM|VvNn=xeR6b$B)uLnj;HG{0itXhp^qU z-lfPLg?f_C%5;--fW`WeV$e$TC<>CX z)0R;!aZ99KzU2Dt^EeOk(p2z^3;OV@Bd8scn+&wpy=6QH9}{YN#!7;s!O^=gb}W!h zo9q-iZO}|iM-dHgb2N*L9{cE*r}*Xbql*km*3kGpRez%TSss!@yash+Ph5XdM6XE2 zr*te{z{uU@M-ANNJ+c>cHWzlnO3|GC8gaY(J}?bU;-MfJl++pxwG+Bt1h?^!^M8_4 zRH3W;e~MHAe=}5D zI-|ngQSsDrUHVS=@bZNOh~iv!^=mxDg*dinu`Y#6@x%c0=hea69R7N z^Nsk^37YIX@UE-oylQAb=0&2Am@@#7JO~sPpu%&Y);t z5bl<(jgh@t0ZIzGIFQxF?{xS0NDqUA#{lb}7w4U&|1JpTX9s7Yu%oKxBT>h4kb6 zhP14##GkfEkAK8G>*y`(>83Aifm4Z}K?D-)0lXX>0?HnEZ23il7mm{Eb!ro@= z{o$fS>h$tIi8$TPq8GIQVNT3hWGaCipXwlbIIuqRa&1p`@%~Mcx8p0lJE2JW(4z48 z!5})$9MIxgJQS*g-ri%z`oD{mfx-dp3S0|vZ#cKe0jNh50BQ_q%-*k^a3DoI6+(5z z$}em}7>g$MyJZsM2bQb2I3^GdC5(DWvio0u&e`_hkdl%zSg0;(YHf`k+8gjfMpc26 z35`HK1_)sN++pR+-X7=RoA>UXVJtI!*+cV+7u%C6e4&~80HcsSGWe`gsBsd4fFZ0U zytC;Aof=I;P6tE>q#G3yvel$vTz!EU3(LZj5N2e-G8;}*d=LOgqbEFdD6O@xU;o>e z^6Pr_csB61ZI@eC)*MoRA%r740o$s4=M6mgdyRs6m+MU+WP!-30!+-C?LU}9VGtcINV$V~6h`?+_Z%B+tZ)QuTLommP3mSn^ z)ua&xGYAgPOw!VBrR2D+=?v->00cbZXHGr}ceuyyio+ zffK&->=el!V^l2t-_F(wFOe!$8Xb2Gqvog3?7pI%bcGCr(;&nljI8|B6705z1Kq=# zK{Xsz^~l-T+|$>|iH8Jr6vyDE1=6C}Pd}1>hCgWn-PeusF@Z%1RdLMBQ6v#qEFfxZ zq_lNl`S7`uy#mXYvM>wjS{iW)vSFeU+W;Iwcw*J;+P&l`S2u#7C;%c4|Lh7Z0m)0N zATf{HX#+Iz8Woz<4_ri**ejoc&2_VHG6WQxW4|oA**tuB+jo^2hl>#JID}3igSaYN zh9jX^G{AV8wIsZh8J4}_eBOF?A-{IS%!{UbhxYdPN&ni z35U@^;d>|ag$`hy{N9Su9pz!B@_r{G{0Z2}-Hmy=yAk3=-eF@G9(#L}V`B}YCg^+* z5AH#8SJTk&xh?Yk`Nw=n5t2ekgOgi``Cc3SDY&r8&Y1gfjYYs^Q&Bw6+eO6f^ zM@kJrrejt;>Z__J01Va8xGXJQ(!d7Fi?}P8x`qZxyOP1+WWNv&h?C(2C^qg=Xp}9f z|M2CK57vjQVBFr`hSSv*z`H$e!laa-dDUU`7Ie<_CD#YFHI>wG9~hC-g$hK>#g5Jc zSY^&*rOmAphZ(A~T7~6bS1^q4@>;P;h*sE)|MF=6xD>Qy8y;THp}}Mcl(mRZthEWk zfYJIQ)g;j>^0-jnawztWF{+t`LtMG;QFGr<5NY9ZhYY>e=gZdcX~e41kADr+kuYNS zzi$IfynDz)IBcZiU$5s&U+p_A~>;abx!*waCnY}Eg6e49G5uc>#ubw$A>q(>>|^-M@< zK9cBOI#Gw2gX|z~mWjjWJq4ZW@!>m850UMnlcPHTSK=Rhtn;>Wn5ujbCzBGDIpk>34ANvzx_iJ zS};Hs-5Vz$O$SkvvjxEWub1}_69VV(OqkRN1ga$;!k*c(dO1$)<{qNYppZ1;0}@ia z{dEiD3hoWF)wx_lq!7xp{d}9fd$htX~ALZWj@ohN1AQ z=HSqQucRQWgDcTR}L(Wbe&4TZx_-|7N8FqpgWfkXYnX%8tRimCglg|c) zP0ffw?vP^nm|J1vB|FO)S>TtbeNb@TAL2&ab;i=16nS5HOgRmV5dkLgHb&(YeY8m` z$2H0-jx0;`7wZ!Y$5-Z-m+ey7dhgGMM1qqDZ;v0UaBPOn9KR5zxLtRFr%#p$pk-wk z+-#Lv{3Le%BFKiP*q`$oB-98n6-?4K^>l4yQY-w+4(#h|yTA>@pv?C6vlf8UkU0KV zNpr~-XDJdv=TvEUA{_dAQ0dc(!~ibh9FOnd0K8s(uE;h2GqFc|IZ_nWGl3J&p5a9OO(Gy2SPQ$`cZ&S=BWLY|(V zpIK>C-1BXwdhXb)r(FKtB;5y_Ez+qQC!Dv)y(gxf6mBpd=ynW0w*2Fo&ouD$rd$zq z?~8l)=RQ;V+V<{7_h*U%k3#nh_0>-WC0IgWtl3p!)Lr%C(DFgJ?!R4sBsiMk*Qu?x zmh2E$Wv_hFMW~>%)xtNOr|{aII21wy9`o)d&2lHsh&2{ACil%x)t;fMG|}J@d=lqo z5;C;-`P_w$laDHRxutk(^uDcSkFRbCe)|36pMk~a%afX7-jq>pCZoBFlA=x17St<& zzqVw5%c-W$+l=eG)8ywr9fb=hks)QmL#F=6h^6be6K7WL;&TlT*)&W>y@k|v8#$)( zFp3RHI@v`_O}C8O^~~1Ci(Yn`Js2fL%tUM+M|@bWRrfpGLdX*tf=4H`Tm}qmRh(y5 z!(>^?gxegdIi3#IKjTBy;zh+}rpY`XTt)s6n+QPLWvTUGqqtH3 Tyf)$dJLH;*Hm*p??CJjk$Bj03 literal 0 HcmV?d00001 diff --git a/modular_ss220/events/event_example/icons/readme.md b/modular_ss220/events/event_example/icons/readme.md new file mode 100644 index 00000000000000..10a520af81a96a --- /dev/null +++ b/modular_ss220/events/event_example/icons/readme.md @@ -0,0 +1 @@ +## Тут мы храним иконки к нашему ивенту diff --git a/modular_ss220/events/event_example/preview.dmi b/modular_ss220/events/event_example/preview.dmi new file mode 100644 index 0000000000000000000000000000000000000000..0b1b4e5726dba1f452ce12c05c21702908c44ad4 GIT binary patch literal 15270 zcmWk#b3mN`AK!8aeTg|34^5ugBg?kcGv0hjf8$YLM1SM$YEL~mJy}y%@4RKU90QfMmyMJcE}DoV<8m~Z z>K_p|If}3h9JMeYD8(4SktQv}O$;QI_*vJi_p`RaOIzDj+x=8e>+E^u%xgSd%ub%) zWMA5>HF`9B)O|t(>h8BOaqnH>amAhMRzmI(|2fAyz@Sk2FKe9tKDW=g-bVLaCLaJ3 z30$AIa2!zO^HvjA?XgXJ8o`#zroX*EmdPWeL`WFxU}hY!qLM>J^w&a}4VqqyCt;`5fw2$~wOc_h0? zFt!d=f&^ti`k_D;JRMzXrT)(6w-&?wo!ke2uHg2F|Gg6NE#{aO9uF_Kd^00 zjZgffd2t|2c>yN2l}SJjekv^Yd;8`E39k=&AnFs zznuxiZa9jhnL-|>8pO6goHaeO`KWMl3ynFmw~t2PxhK-10_#=qgHO&e8Xd0*ZETKD z&d!aIO$!)zCto;Y$QIHV2ok|!9)B36Dum^j9(IvQfYk&MyU>L$a9+?EqgK8^_mEC=`Z00z_;nQR+v zcFw6|PSR-pc8_SS8l{ZAU}Gul&~U8xdHcqW#}i8dVeS}QRpNoUqD=HB$?3On%8NtS z7XI?7e3imE2tjXeOj^r%OBmFkO`>qlY?k6*Q(a%5LaN*}shH}ljgsIRrZK6rnMLSc z>4GN|t2Jq@i`@HFRM!|=-B`Y-&PJQR&Lc2v!-XQLjZhrGK^_Od;ug%tgpRk#aFXL9 z5J$mL{X;?u`dHUEo)8l9lh=bv*EmhgGvjmnr8#}E#f31IC98D?b#Ui3kGec@IsJss zF_F(k&tjr&*2N|~*44BEJ2xNS!1x(2NWK<2LxIj1BDw1A(hsCUdHEeN1Y`-cYY5Xw z{HN9PiT1TCeu2%<*v~E?>NDGRG=#i&bb`Vn>+`ITKBUCs0>gguXph-vSq59_qLcuN7tAJE+hJe_ZO*0sw#%iJvb9f2umC3Mp>{Kt$+5zc6q zq_|yHtx+Y$=7(H6#uOSi0=_F`U>VbF$XXUK$|Gx25N`HR5UjoHb zJ_QxJ=7{l2kAGmWMdM#(NNEmGh3)UBS}gv*=ax)bdzG1nFOcjX%s+ zC+UnEwuXNW>s1S8V3`LMdnLho=}0X+g*@n0ze2yId%9dtXGax{zNC0ihDpLY_IDr*_Snsn=4Q!HRYah#RO~A?u~^-@RXpvNYTp7dnRs zEqy};CYT*`GAMBi)9ze`Q=&NP$g5G8(<(UqEN}eMUPNCpU^c&LgY}0l;Aa7(0DCjK zix9QN+uO{#->Nb0E0S0wAWl|MS(!RSig9Rp8EwaQ{sr3l{iW&I9gR$V#VV#+)BxZsGGn|9y3@IO+5sN4V5cD zJh!cOvWcj>{py7Wo8@qJEx$6x#A8To1oFQ>i2r6%c z3V`Au2uSc}F(47$KA0R!$ZPfoN7z*-#a(u@19HTg3^p zcYX{!BI9>|b3JQ6H|XynwIN{6To__!_(VO%-SZCc<;LzManAAS z;PNrnjB-r1yg$+(xx-3y^z{noZKuv$n$Ekz1lm2|=;*fJEjd3#U;EB$u@xleX<>1C ziYJ(zoSsZu*DWm>6zj(QL4>n@TJ>8A0!ujEjw;;Hds|Hm=!QY9UnnUfsS6k_OiV)C zvPFE@&0NhxLi!N7h=WgmH{Wq+#PQ(yDV|s>CUBBMXZr(rf*J`wB2giF zx3ok*)7)GNDN`2prr|Q7>n3oK7TrA4Bns1@NmjE+GAm)r@>}8vmQW5Xe3W>!DxYdc zmTAJgZZxWlxZIu(>51cqIgV(pg!gSHhzgREp$c06yU( zwK9GqpACl$c74k*J3*KEFHgBmt;C=dFZiQ%;`I4FQ5@ctuq=wB10KG0O8g#?P84@2 zHTy-Ktkz9L0dYSHn_>ItWgiWKfpWP)AqyVdM2UXo)K4k=L;4~N&U(r#xZvMYR#J&( z4}NkPu}rq&fCXz_5crYz&_OYQE>E(Xk$OTFT%3m^wa6vQBo27+5btDUofMa2K`b;L z-hRcJw!3lc(O0x?WTLBCV{)lkH`3W3RFf+_OrXVO!nc@YxRd09asW)sIg9#mR44GX zwC$^JmVdv!i5BvRGXViVeZ>0j;eX!#)+z<6$#r*%9b_ZOdm`i@eN^W|fu~4H9vxTQ z`d7P528t2v*&-hokS9=WPII(#00)Dxt)zAFb*Px4#TmnxSa1Ie7(!pJc^Efsa4`dh zmgEg8Amk~=veMXY4G!aL2uk2hUie(qn(5H?DjVCCJKZ^}B=DG8sIV0TroB>12ti>> z0g!}?fM$c(|E+M!NSNHzHh_%>B=HAbSw6C0b71{?XSLNQhI#@R73j!i;_ePlVoN&c~4Z3~OqJ^~u{CJ&Y)*3!8}!w{L#` z_fivadkL)T)X{j%tmWHuOJ)uyt?R1hxty)gjF~<6zRrht15C-(<)?WN85Y;qjh($Q zQn7#oi1qt}Gg-zN@dDyarw429#5i>VjuWSl&vsWSk2@$ySZm97Y=y>MQ#3PXp2`{K z(E~KqJTxPC(=0`dT#>DLqt3yzvzZuLS}6|mn?zwr8IA_H*3!C=T`lV1UJIg98yf_S zT$il)SUh=CK#Xkd4pM&#KKSO|?NT^SgMm;@f@2&VuFHXkR>_%np@FkI!JO!o2e!Tw zFu12_3p@Cz>rg$@)pjGPC&Y>A>6`h{=lO$igZ+Mk0~@tyB25HOQ86d(R6&#F3x{qd zg84MusMnsE%q^0!?T<=EtUEmUb7fRlTR9|Yfkh-FtFBPTr)OVMP0LKEfNly5&|$+> zvlI?G$(u5w1*xX6C`|Pmv(idOup{>4rtJ{?hb%6_}{7*Xi z$(OD5j+oD`ysEiMJdvdAD{PI~kj|sn>Qa;O@4;iLq@d(qx&$X7F_5)G1F)?S=lQcV zP^eVpjbU8I7s3@S!$d+N#1Hj&7FzF$NOI=_oEVKHcE|8TeC;}*;<0Ov425dB1%nYg zC`#$uXxKT$ENIxOgmmh1ScpYPbjT)~OHguM%WHt3M5nS)GOZF!603yh~7nV3tP+pO_AlHv_; z!T^~Lv%|Pa8C5JbSY7T9_HPHoae=HPM!M3Xa(vyCgc2sSf!F)>t{oNOzUQ|k_KTw? zZAW@zOViYzu(+D$_I_@Pclu(2O{(cG*Qa;}`c{`?PZG z8@`slr~gMdb;fh)+jlq43rtL;BQoB_llo|m+6{c_!2vNtP$rjY(1~fSb9spVRU#=W zDH)GG2)!S)eEEhF;EJiSyDJS;Ly6`_@7JF4lOy#iGiXy>&DySW-YEWu2xT`AXCC;R z;}O`@BtY)5DJru=)y#q+AzxO7?-|R9q@gv^g-=M!|4}0FjwkW3XgYt4SOB|-zUKNOS`*qHVrqOAHKuN zHk|)Wn17#k`xAc0o4sGMy~s7G_cP77VPh~};-7S)+RxSXiJOPNPfvfz6ml9DhJJwD zIO-sr8Lc9mF77DB2?yTt@Y%2LUL}`;=@z+m`1~HpgdcBo{jMn5at)u7{U>g=P*d#7 z9ggmVJZ`E6Ja(0^v_*dKE|&0qRnR3w7E13Spu=@gH;V3Ac*-N^uZ21u)+vyRwWDAP zX49iY{ewg2*E{{~NJLaIC6t(!C6txhS64Mj<9n@oJ_FBMpY7ToZ@BRhk*RB> zP~z1$vw8%3>=LeC#U9vXddS$6>YE%cxj@e>`iA>fvW_lZp?Z9kxDVubYp7Z&+E(s@ z!nY52tF2YNH(Qc})DufO8~+uTyW)EW)NG`WU~W+=)cj>b5!VH033?_@>tJspKjpc} zWw|mI;YeT)aGL8<7WIR~Q_NAQE<|$2rBYRVt~6Yl_5B_O&UbcCaDII3*+NnIc?Fxk zqzx&l0xljY+?B299%f7N4Q;1XXKnZV?rWa7(NyTdp*_CjelwYj(bD{1uEh7~HP7Mb z5Q3({%LbR0)=li(Frf&s;JRdtc_fG1dGbo1XVUhRR;b8%*S!cYFJHP|nx&~tR8JTt zjZ#MbOJbMNw+yU*)n?z(iDJU|j|ZdhO+Q65SIuvFl3VA{{HI!}jy3^=ID@ODj{ct5 z|N4c8@bR^a&3Jd}uzr%bAY)=D&NqTFvhn@xxzS6Y=PM@8y&#H;F_VCc8F2}^Ud!{3 zmgatiiB^doHVtjQggdlnl2idtmYy_p4)jn-1J6Ba2rhEXu@Jl3CHprNX>#{(J^r|OO^9-Vi{3D?<7pBwJkUS z>_@fcqGxwnKIMJw`b`Vugdw*SfT_cH0zInuW>PV#Zi=RfJ`h5PtkAN|pM}Rmg5fad zq%|yw*7v#nf`7Uy!3YhARovI37>&!Ps(7aqGBW(^HW1eME{tCcKS z{c~}SXloOhU*J1XQo20lwrs3(floJQk_QH>4o+`J9tAeIH>gGiZ2%q6Y*^JnK8B{* zR5x(7@et_%sQ2y;XHLm~bEfoOWWhXEr(2%n0ni!Sx`)L9G4Vo!oh?4$Zz(0Pp>BM@ zO&+7w=&U8&Uqiu6w|4G z)o}~`&E#JkxvcG4s{U!1RfMyUexOv-#%;u}JcZkSu?W@u!7oE9rE?4@BAO7rXYsQ4 zjygCnme{c5Al*hLmrltqOeqQk~F zH@J0H>pWVf*Jj)J_GRq8{Wx0=hQGE$jT|pgifdu00e`^u*Ex0kuJrisF8z6Ut)-_doM!agf-Vliql9es3It(GSNRZ_Kr5borGW((>4$V0uYZNiJfX zzPdr3d91t7nM~5GLWFR%kW%`6~g!V*xcuM3LMi@mW zUyd$A>=8N=lRRpU&kad}O5a*j#@;xLu|#c zcuXvvqxO>Z!l(0MI^mQB%iwjt^aEoFV)O8lPOu`n>EmYT~5N5Y*rR%=|nX!ks3fX1u1w>)^8X2;by}pW!)_9{`KX( zb8AVLcb-qc$u>u_Mk#Ab___S^ql*9K!m8hZK2HW~Srw^}Tkv(IVOcePzti?E4t0k( zm$HMI;<%#Y?jq-SmPpU+2@761ZvcgfOTa&l#p9a#_VMvM-e=&R5@x@zyy=03Ku?>f zyw{1X-M^Zq0E&S;w8%2rF>V>4M8gWrgvK`L_xjpWJ_~O2*c5TKBgG*y56Q@3!a!buU<2*Vs#zh<(UXYf&XO0v^rOYpH*+ur>r;<`K%%0g<+DsB5IaWu0;rvFg z$1mE9IJn#V{R`x6;|N&77nC7POY0kjEb{ckO1$oFy1IIZ@M9K98_%0zX}Otumje_# z!+TMRF3oeHd(S7Qb~R0Pz;|1U?C5@gHg$-prFW_fIta}PU03T?Z(KmeJV2dXXyqAQ zmPqt!2aXaGBt3H|{N-S-bKA zrVIHOF|t@rGT9H8|P5V{E`$6CzYk8Y4ooLS4v8RVw3UfV?K>l-e?7Fp5ERvzD^YN5Ujg#&2vNt6hTRBGy) zOSNv9b#oY3EHvST=Q0Hl7&}4QEiyhEBrRpO$rAPp%zb4Li<%B(_qznPKkZfK)owQU zKg0Vx7jeIwLf=v}7(U~$`(86OdZ6E64g5v$Ne7g?|6U zvew$isKhjqD6H}!i~uVvLwkjeZVnV+s-~{J zp{IZy0xs$T(~Vg~l`hy{tmpc$zdw}tg)+b(E;QFWrN(k%+fx*TT8q2tJRyP}3cnAv z&;>OIc-+pV>H`qcs;iU0)0!SEXj9@JJiDfcx%li>H=o8b(Pbr{R)kK@&Jv;r@at)T z(|`LI6-LEX_N8&|MJ_(XcokMZhoPn=-p&ym-6F;Lz1a)!eC>I9;=&Y9ff5i*p@Ajv zt>;pW3qB0_SG|TVq7MC4F)gkXm<(2iMP(t$NY^&*1npOIl@6CzbWbh5;1V@?BS()M6)xf=dZJrU49^Io5^(bwJxr3m8E=$A zV7OtTJ@|XII)b=V9?iU#cKVspmB_h=Y~MzfvAutT#Db@CBAv%_59(kNr{5-?6pnRb zVzhd_d&5u^dO$ZQh*iFu5sv8q>#5hc*C^}zcWDLiJYa_-B%hp*aU_s2Iz8=-i*st{ zB%xP&4_ZsS!lkc`qV-s(mD(?e7h%u2|-4-V69n zwY1>_xtfn{5#+CL@%(mNq_Nl%lgGD%(*4_2(iQsc<9TA;0^%(*``PyXBz&nA&Ubs< zib^Wg4rZ}`(#Z$nav*%1;w3ZU!uKb9Ye_1?LmiI|6q~H}o@r%_O^&aE`ya6U!$5)E zIzi8|5no8v58x9)tddO3KeXp^zD!2dS{+jT2uUjXuEQen`++T#02yv65Rs}AHA;(^ zP8foZOgEfY*PSm{1Ma;3*FR}nKX{Ffj9)APT<;n~EySksec_M-YO5JB17gE$fBm{# z&wXJE4zkSg%n-8gAp2LV_Y0FEr+6U~vXI$pa#$~`UmPcFZd$eKkR4S}wXzap|F(Q2Zn;8G-asYy4*Zk0CL zN0nSI2A{w+!lk!+%9N)?N%ZY9?)uEVJ&CXVl8U_b{NslLeyJ~?lueaf&$VHws=hgv z94j=7-#2TDu|#Nz`bOevH7_}AK4bISavrT`PEB3Y$o!pFvIIE>hqUgRKJt#EdeiP9 zARnJGVqeIl_1oajV!^|BEe&;QHAV$Q+U&j$gviSA>Xe+6q?_^6f$>(xRmcJ0(>3G1 z$D-bA7Wq{E<3^uXV&hF?hefWQU7GHU2@(n~c(w04c)2gMIkPUA`pVgYL#HJwT!1q? zX&mpCoukBL38ublc43u!_Ya+`tH!aU>3d7ioB=?g2IfOT zMx5{NWYJ|>%yc8iI%WvJ_k^^fb@G1rK)mz+EPyZvE~Iyn6kJx2F;A7;vx#ZTEn9A2 z$^%dZo6iwP{AH6@E5eg~j6X~#QeN}Tq6K>19olm$_6O&vBo^yv#G4aU<_HbU?&7P< z_ZM=i10!3FoSl>UcA{o~Vm!Tydb!&tH3;9x1m>Y-uU>EJzs$Vv`@S4?ynVGZXd9=o z_YBT`fAk-&d_8*aKHDc;cn*Rmx0q^0r6lkC?{Lo zMhQR6#hc7$Z_1Vzru$4r%b+SSkCatZ7AUy>t3|XaJua&#tE#C98j{h0$#{&8F!FlE zV(N9r0i&MZzLdF|^&~z%vfZzGi!D?dLXTDa@rroH8iAX-!R$Ed9DtiYeAMF|e&iY% z!*ACItVL5D=;mvHoo^(unrVGA9eDxAV{CnwYE}IvZy*z>bHeNILdowcfh5H)mWo$x`&){~fe$o8QdwKHI9FX>hz7 z9r@AflhCvziGKG4u^v7DDmaAQ<1))^^g)U2YmYK<-b<8e*1K^wO*H#&2mL zs*tkl^LCxf5c${VzY?D7uzl;+w(+7*pJ7t$l*5lY4E~o%nlUc7dpD2GfM;xpt0x<1 zf;ty*3;-i6x}T<)p1CbO4ZOaKpOhp*n~{AgQs3aVw07!z^WVfuJM@?Bxh>3TH+-5- zCBh7y-4^VbikA#4L4{MopNrTLbl-49Zdh~6>Uf;>H|9}Xq(FZRV`y$YzCa;JeYtFM z=2=mKJzA3j>4d%3ms9)e+e%-IKL}#C>-W{QE*VV4Fb_;-2gg|Fmq}q) zT1l>7EOTl_p5LYai~U9=#KdABJNGfOV{3bFB#YlOF{Dto$>|nNUB2Q+(`b*qPnwGF z(G2X?%C$b7dPXPV9P*1li`FsHN5b2+>>zAB^w;#NVVKI3#tsb)oiF3+>FND8bkbK)hhJ$EO-F_CL?v$iLO7hS0pfo;07u;j2KNK+cdeIB0YP1-`wBn z>KmH6*@e=M$t7~C0k%91=E!tLJkpHHj67YFReX+;g4KfEFl(_DZ<}8hjQ*V| zcVs3MxM)sA6_uT5QAg4mzQw{ND~%KZXXhI9Fy9=XE8tnjyC}KW1=arN*vl;}zj&>d z5NCpEn65{0`E*?AeBIAV?tSv<Oe7#28Ao9AFw?%gE9yN_)_dRmof_o`;CkXgu3)L zo_IxH4<{}*gRrXF^yz%hT94Tbybe7xa>|M;=NhL!(#fDIg0|;`hpT(~hXmP&No29_M2vpF$5@u{MHH<8^h`V1tQjlN0~ZS& z?Ihv*mj-LMWl{mRXs=zW;BJ6g_K`#$I7pm=ia7`(2KeV_@o59=;I>(gNp9xmTb3T- z=QvIg5lI95lk@ZHy0($IQtkA#gR16w`OPrl>yztMzvoj@L7s_?*ENlUPFd@*CT*Bb z=@JDs^hnrv%--0V4jGxB>mxVmUUysy+!Q|%FiyOx@$+Tn)jC)B? zf@%X@-L2u|QhxKx*Wad}qH33arP#9;`sRwg&4_J%_P_k>*+W9Ujpg@#FODW}+2nOf zEacsi)!~w0=&`}hAulU@zg_dA-3`@-6!e^j3DHmuc?bW!XjG3>Wga%`OfFqelfbpk zZBfqN>`NVw$45+0Ku3m$Wqby=se}A-*?kXD!PA=v6+CIvRCV9p4d3q>9>m^u+4&t! zVsjr3?=PN~oPRV|kM*jmRW2;8P!tCDrnM6DFkyx>o}9*bUOsAfX7u1uqKBsxZ_vf_4l`X?zPNr;LuaQpyPM* zCruBMuT2x5jQvj*e2ViU)e9a_4fjNo`hrLSu8>ZNly%RSk`j$r@0 zjdzyH9D#xPC;I!W#+b@>mr$EhW;1Ul&y=J^I64CBjLp%F37$W5UAX@l6mjZk1SV+? z7!YnuEhP6Qs@m{cxh#|Bq!p}L&m7-g)Y3TDbnRy8amjUKSz3r~?EUThA3 zfHR!}pZ0}hZmrnvg8xqHB)k?Op;{0tEWrm%oV4Zf*ju$(b$j9wyUU^2sTv^f0Z1l( zXS#@pz=X**viot9nHeu^*La@|UQ<35hVv*bt*-9QEsS5Y`|T}Uxcp{i5yVW(&Ner8 z3#BEgs&A4ZZ2-jfLxA1PR)`$^o^m=q0ElU%OmX;={2zbis@JY_z0;t&d z=;t3%vro3uqx*WDvXPLGkm?DKr++py@$<_`mz-(!J9M=uhN1+b6m{PFfKCZd-C#D4 zQpptcMD-VaB5B2&jPRNX|1pdKy_;wFE9#x~XY%dmjaR%?kbyXV6b^Cu*P5_qQW0KQ z{>;SQK2uoXA0j0WY zPV;@^v2B0NiU-O0tZ5Ge75v;uNOee*e?5+!{QMo=McI8Pi+8 zvuM8^ooRJDBYs`;m1pIUj;M|08R01UL!Bm}o{~JZHAf9ML8GJ&0iW2Oe~&n&F5*Fy zPN{Bf4gRdt3JQa@7uF%KBderE;?&#k>D})`NngHkf900U*}I_^G}D!_|2yEpu{n8z z4zph_Yh_tub#ylhggC_y{si>^2w1QQvuaMTkVplEBfcW3hIhPOZ@3N}a&`Pq-MLGv{bE&@Df3TVc|BOpU;E+^E}J6?4h__bS-*{{~m0!uqei zd0|Lr-@cc^=K^amOyK|E{%EfBN3%;n`6)(9s&>S78s+Z@t*}P& zndAEObRnsXVnszoqjGbvg0LGp6_Hzl3PZ#0dw4fD_XD?<|GQ(Sr*yUFH4Z1(nIy+9 z%dGSz^w#V-MmzI}#JBY3CPbSak5`{~}%7ZNb`v%K(MkmYxNsFnM&&o#*gbu4jO z^|t*yNM80f4Uz0S{$ppMgU)`OBHjWu_30Mru5JW*F;O=Ve78tJGK>NmBUIWjb#_jr zc%E{1I9sh@Sz9hyIo5G3%l1TJD&%+7xczyGu*|PTY6^}73sl%yVLEm z=KOs9(sI)%<7rZfK#YuHq1MjiOVt+;7&AeA%LXfn1$eZg;U-qkXyirQY&Gjkduo3^lVkGtG`sRR$*aKPCj*Z zqoc3ev!L0lp%7Ag)xMVw}?gkPG7!3d$1hTk~X?Su0yRMFs^I)TO|BGTVKvYfb&i?v@Sm zm-MKB0|M-axl>OqLNCkqJ|Ar4eZ&xub<;VOWrS~MQL@0(w+-rXvxe$p!JZ8}WoJDDH)+D5FNCGvtG`E@lu~P& zNPfg8iHz?Q=i-d#Lyb=uK$@oXgzK3R{zgnmWR8tOMEh~JI|ZRsusSkz_m^_R{W4LR z#_aDNZ%5DZb(_l9%JcUm6x}mUnS99(P7>8!K99GT=v4OH?K0wzn6a^GhClw*H4QCn z`1Ity_sPCI^k6;ovA^Z4k*vU+ME{|;H}ZSKmWj0_Z&GqH2cP@+71;)daXd-Y3A2L( z!E9O}ZduTdP{Y<6nL1il?nG0Qe>fyuN?d(pXG3)VrSESgP2hdXQa^ZYOO95ch)(I<+h)!%QrpMC>s@T)3tk3OjrSLGbu9v6HY|J<6}lmjbUkN4^j2#Hur=JFI|xo3Fu1L zQaJrD5er(7_>fmqw-L;H+CSL;7?&$z?rifZ5f;Yi-#rK)atZG3+_&v_;P_^e$B57T zZ+tsjxuDGbVE}eaeM@LiE~#MfjSjmke2$1%QN_=UVpB~RrPxEpI>$531F=4y9!Bmt zVW0yGUXwXPv57@rcR#(|zl-jrcDxXTbC40B%V;Po8|-x5`-g>LQ8;-xgwr~NlmG}7 zU(j>W>8GITa_^bA^D8--AI>pQt&YB>Sn??KSn-?KI7ex=(WAoT=3$f0pG~bf$)f&m zSwkMd&hUg$bOQ}%4(D~I!GmV}n{_SjjtJF1210|>XK>MDX&Keu~G}lQ(*Jo!SWp^O=rJ$qd8ZPT%160lim-m8hEtS;+eB%N!1W~ z8agVMfLl<*kga8LlCx<7*z2v#^0>S$2SQ>BJ0MjwE?P2pP~IVExuTFX1wB-NN(z?i zcGaiC{gXnVsqg4?Pfqc^;D3FQ&9}Ikb@>Q2yf~QhM!&{cN44*Gl@Zgb0f)@Xo4P#h z)+qX(%x}vUQtf@PTV14d`S*j}^X?_OJX^n+?Fe-rJ*w{$8(S*NLPEVYOw`Ax132&& z9)oBRkb(jV65HSlV=!P$ql+%6CYG9lS%$LQ?L~y50_n?Fb)v%D4!ak_%X{E+;cVQp zQ&N&Sk&@NWHD_w7B}|dXsNdROR~q+R@}|X0cmJXPqpG1nb_J=J1h3h3_x8MP+>EPV zS7F69@^92;t8Ai$3oZ2D{N^t>DD^m|<4)kA?eN_=IgE}s@r#uZP#OJckyCtc z<1a66YeZd>rbz?nsJfWfGQ(6r$b`bPKi41gEsbV&)4>n*gMSLH-9(V z@e7C=dAmouG~b#|#24X5Fm0CgLFqoY&X}}mTNg7rp`%*ju}|b&Em`JXJPb9+oh;`3 z`X{hrWaHdDHHGQzEfg(Fd}SO{5m_+EbQRdjcrnh*H_rp&d3BebA&zo>^ZHxXS=F)6 z9}Np~$%Nw^qpy4Ky+o0oYc_k{V$-2gAKmD%rh=sd!r=n0A6q+0B%%{k#GbRTV9;B! zpTy@jvr?elH?t>`ugUe8P=AezY1*}%%TBuZhfIz}3LU*}hSpbZF=C8AHaZpT>B@YZ z{gZA44d<(7?v|-rjk_m(Oyg&gf}2Y<&TJKIOktrUe0GONQh;+ z-3ox(#cM*L;FhVE(2^B(Ev2778ksYo>^q?(3V^e%g~lX$(-ugLqX@a7tHR^Lvpz*&eUhEIFLA)3lE;iwxi;F9X=_1^lGU=@-SNn5$#6-;?G9`OVeCw z3nzcr33Iy8;s+cxcUtLGeKeYy+ub%D&XWG})h)6>UD!#Pk;5QHBdR^`6RxlXu6PXDj zxTW{#N~Y}T*gVnvkd}hyKXzF(F}Cpz8geOM!%E^HEOYgM{_8hJchMiX`l+mc$U@~7 zuoQZyb)9Ni`>TgS*eg}+@$k8~g3kT1?%#KCU)`wLZ#EZSw$RWNxf7gn|EIYPy6AXI zA`1VP+)-Zl#nm;M-n_23aVIY?`8J(D-C0D{im#I9ya&1*ugIl8rMXb65^l}==Jp6C zpv=k>(2USjb?S+)uhO*C zIJRQn)jB7)kAKG8+!ojoE?B6&7G%}q%pb>w)43#c#{k9UQ0lBT+eo7?*Nqjs5oIkz z>}<*yE!79zxX2Qqh*)Q9$fo}tWt6ZJwxKx=9gZND=1}vr>3j2#tJ7*ab!|S+B@-AN zXB|i?;c`y%HPGWm00M-sg8`n;T_S!DJ@+~+-;+L3nRqd~so}>5Q&Sja3*HSoebXn& zO7hFY>jKg8G>Sd-xcigSO}mnD9_g&zn$`l~j^`3@o7K-<)Cs0_PBSy=#l7CmW_XwB zJ!DsEXX_H@yYnl?M$=j{Wp3ug|VoU#QRIwqk7+&iqtXD8iL%_2E=xD4$$omv;`;{7J=p)I5uIIk~U2}u2uJ0U@w$JFOzxb1U zpo{ERq&CJ?KTOJvd5CYXKS*k814_k4NhHs>)zMxv$E;RDd8LaBSy@u(%j>RH5*QDX z5&ki80u&jm13fi4a}6Ede8wMN0!Q{DoBu{64vu7%mn)Hrc#%ZW)P4y$&XvRk;5pP! zG#+Rt6(Hu&QUU`Gu;I-OC|?Q2bfZ+5rV+X<q&yo~B(C}y$N^8`HMbfJOff_KxqJ#&WyoV=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex7wuvIWO z2_(cW95w=s88Djh%!DDZ88aRTJVpW{JQ#zPVHhx&MMg6kNfttCwYpnsw6}U!S9Mo+ z?ORsv``hk%&Y3@MN;qQXMP$5;%!tf)&pqdNe&5gcd#-r$GoN;+9(a%!o_m2_qeZGM zkALPfeD7P|!quBM7!C%MwxHMRfw6r4vyZZP;w0^fE|)G{;^xhpG&@Ze7LIUaVS(FE zo+R)zN^AVUr`2l{D9sa(e-U4MEZw@r_17=5v9``|kRSkG2?EbUBN473LQ{`pw5I_< zZVRUGyPJt4vy^3uFFk}1SRs)@Ac2|bIh+F}C6}*V;}`zBU*_YV_!wC_;>&;cO&awM z%~lg31VP{8l}OVaU>G)b5}beL;bud=?rMyJzZWn~plfz}@NdL8|TfAkS* zq0i#tG46lk1KjuKH`8j=c;~y`#lz{C>(_3uy1K&7);d_rM6X9ywsDukmUl5B@9E1gFMZsH(HcsiF1xxt3{fmeBtrW zgL8DdT|WBJKjBMXdWtKTuQ1V@;ON3p#^W)=;fOGjq@zB0t{4qGJRR`B{SQ#9NA!9< z7Ut$DO^N4ef*?TLw>azaEalMbG(Y@06h^e$EiPWV#Attja~41JFr`H)h4|68KjHxHx%)1BPckz*&2ull%!w0sk|YU9 zwoept(FM+&Ig9fYq0=-Pb)qmPjw6aZ=b?x0XJdU0Plx1rL8}uJ`aVMB z%*{@tmBJz@O$o-4BpG2)V{ZO1MP8tl&nV4_+ij#NFwU{Rw~dsFD2xzNV#<;;XU}qc zagm}d==b*+Ckc&4ooiPwG12QW9E>==c#KxFO`7F|VMGvyh!6kPZ@9nt>}NSVGmR2Z z8pqY8n+*Cx(*B4b@ac8CIIYp4PoveO6~x4in7i-0hv#0sLZj7W)E}_9wZ+9tmq^ka zKTylmV*DhaSer|z_7cSzAq#ne~&mU%-7t}jlq^EfB z4R7G;DM-3@#%AS()_03-~ycGy~5XMea)Z)%#)KKUj7@lXB(oXg45gr%EH z+*-Q9^wbQ;jx91hJM+u9Ii)?LgqCJ7LCABc3 z*=kW*i!DmZH06^|evu#gp&ua(L$Hp)aKLalz|%gLu3zT*%~j^758=C%PN&6@BS&b| zY9z^+z1;!lFPvvI91z4YN9SjWw5Hx^QCfqR0s!X(d0yaq0e9YhJ6UN_%0nohQ>X9a z)~#iv*3=pe!YCvRBcdoIi~_$lUUAQyoeWv#8HGNEY@14dXqF7b?Q-wmL7xsU9Mfb!qTl< zSmRI%Ca0#zMkzvRgj9@2eS8hG^Yi3Ij)z1lg^+?Q$%tw-jxC(P^L%nwa%BD}_uTUk z&1Q}N@5_KSBw0#s3U1z5V|i_juYdg;y!P68KKzjn^VE}%QS&XETf4NHEeh)xkH@sz z9mF4hL)MBujE?t+*<_r6#V& zWY*wo52+=AYNDi~5a5i#774q1dt_SP9lXwD9PH|3gdLZ+~Gsi z8+9&RT1H9B#_AGB7LHHF^_07UlwOyKS1yL5&-0IiXbeL4YYs{J=+# ziiA;Gu)Q~6ap5p&o}=3#f#(qfIYKy`C-9|@u_Z1^NXvpjmM~fw(eDp%fls5+rq!KA z>N>s%D764j5lBTCM)-lx+;PcxG~niqD{QRXWH{JID9hgdDtTVu@`7%6l8Hv0y{#RJ zq@-1^A!{L{-7U&2MQD#aFTeqU>K!teUZz=(sRaUQ`rLlogOoyY^~_83CVObl z$61HxY5X7{iXx&YW@2K7y`4RLFJyIPg@wfhG8%;KfV%cDR-mL~&>zqr?b2V_=js(h zn)b=^5%(UQrPa=8PBxjCjM!Yw+1oDhe2wQ>bnMaYOrno2oaXdhF`aIg$Pdw;hwuAH z46nX=hV_jNOj(jADe3GHf>4nrhT(XZ(RhsSd1T`u`_A#Lr@xM~3Dy)8Wl33-n6ktn zC`&;y9xxaV$P>Xh0hi~XCC!NmmR8m{adL)*g?WM?AdX||%{puQ`(&Bq$jKAPJjWAI zN{QzO2n1zm@H`(=mc&7fQVJD@l(xhZ3CaEjqv4n&%?N{-y}f;MYrtBfNK=nA6MjtW zX-ttZ9Ou-=f;5A;76Aq!1$(=@jQb<>@jFf+rNo#LDGXVWG90D!`y-M(BML*ZEJG_z z5Jxy?5jv+J$JYTu`Dm@tz6U~b@xo=K5ICoC4h#k@B@!PepwXT}2%lq%iwJFr!Vu3> z+;RJzI9GyANU}Y)_is_&%n|uMy>5+GU>I2+BOFpteMvAzkQW7Wofd^P2qa;>NvCdc zMW4NVgiw;wrdZ))tU)`-i-J-ZY-!0U$ZST#_ZjvLwaD_y^XGZ)wN11ykxFoD^%lqd z7Fr8f+t?;e6DB%MiZUlj`VLi3`VG+PQ6j5UaJ#?0cn!b@Anyx z6Y@Mq2yt)|6ls=_Jr42E*Ao2o)fYE3~l4SUPNScl*Y(W^;X*ODv`8MfrOpzs^L&7K`&qtJ|pbH;KgRb1YW09tt4UEv!oWZ1 zodimG1fB)~Qa1<#ACr~L&P?I3wAvk7jXJ~O9?m&R6;QgEac*#wgjx`2MOhSBAt{VO zD2FL6^{9c#4BE5!&SST9WJ!jnpvX%EpiGV}BW&Tw$`n&NLKD#NeDbtpSR}+wVQ#H* z_H$2>G-GB@E~0{%{UAa+=cv^i7?TnN3Tp+%K%>!MJRW0gfi5&!La7W+N{XW3$KLbf z+`N2^BF{;Sf^jlpcXtCJebOYSEED$k_8AT{@;t*@2i6dVAyPWD)<`Mv{eVD7d|xx} z57E;t`g?l}hZ%#x5Gf@R#n$G4*|{3Nr}2D^QVKL=X-d6Wr_<@sX|=J|GIMAOVJms% zHP(p27-Jn$O42k1C)nQJVRL&8mk&wD1-9nM)0|G2!4?;|9i-6qNM$cJVxqQzsTtLNLw@ z0>N-JBu#TR*S9gkk!2bEG-os%kqtA_B%{AKBDD#5nNj45BF{k@CMLQ#4AwY=5`=z$ zl!CZcBZ^`=?FkOg_E1{Tt=H*xVmhHCGYRu~n=89Rj=uR#U@hY`q1CJ*^5LEvn1ZDn z8{Bp0ar*mx993tV=Q&xHFdmN(0tTl|21Zy1D2y9s*9XRC! z4o{W{q49KtlLjXpzUP4uXa&|doKpxPP)cEpB@BI}GK@wk8yh<~=Xm=&-oc5JcQP34 zV9S!ijX0!zZolV2j@@x5qfwut$cf_!O_|~f5T2$i3J_2hB~nP7bNEVQynwPO@wJaB z3IgpT$S?-9&>$?@SA;?w^pXNi2|(!zv@cMi62MY{$t}L`<7Mdp;C7&Y0?AWkqFc-8OL?6UQ-ITbo!baL!_^A&w%HQb?%~fN%xQIh>OyC99>a z6j_#$rzr}@{rBF%<4-)r`~KN4@anl&aVBABW0%8+kF&bI#xo!P6puW7jqm&ZcOfNM zQ=p5oBuz`Ux3_V}4qBBBVrXiOn5SDwL8~tq?+@wI&^p0q{JJqA2h@ z#bDT{*=!=DBg=B!!ETFl6sE-Ue7rE`U;V59nWz@g?lkdz#rDoNNs^K!8DZoTL;+<9 z!b$qW5n3yx2+-Oi3>qY9pEMb;uLev_O<~OVJOAjrDnJNixxZ!|f5`WWy0zIS1rBQqs_6mZ+(0)O;}f5lh6`V_zZZ+?|;J@XuU`yh?Q$^s&cya)+Wv~q((Q?ndjoT9(KMzb>wMM-~qz((DtKOEpiHGc31-p1*2>addiyTQ{!~_#PA8309U@Xm>h1_4pIqys^ae;X}wG&)HbtsHUxT6nTNO-WmzDl##%v^We6egJ)h1*mrs21 zQ*^pre)z}l zAq+f*!+jop)0-%aMK~(*LFFb@?*b(RgVBhr$l2N6Vs3U0=LC+FwUrIl25|M(9Ete&*P7Mi71M=|Ni^9 zdO5>24iWb{2v1=tIdo(irwm)W`&_%a#!JtA!X)-1+a@y@S{lS1FNik(fS(X@MaL$n?2|?hK zrU_Y=RbpL9q!tHDyV^LM6EqtQaMf~F%0mjp7ryW~$#~3L-|{xBb(BTUc%0yQK0;Il z`~2${kXjRk5#9C#onAmQ?$ZqSQ7WTecR1q+J&$CZ5(IvQqs2kZ5>@SW@ZFzjrPO@!NpK2lEmB&1Ut^si^a8Yyq(y<}d*tIWo~NkQVU#v}~^LBM)k4y%~Y>DFN*VXdTiF z3Q(3Hh*3gu|6LLFMvN(Qpa2R?nIUbpGdrrAIM~baw87c3YG0hC5!adMO|!i-U^q_E z629=*=UH4hMjS*~>lmdO&p!7odT)D+=;&cW-)Ar!;s+i@mZ77VqAaSe88Ei;1Cu2E zZm(mk#q<5DlNN$ny@r$uXR8?{5ENxz^>b0c>e?n(F5f^Z&G)|Rok%G`7~+Otaq$SI zv}9>Y6vu3B?;?=oMb6~p6yyFfh#p`6yH$*Yt=&3t6jPQdd2ZO*9MNjU_yUxw%-%+$ z#b}h0rU|Af==M4+-&~^M$L#IzRGX5gu>zKtZ?UzpK~@%+QjjD$je3oXuU}w({xCX> z`0Ky^E3^tFp|3DSN)U#WCPN8BG8z%ZwTfps4hpzL3XL@-w#+M26)?60EJ9RtA~yyp zC8e>Hrlj5J@bmBc=lsJTe=n=6n=CBO^Ot}1G0Hq=XJ?O3{XdU$>eMmrxcwAJNe~8H zzkUOXf+8F7na_QRSI@qPmWs^{habQ?c3>I?=cN(r8P>I#FcJzNRp!OdD5tRb`_ z3VpOEQ6}S+=U?L4uRe=&j>S7q6V@V>=ker|U&icju|Ceg`6wlL;NJV__1Yw(F-I2` z*xuemr_Nxq3@sr`ssisR4!9!Xj-G0`Q@ zbG)d=sk`rCXM3NOt!t#om?QJ^1YXE!G-79Si{+JDh|-{qzyeM>tOHpp#z~G9j-V5h z><@{;kkXVGYsj;d(Rjr3FTBE`sX8~7H}GUYnvQtzzWbP+Jw#Dj=H_NOb^17Zdu@Z$ z_uhjNAcVp59A%l4^!rTqdW@{WQBYG6g)PZlhP4IbQ9&I0qzQz92hK4Xro{CIMUqjJ z8BrKemIj37M}Fi7`1Id=mfmEK_x=3;z~R|hHr6*ea%`SMvrU}QJo~K|`A>iG*W7-` zT{N04tg#$Bc7*e?{+lHh~{vbb+ui z*z2>lzRu3}Hv38CO%BF=N@M6|Im5j^N_y;UZm_etfgi`93W}^G%`-+xLbux^&r{-B zfZpBNp)3-N&CwdNaUbaiG#de>N${1WEDS|C!a9q<;44WWf&-mak|vd&vr6EcLAw$o z97SG|6&cE_@z@i8$1CSvqZUUF@y8$I%H@kpbh_1s<@@aIZnL?yN&jpgos7m9S0aUD zXM2}M3|b0u3|Ut7Th2K~Wr0#2JKOsVM;S%wh=YKBzt7a6DU?$5$3s@uHt4pycu@!I z6k9u+j0($d{=45}>Dm?kRe@3OwK!o8zxwD$`A>iLr<6s`Ti*00w3b}GdX4KhZqRNw*xlWwUaup*@`?ZG zf~jdrYr$BIG1Tkz%5QWK)>{;1NiB*orUHOo;G+YNy)w2Dd}}5NYb4By*;+J zxABx_oQx}IctAw7=Y6-M#|lc5AOt8OQBqaew04`d)eZ72BZ#B#MxvyY^!EnjrD5*K z9IGp9oIHMlpZzC4i_(HGe(_1Z`sJ^nwBWaY`vZLYr8E5P=l+&cCl?Vfee)@|H5f8E zHHDIrnL~&0w8r5u#$ZuMtq-t`hX zO||T;tv-o$2Xf9J4pJ2WzOGnVp63{2QBqgmTT2jyl%>IxCCz4wtJiPv-k*Fg|M;hV zh9{nQg6E%mma}Ki;(0z=3A7U2bI&~th6Cp3W?5Rgfq3=Rmt7bHn6k2yl+smrEDF&< z0C~aE$}%@@+~E4nn+!)o`rA9~?(UI{#}u|CNz$stC`-&iPv%7t_dW0cje4W9`-cgb zlJ(UU>h%VXe*SMcG&RHe`Z5kdmgf}}(F&A86lv9$VUSj$@vzEZg+W+FI!^F~#0e+` zc)CjDI;}NQDS8tV?C$Pz`N|c(^3|_!@$x15yM2_W zQA%KOc%Dx(8j~g&^;(TI%P37r;QI`RLngWt%pW<*x6i(WRI*Y8K}4V<)>c=U?oIMN z?|4VW-<)A@f44%{c}|igc-q4W$dfUC;1fgoK#drOO)0a<1mGx9ychz$(c}F2i_9E4#GpTBm<+gnSB z4W_ghQDSXXQ-dJD_dPuASL#Ane|x{*Cyp!gHt++$QJBi;NV5UGi5~s_kT`BK9F7n| z(ChWcMaJT>MXp`G&Y(ZSIYGPA=ACbU8#kA(bN1ykOifR+aAc0DnI6ZFo}@Q1L8sF} zJooH3-IGr|$-nuxAK)!-d6=vyxpDnE<^T_WhgyQdScChHi7FgY=mU~kEkh|QTvyee zD$t9xXz3wbg_EUJSS&(-_%4PO2LjoG1tGu*i&h#XJfsu{CCZ?+!l6jBl)&?_#?WlG z$ny*b6sAH+-oecYqYxo|{4hjGS5aUq7>9#vnM(mZdFS;N3;UC5z(Vk*wZ=Yl|#u|fG8tW|1IdBf``EXDWrIgjh z8$+o50|pjS6a~q6j8PsSkglXCETD1DRs$?7LW0r~2bHF@77k{ta}Faa4?&a)D=ek3 zNTI74X)IC-1SPdNu8O;oXwSo<@jai}xmlKO+$4;AP##E)6cu%ebi~5ZV>G)>KK}8K z^X_-PlQ+KcAs)Q%0RW;nL@EnTV@-AMSC?p%f^I<~w4%K*(yQI_Z#BQ&J*P zC`$z-oy7na2TqVuATb~u;E>XSkZ57C2Z5CB-C@P~rNr|rN|!k6xP1K@Vc=nK*fPW9 zBf^@`8y|WDQB>m-pZp}A)O4F&PMtc*?Wa#N8155A5qrDi>XMa0DaG?IJWsdN<+ zjP`xa+ur(SKJbD60KnYbVU8S`<=N++#~6Xqf?jU|ArvQ0o}d;6^d?%^ZjHD$M{qEx zje3iTiAnO@(CbaJwX@A|Fl1+Ek0cqB=LLg7pV25K%X9YjM+`>0WLb*Mzta{}!-c#G zz%~E?8skYsK~y|25~Re~9F(l0n${te!{82VGe@mnV>B30lx5W#iK;G2QKb_{`H<Hi_d1(2Pb2 zODoH)uCJm?b+EJrYpd(5uB@@MvxCV-SRGPIC~ra0Zr8LdBN#I$8FA-8O81$((BF?{l9#fji{_}hWI|HPW%+1fy-`^+j zBOZP9QFM{#%*@Pi+wG?~^YU3{XJ&cj?906Qk%zf?^*Zf#mkSpzaQ8j;aP9I{rYCz? z1lv2?jK?Fiki_*mwOWkUitU{(%AzW+VOXMcRfLuHSYKO1Yme2nHQMbCzVEZQyT|(a z8ufaeX0t`R+o3zrrP&DyV~LZ3ndvUy^Y%w*HX8VW&$(C5v$nd%ZhxOL&(N~T?#?b= z5Rm7VLvh9ry!Cdzx|;L3M<2!as>o@**5ICd?%}r6w{iB&%UryCk-6DJ4EFk@Y0B>I zE`cAA4P^r+9+lE_m;CX$b2Qsc@+@OwqD7JAy!`EN z^YyQN4XFe__1^cgv~+`HJVr{%-rgQkN_rDLM&l7-SZ!jX!I-C?evT`buAqMLfBbLX zucTsT<`AFxo4?`W<;$End6I>rN4awK3Q|h0T)E7{Z+w_fed^QH;ySHX3+;J~lM(4S zLHjK$;t295?fnafMYZqVoX64MQm+tQIt9V)35wX z-uDYX%c+wmXtle{&du}m(@#?rIsf8c{xZMxOaGkt`2}WXW(b3bPk;Kqu(h?p;^I-R zU0Y@}7_iWaSl$`)7cZ`nD1{P&PPfZo&YF;N)d zd0y2WSj%{rV60$yb(K$i>XU>~gmw#uY3 z#w{Lu>Nd_vd>qaz8RnJ2V@=6uFvJ>AVT8#H*REaX&bv>M z7Zy)Qq>yA;#?J0erBzY1yFEVs@lW7;0XTym4E6}4kXjsb*XiS|T)zp24wKn}QzuVR z+LDib>|-dcFt(&DGj?`I#Bq#s1xcFXX%FA`u%+etwKZzB8aHp=AdW-o^(NhJm(gg% za5$vj?-Pb0wOWn1UZdOX01$XpgPIjNHCf=sgAcL2 zT_t_)yz4Grdg*zZo~)*?)wt3DB}NHMTH@6_%Cf}wJ^Uc35{KCskYK^)jz~#{NkU%a zBuRpj8b9=T_>o8W&<8(M87O{$%`DpYJxpmBCMn~cEt=cs=yYaSSCc&V{I_X#+8jA{ zjC$lDgu)n6o$5lMl!HMZAuEHVKNzraW((gB==HitDM-`wK%83^7mqPLJwsj;?C^;+QnesK;?NU%EvlKpgnrMNshnaFoVH>y2?|j zDcRZCK}kuw9pm`{XV0D`jv^3(vdF7rG`=5k_S~!d-tT{i^;q z@%?~f3yZYcZI*8>qkW%0{?k9<7k}{=dBYnX;tziR_dyCuXSsUq8asQtoWFFDUwGdy zFiJ-}_u{i$zIF*~99sLJ4y031h5wYQ@VHWnP%Fx0z?G}lQ9=@FpSw<-;Ki5DkQ4=( zkqid|qA29Z;v$27pD%ss36#>{1gG$yZ*>haF zvdzR$QLoq8+aIHZ#F)GqG${^Wv0b7}4r92m=p4 z@QG_NI2h+Cd09}VIfH(m!*hqZaNz>${e9m5{`WK8o5D%S`o=nORAuY`?9cv!-};^3 z;-NRZfscITBc$Uo*REbeX^F80p%lvV@eU$SQc5Ut;;2DUrWB=Qw3pCqHrd$SBZy)e ztvY2-F%oCW>MZg3)Ni?#>Q_!GJ8wxOnL@<20pA6ONuZfpaBGH&^gO zpR_;Z*MH-e`Tc+Y$LIr=rnEwesvLAWT|8e^xwNd{_=)38PEPXl)88V`OO70w#rGxe zc*nc=`q#hCty{N60ie@N|W`qgsR%ib(qiay(!0;g9?&Q5X^i zmHtcflF}G_&trc6FuOZDv|24%%_d2b&}=k#<=kuBcKS3w_MRW(vBw_67(vo0|MC^Ai_d{gw-(h%`;v+}z~V*Us}Ve&yHjbbu`^Iu4kfon>Kmh9n!Y zwz)&s4<13%|OCFxcxbtL#&p$t8W2AXzJ!f|`#MlCt6%~lolIiIwq=3-( zNt3KnCZ31ZidwzK^71XD@_FD5Z=fEB)a&)?C{7q5T&0iWxJGGnx)U9=1ATMp3jO{r zjYf-5N31O`;rk`yB&|NTbfn1`Gu^Eigriw2m~(Fr}#?m_kyPmC@nVA_C7NO;V(k>}>B;iR~h#GzCSL zvb?;67g)SnhtEkkCK(f)warFSw?TF$9OQ{(DV$q zmY4CgM=g$6S>K{NF+u35gTojVED#o)v+NHB96CISH3m?Kzy9;zb;=b4p~vRVCKO|u ztwX$eaSdN)dh2ZRDk)9@RL1{sN~@EwHw_ z$K^|x_$NR6Q&__OR)@GA z)9bdGnL9+9rc6vsV6A0(qKEV)Pd)V&u3ulm_XBozcj$JzoH}`u?d>JB@3V015Stt8 zblNQ@rzUAHo?zqF1se4xWnvlNc=_^G3Ma9)An-!QqcPgosDo!zIOk}0+I;gH-y)70 zn6lz;)>=wO^_T@6R22EZqi|T}k4zKSA|mbK9OPMv9;O+>NtE{39~;UDTFn+s-(z!g zmmmAlcX009vn<`(r`zq|dz!o~SzTJ;*y22QwN7*IgLk6Dk5?!5jb&$ho28{&Y;0_D z_1aa|*0$N&xy&f-lczb2Mognt2kd7G+M{KNbP#QxR z#SHrcKJuZD@X?QclEh6hGWem-aNMU+YY>GYNt#d;IfFr;WIU!8 zPb09Td4Y3O#~1^j+!%y2%paL&YO;$d3jW_B&SbZ;>pH{VZccMsUUo$m$to7BRI*fx zVyhxmVnYEG8;X#?K{7FrHwloD#|F|Mc>=@JJU|*C8D$U%86*MJ!j=T9IkyFaDn08{7QsXMf55{vP*VyU*Q|dmJ7eaD4ZW+ef!>&J#mW z&t&N6I-Mj*60C#cJGbHf9a0LMpU=6ue~6@rL_t!^< z5>TxxW}9PD>NvT5$c-DDq_BeI@ZJ**WVxaVK1S7uo;-d+S*mKfv$Mm~Cr=m*hn$_g z;N|(8i;Hu<`sz1az0fR&rfK;4t8e(?^DmLMB^W_sBq8i>?{jqbHgCQ8ChtCchtX)n z{@xy2+uLMWMi8|VJFEFTvHz@fmPs*XGM#FZilGY;iIMsFnzOTCtENA@;qlenW`Bq3WGt(dbz@2*xuge=;i^BpFL%BYYUQLXJf|X=!nU5gv7w+Y{qaf zKw_XM6f3aK;+;;Statdw-#+HW*$U?}M1+*|ST9Edo;^RMu45AoY4Gc-5{gp9>5E?%HPMpI5sUgN>{e}{(;A98qb zh$L`@rx+GY{0yHACei0oNdKD>*4v(&0PhV6XzDrxDTr8-oNM3r{*nvP(w}^DCs*3gcg1V~6t>bc4bAG;H)pdBE5rRQt!~k_u6C@%* zo77~JvZN*>B_VWy^;);dY-gsEb7PT|dj70u>#e<>q#I+f&f-uCG9(=XTA=A#+ODG< zmJEhDAw&j)0d-v=5~-FIRb6p%_Z}br*MIZyoj2IrobmbRU-0PBdkpgt-U)BN{YF1l zvy_F_N@H~Tqm2zB5Q4!agR_nzA1g*6t##K$z0PDZ##u|-b&9Hs;JinYW^^Ryu-4#h0_P&m4sqjx>1@jO&K~m@FS$5BrxhV! zsk=x_`bCNa7*Z74*y2-*^B$LF*XAokS4pZ-Bq^y`N=!ne#TtuCrYEU;_l8y9MoNav zTcRY*qDYEfC2L{ap7Pme zpYZs}w@B>Ro^7x>+hA*R!qL$VdwV;KM;h{BjjoEK$SLxIyeRO_D>v3OS~UH4MbjFK zbN+g&tiZ9q<~WSC7MS+L{f%Eyizo$Zs zkxxJSoDcu_Crrj0eD#~JIlg_H!<#p`b?X*g-61K`b*(aPMW$Rvo>P`3CRv(RH8VdN z4XJBbt}mHRH!;T0b``hp++=rWm*KeN?8Pb0?n64@*|T#lt}ZEuh8s6GX`7C^NwjT4 zT~!FCe;A6SXPIu>7$S6f&r729Da00MEeYM0#yE@zF@da97DEUaF}=GZVvOKyNg@$U zr1T}R6EYKN>k8)#tM!Vl?Hytib`NjTB1|V6{PVy62W?ez>((J}y>-mV(QUfA#bSv` z@%6GS6_F?=S{ke+bb_-^g~q07sOyf)#TB@Wi;D{m_xF(~JbUt#7-xhwG90O;WPfj$ ztY=%FK7Pi_m*@P?XO9tySeN4@QDn-ST`jKYLg>|qgwLIBAu;{R?4$X5yR!y(WY>j0 z#t2Ci){9B2m~|wh&|GdjF?1vpf$F-zXjrl}8gcsk1sk&&P7KS7OP2Es2IYuqx#lMy zzR$_=F%l|@(kc{_N{n?>RfTn)(O{@2w1@_hNTjI+_h*0l{?B7HY|b_bZ9~0UQ4UJ} z?O*?eH{N)i(PWHu85dP==msluEYvP}I$Wj5$pF#QLsYDF>$ zv61y<#nt5%&*v*n&(5eL;5@B(R_B*w-a{}-GF$0opI6Q^(~FOKV9aJ?yicU044O@r zyeJ5wXnWhVgebI4L&EhdGOBqYMnk8;tq{xE@55XD40#%L0(6qG{!OCO3V;xqQWRNbB8(kTlNo3Zs zy}3gxEx`mzo3p#K$w?i+-e*}YXcK(>%_(O^t|t6wY1)Q1#Orsfpq(KlFbLZ_6Y?zU zLql}n;jO`$gz=UlA0WdVV-2K4Oo~@Usnio?pbLrPTL*mb!H-Bm$g@E|xJh&&kms6) zmr^3nHT^{-A`xsqR8hdt;_GUS^!1`{7T0D`{i2+<5!UODh$VE9!i`BmNC>rQRlOXH z@!nxf!UacWJ#K5PC}&d0b}}82=LHY$-3Mc+>YAr77QG5_$?3}ltDYh>y3yH)s+=q>zwFAO%gHEH@7Adi5P)A^Mn|A`@uba_`~;DEL4*VA!2$fc2N|Ngd!hc zojUf)azI@hoRfZ_6Tw;}G`K(d!*_okCDL^rG3r>|^<%Uc0_)Y1^}15;bW#=4SnZpd zwn2LTWYadxmwO$#8 zcQv`Q=lFS5(M%%5sFb$x_=urhtr1BK#zUeAi^YQRWQ_9$Ydl3UAQ1_@0wGPHECwV| zsKj|s9&;o`oOPt6Z>j4#lF_SC8zR;?npKN&YQi_R1d}*DpL2db$2-sM!$ZFR&IuWb z&_&LdYrg&Vl&4STG;M{%NZm~s76nL6-Lx3vNe&#AD1x<#>2!lf@4mxyTu@gFoU>$E zPF>eTsWH}KP+2-j0w?{p&uQBZV}+E|feZ>QI|t?;{^qY!?hMP77Ip7^f5FwrymkLD zLu~sD32iu&eCOCN@*Hbnv0P%UVKf@`^8iCvR~V~5%o{`QGg1ouo6Tt3hBif}qb)3! zCatyePmw6-l?1RUF&IoJvVwV4F`r*>{(R2){DRBnin^+alE}-Pt*uQSJ$f69BeW`o z>z_js7!z=|&$#GUlcEKlBp^wF=nw;CULgGy%~^|2DbfVNXG$mAS8-1L%9u>YhzND9 z$&}7mg6Ne&i=paVxi8q--0qW103$S2qeWA+hl(aqOO1p`S(N0N!gyKi2&n~YNYNlc z5R2YPkseo%^HJv~7| zK{+`&w6nBjWo2w^Y;bUJQBhIo=jhAJ%gV~iwY9Z8JUsLB^T5Esa&mG~Qc_GzOsT1< zK0ZE6OG`UDJ6&B}M@L6SMn*|VNkv6PTU%RMSy`l{q@$ywLPA0_GBajoW;8T3H8nNp z=;*bywR(Dbe0+S(&CRB!raC%0H#ax%@bJ;m(M?TFNJvP=#>VpU@LFfcGbKRfPjF<$H%U&t~NF{^z`(^#KhOv*Rip&I5;@|{{C@saaUJY)z#Iou(0Ihp?WB;ALWp0aNcTB(p!}V21GvTiHF(4RQA)5|R zZ|9zUC9Z&XPXn>VI@?+4&({4e&1>F*uw9*QI1B?YozXq;?r8w_ux4#vR%y_9WM6#j zsrh4jD&X#H*PW}U-_B1G0ayVwW!#&3=RMN9-lVer)ECpAJ>SNsqwjW}@2YLT35MZ| z6^(uiL+wR>Z7-WLz|tgo<%9K8R4eaw)yA~fO}Z5E!JD@_F9ywb&MkHq-Y)~YU>HSC z471s>6K5lBnNH8SF^msBxEiai<*Xn93~L`dcUkX+X}dNN3>qz0W9MGGHW3ViI&9ta z>9f7w$h?6dhUM%Rde7!ggPfwjvf50tI>az+(8I>J1psveeQ(H075yj+V{lMHKk1eN z)JltDU99>W{UO>BI!>M$ zBO|rG8C6l`>=*{6ji#735Ce{=tTxgG@1(Lto}`bNcI1tXrntWA$C4F#a!~dMjg&i* v|M`tGWVbx|8<{4Z#3Hkz{g2dn{?q&e(VxA_&A-4E00000NkvXXu0mjf*uHfo literal 0 HcmV?d00001 diff --git a/modular_ss220/modular_includes.dme b/modular_ss220/modular_includes.dme new file mode 100644 index 00000000000000..5540d60ae383ec --- /dev/null +++ b/modular_ss220/modular_includes.dme @@ -0,0 +1,17 @@ +// All new mod's includes here +// Some modules can be easy excludes from code compile sequence by commenting #define you need to remove in code\__DEFINES\__meta_modpaks_includes.dm +// Keep in mind, that module may not be only in modular folder but also embedded directly in TG code and covered with #ifdef - #endif structure + +#include "_assets_modpacks.dm" // modpack picture integration system +#include "_modpack.dm" // modpack obj +#include "_modpacks_subsystem.dm" // actually mods subsystem + tgui in "tgui/packages/tgui/interfaces/Modpacks.tsx" + +/* --FEATURE-- */ + +#include "features\feature_example\feature_example.dme" + +/* --EVENTS-- */ + +#include "events\event_example\event_example.dme" + +/* --MISC-- */ diff --git a/modular_ss220/modularization_guide_ru.md b/modular_ss220/modularization_guide_ru.md new file mode 100644 index 00000000000000..eab7098e6c87bf --- /dev/null +++ b/modular_ss220/modularization_guide_ru.md @@ -0,0 +1,493 @@ +# Руководство по модуляризации кода – MassMeta/NovaRat style, v0.3 + +**Соблюдение этого Гайда – залог успешного мержа Вашей фичи в репозиторий.** + +## Вступление + +Проект **МассМета** 🧰 – это, постоянно обновляющаяся, модульная ветка от проекта [/TG/station](https://github.com/tgstation/tgstation). Здесь мы добавляем свои фичи и по возможности откатываем неудачные. Прочитав эту инструкцию – Вы поймете как правильно нужно внедрить к нам Вашу идею, чтобы ее увидели другие игроки нашего сервера. + +Несоблюдение данного руководства приведёт к стагнации код-базы проекта, как это было до. Осознав наши прошлые ошибки – было принято решение привести проект к модульности подобно той, как на серверах Skyrat, однако доработанной под наши нужды. [Оригинальное руководство](https://github.com/NovaSector/NovaSector/blob/master/modular_nova/readme.md) (на англ.) + +⚠️ **Все Баг-фиксы и рефакторы немодульного кода, изменение не Наших карт – Вам нужно будет заливать именно в апстрим /TG/station !** + +### Про тестирование своих [PR'ов](https://ru.stackoverflow.com/questions/134183/%D0%A7%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5-pull-request) 🔬 + +Прежде чем открывать PR на слияние, то было бы неплохо проверить Ваш код на работоспособность. +А именно: +* **Скомпилируй билд** на своей локальной машине и посмотри, что там выдал компилятор Byond. Если билд успешно скомпилился, то уже хорошо. +* **Запусти билд** с Фичей и посмотрите как она себя ведёт, если ли какие-либо аномалии в работе модуля. Достаточно поверхностной проверки. +* **Протестируй и так и эдак фичу**, например, если Ваша Фича предполагает взаимодействие нескольких игроков, то можете воспользоваться Гостевым аккаунтом. Для этого выйди из BYOND-хаба (надо закрыть Бьенд) и зайди на локалку как Guest-[много циферок]. + +После этого смело создавайте PR на наш репозиторий, там Вам уже подскажут как можно доработать тот или иной неочевидный момент. + +### Про платформу GitHub + +Это одна из многих видов Систем Контроля Версий. Сам Git одновременно и достаточно проработан в плане алгоритмов, он ими же и ограничен. Они не всегда могут однозначно самостоятельно разрешить определенные изменения в коде, что приводит к конфликтам, которые требуется решать вручную. + +Подробнее про сам Git и как с ним работать [тут](https://git-scm.com/book/ru/v2). + +## Суть Конфликта ⚔️ + +Начнём сразу с показательного примера. + +Предположим, что в какой-то строчке оригинального файла `foobar.dm` /TG/station было так: + +```byond +var/something = 1 +``` + +Однако, под наши нужны нам потребовалось изменить значение с **1** на **2** под какую-то фичу в проекте, + +```diff +- var/something = 1 ++ var/something = 2 //MASSMETA EDIT +``` + +Но неожиданно апстрим /TG/station вносит свои изменения (commit: their-feature) в эту же строку файла, меняя её у себя с **1** на **4**, + +```diff +- var/something = 1 ++ var/something = 4 +``` +Затем мы решили синхронизировать изменения и видим следующее, + +```byond +<<<<<< HEAD:foobar.dm +var/something = 2 //MASSMETA EDIT +====== +var/something = 4 +>>>>>> their-feature:foobar.dm +``` + +В данном случае в череду коммитов /TG/station внедряется дополнительный, про который известно только нам самим. ГитХаб, видя подобное несоответствие - даёт нам сделать выбор. + +Например, нам нужно оставить только Наше изменение, то просто удаляем все что нам добавил ГитХаб и оставляем только нужное, + +```byond +var/something = 2 //MASSMETA EDIT +``` + +Подобного рода конфликты разрешаются именно ручками, однако есть другие подходы в виде модульного кода, о которых мы расскажем далее в данном руководстве. + +Подробнее про [Ветвления и Слияния](https://git-scm.com/book/ru/v2/Ветвление-в-Git-Основы-ветвления-и-слияния). + +## Протокол модуляризации 🛠️ + +У Вашего модуля должно быть короткое и информативное название в документации, например - **`shuttle_toggle`**. + +Этим уникальным **ID** Вы затем назовёте: + +- Свою модульную папку `modular_meta/features/shuttle_toggle/`, в которой вы будете локально работать. + +- А также в дальнейшем Вы будете помечать все **Немодульные** изменения в коде /TG/station. + +- В редких случаях он может пригодится как некая метка для включения/отключения модуля. Об этом будет отдельно рассказано ниже. + +Теперь подробнее про виды модуляризации. + +### Не Модульные Изменения + +Иногда наступает момент, когда редактирование **оригинальных** файлов /TG/station намного удобнее и практичее. + +📌 Пожалуйста, не забудьте записать факт их изменения под пунктом **"TG Proc/File Changes"** в `readme.md` вашего модуля. + +В этих случаях мы решили применять следующую стандартизацию: + +- **Добавление:** + + ```byond + //MASSMETA EDIT ADDITION BEGIN (shuttle_toggle) + var/adminEmergencyNoRecall = FALSE + var/lastMode = SHUTTLE_IDLE + var/lastCallTime = 6000 + //MASSMETA EDIT ADDITION END + ``` + +- **Удаление:** + + ```byond + //MASSMETA EDIT REMOVAL BEGIN (shuttle_toggle) + /* + for(var/obj/docking_port/stationary/S in stationary) + if(S.id = id) + return S + */ + //MASSMETA EDIT REMOVAL END + ``` + +- **Изменения:** + + ```byond + //MASSMETA EDIT CHANGE BEGIN (shuttle_toggle) + /* ORIGINAL + if(SHUTTLE_STRANDED, SHUTTLE_ESCAPE) + */ + if(SHUTTLE_STRANDED, SHUTTLE_ESCAPE, SHUTTLE_DISABLED) + //MASSMETA EDIT CHANGE END + return 1 + ``` + + 💡 Если предполагается **"Масштабное"** изменений или удаление кода, то его уже можно переместить в модуль. На месте удаления обязательно допишите куда оно было перемещенно: (Moved to: `modular_meta/features/shuttle_toggle/randomverbs.dm`) + + ⚠️ Обязательно оставляем все что было до вашего вмешательства под пометкой **ORIGINAL**! + +### Полностью Модульные Изменения + +В нашем проекте присутствует папка `modular_meta/`, там будут храниться все наши **"Модульные"** изменения кода /TG/station. + +💡 Она полностью независима и этим мы гарантируем, что кодеры с /TG/station не будут туда вмешиваться. + +В этой папке есть ещё несколько подпапок и файлов: + +| Папка/Файл | +| ---------------------------------- | +| **\_defines** 📁 | +| **\_globalvars** 📁 | +| **\_helpers** 📁 | +| **features** 📁 | +| **perevody** 📁 | +| **reverts** 📁 | +| **tools** 📁 | +| **\_modpacks_subsystem.dm** 📄 | +| **main_modular_include.dm** 📄 | +| **modularization_guide_ru.md** 📝 | +| **module_template.md** 📝 | + +Теперь подробнее про каждую из Папок: + +- **`_defines/ и _globalvars/ и _helpers/`** (D/G/H) 📂 + + Здесь лежат все наши модульные "определения" (defines), "гробальные пеменные" (globalvars) и "помощники" (helpers). + + Вынесена отдельно из папки `features/` из-за того, что их требуется ставить выше основного ТГ кода, сразу же после (D/G/H) ТГ, (за счет этого мы можем использовать наши дефайны в коде ТГ, а не только в модулях). + + Храните D/G/H своего модуля только в одном файле, назовите его также как `ID` вашего модуля. + + Все файлы в папках включены под `#include` в + - **`modular_meta/_defines/_main_modular_defines_include.dm`** + - **`modular_meta/_globalvars/_main_modular_globalvars_include.dm`** + - **`modular_meta/_helpers/_main_modular_helpers_include.dm`** + + 📌 Пожалуйста, не забудьте записать факт их добавления под пунктом **"Defines/Helpers:"** в `readme.md` вашего модуля. + + 💡 Если же они применяются только лишь в рамках одного файла, то их достаточно объявить вверху и внизу файла. Такое записывать в `readme.md` не надо! + + ```byond + #define MY_DEFINE + //some code with MY_DEFINE here + #undef MY_DEFINE + ``` + + +- **`features/`** 📂 + + Здесь лежат все модульные файлы **"Новых Фич"**, которых нет в апстриме. Каждой присвоен уникальный **"module_id"**. + + Подробнее про строение папок модуля расскажем чуть ниже. + +- **`perevody/`** 📂 + + Папка аналогичная `features/`, но в ней уже лежит перевод на Русский всякого в игре. + + Выделена отдельно, чтобы её можно было легко отключить из компиляции кода для англо-язычных форков. + + Такой модуль именовать уже обязательно с припиской **"ru_"**, например вот так: **ru_crayons**. + +- **`reverts/`** 📂 + + Папка аналогичная `features/`, но там располагаются недавние откаты плохих и возвраты хороших по нашему мнению фич, введёных апстримом /TG/station. + + ❗ Если фича была уже выпилена давно или же апстрим произвел её полное удаление сразу, то она уже может рассматриваться как самостоятельный модуль в `features/`! + + Такой модуль именовать обязательно с припиской **"revert_"**, например: **"revert_buff_lasers"**. + + ⚠️ Укажите в `readme.md` модуля ссылку на пиар, который откатываевается! + +- **`tools/`** 📂 + + Тут лежат все дополнительные инструменты проверки нашего кода. + + Они проверяют только файлы в модульной папке, помогая нам не совершать дополнительных ошибкок. + + К ним идет прямое обращение только в файле: `.github/workflows/ci_suite.yml`. + +## Подробнее про наполнение папок (features/ perevody/ reverts/) + +Чтобы сохранить общий стиль и обеспечить удобную навигацию по большинству модулей, а также контролировать количество файлов и папок в репозитории, Вы должны располагать определённые типы файлов по своим папкам. + +⚠️ Каждый модуль обязан содержать в себе файл документации модуля – `readme.md`. + +| Папка/Файл | Содержимое | +| ------------------ | --------------------------------------------------------------------- | +| **code/** 📁 | Файлы кода: **`.dm`** | +| **icon/** 📁 | Файлы иконок и картинок: **`.dmi`** и **`.png`** | +| **sound/** 📁 | Звуковые файлы: **`.ogg`** и **`.waw`** | +| **includes.dm** 📄 | Инклюд всех файлов в папке **code/** и объект модуля `/datum/modpack/`| +| **readme.md** 📝 | Полная документация к модулю, [пример](module_template.md) | + +⚠️ Файлы строк: `.txt` и `.json` вы помещаете в папку `strings/meta/`, т.к. код /TG/station не может работать нормально со всеми файлами-строк вне этой папки. + +⛔ У проектов **Skyrat** присуствует папка `master_files/`, однако у нас в проекте её НЕТ! Все переопредения кода у нас помещаются полностью в модуль с особыми пометками! Пояснения будут позже. + +❗ Также у проектов **Skyrat** стандартно все новые файлы сразу включаются в общий файл `tgstation.dme`, что я считаю достаточно трудным для дальнейшней поддержки. У нашего проекта другой поход в этом моменте, как Вы видите. + +### Подробнее про папку **`code/`** + +⚠️ В этих файлах не должно быть закомментированного кода, тем более полностью закомментированных файлов! А вот пояснения к коду оставлять можно, порой даже и нужно. + +Здесь располагается код двух типов: + +#### Абсолютно новые функции и объекты 🆕 + +Просто ложите все свои новые файлы кода в папку `code/` своего модуля. + +💡 Можете разбивать файлы по подпапкам, если в этом есть нужда. + +Не забывайте проставлять все пути к иконкам и звуку правильно! + +#### Переопределение объектов и функций /TG/station 🔀 + +С помощью этих файлов мы косвенно изменяем основной код /TG/station. Это позволяет нам очень изящно внедрять свои коррективы, не вмешиваясь напрямую в основной код. Тем самым не нарушая их череду коммитов и не создавая для нас самих в будущем **Конфликтов Слияния**. + +Однако это является и минусом такого подхода. Гитхаб не сможет нам оперативно подсказать где файл поменялся из-за вмешательства апстрима и где следует учесть измененое или дополнительное переопределение. Иногда прямые изменения кода через `//MASSMETA EDIT` предпочтительнее. Старайтесь использовать здравый смысл в этом вопросе. + +⚠️ У данных файлов **Не надо соблюдать Иерархию** аналогичной папки `code/` у /TG/station! Просто ложите вместе со всеми файлами в модуль. + +Эти файлы выносите в "отдельную группу" с помощью пометки `"m_"` в названии (от слова master), например: `m_tg_filename.dm`. + +⚠️ Над каждым блоком таких функций/объектов **подписывайте в каком оригинальном файле /TG/station они расположены**. Таким образом нам будет проще смекнуть что к чему. + +- **Пример модульного переопределения объекта** 💡 + + Например, Вы решили модульно переопределить иконку и описание у мольберта (easel). + + Оригинальный объект в коде /TG/station по пути `code/modules/art/paintings.dm`: + + ```byond + /obj/structure/easel + name = "easel" + desc = "Only for the finest of art!" + icon = 'icons/obj/art/artstuff.dmi' + icon_state = "easel" + density = TRUE + resistance_flags = FLAMMABLE + max_integrity = 60 + var/obj/item/canvas/painting = null + ``` + + Для этого создайте новый файл желательно с таким же именем как у оригинала и расположите в папке `code/` вашего модуля: `code/master_paintings.dm`. + + Для выполнения нашей цели, наполнение даного файла будет выглядеть примерно так: + + ```byond + //ORIGINAL FILE: code/modules/art/paintings.dm + /obj/structure/easel + desc = "Let yourself draw!" + icon = 'modular_meta/features/art/icons/artstuff.dmi' + icon_state = "new_easel" + ``` + + Теперь при компилировании проекта, данное переопределение подменит эти переменные у оригинального объекта мольберта. Тем самым в готовом проекте у мольберта будет уже Новая иконка и описание. Даже код /TG/station менять не пришлось! 🎉 + +- **Пример модульного добавления фичи в функцию** 💡 + + Для простоты предположим, что вы хотите заставить оружие искрить при выстреле для имитации дульной вспышки. + + Также Вы хотите, чтобы это можно было использовать со всеми видами оружия, использующими эту функцию. + + В модульном файле объекта можно начать с добавления новой переменной `var/muzzle_flash`. + + ```byond + /obj/item/gun + var/muzzle_flash = TRUE + ``` + + Теперь у Вас будет **у каждого** наследника этого объекта доступна эта переменная. После этого, допустим, вы захотите проверять её и вызвать искры после выстрела. + + У этого объекта уже есть процедура, что вызывается при стрельбе: + + ```byond + /obj/item/gun/proc/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1) + ``` + + Теперь мы начинаем **добавлять код** для работы нашей фичи в дочернюю процедуру `/obj/item/gun/shoot_live_shot()`. + + ```byond + /obj/item/gun/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1) + . = ..() + if(muzzle_flash) + spawn_sparks(src) + ``` + + Тут мы обязательно вызываем такую конструкцию `. = ..()`, она по сути своей говорит, что мы **наследуемся от родительской функции**. После данной конструкции добаляем весь наш новый код. + + Теперь при компилировании проекта, данные "добавления" допишутся в оригинальный объект и функцию. Этим мы добились того, что оружие при выстреле ещё и искрит, при том не вмешиваясь в функции /TG/station напрямую! 🎉 + +### **`. = ..()`** для чайников + +Как вы уже могли заметить, у языка DM частично заложена парадигма ООП, нас в данном случае интересует процесс наследования объектов и их функций. + +💡 Вы также можете нажать F1 в Dream Maker и прочитать подробный мануал на английском. + +- `.` – это возвращаемое значение нашей функции по умолчанию. Изначально оно равно `null`. + +- `..` – это возвращаемое значение родительской функции. + + Через `..()` Вы обращаетесь к родительской функции, этим вызывая её в нужном месте Вашей функции-наследника. + +И теперь, если вы сделаетете подобый манёвр `. = ..()`, то Вы вызовите родительскую функцию и присвоите её значение возвращаемому значению нашей функции. После этого можете свободно дополнять свою `.` чем хотите. + +Так же в случае, если Вы не хотите возвращать родительский вывод, то Вы можете сохранить его в любую переменную или просто использовать `..()` + +Мы так же можем вообще не вызывать `..()` - тогда функция оверрайдится полностью. Но я бы не рекомендовал делать подобное, ради перемещения ориг. функции в модуль и дальнейшей модификации её уже там, ибо когда /TG/station поменяет её у себя, то пиши пропало. + +Также учтите, что вы не сможете при модульном дополнии функции использовать те переменные, которые были объявленны в функции оригинале! + +## Отключаемые модули + +В папке модуля есть файл `modular_meta/__config_modpacks.dm`. + +В нем перечисленны все модули, что можно отключить в игре. Для отключения требуется перекомпиляция проекта, т.к. эти изменения вступают в силу только на этапе работы [препроцессора](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%BE%D1%80). + +Преимущественно там будут присутсвовать модули **"Перевода"** ну и некоторые другие, что были сделаны отключаемыми по желанию автора модуля. В ином случае, модуль можно убрать только если залезать в код напрямую. + +### Процедура создания отключаемого модуля + +Изначально мы имеем в оригинале код: + +```byond +/obj/item/toy/crayon/proc/crayon_text_strip(text) + text = copytext(text, 1, MAX_MESSAGE_LEN) + var/static/regex/crayon_regex = new /regex(@"[^\w!?,.=&%#+/\-]", "ig") + + return LOWER_TEXT(crayon_regex.Replace(text, "")) +``` + +Но нам потребовалось изменить одну строчку кода функции, что к сожалению просто так нельзя поместить в модуль. + +Для начала создадим в файле `modular_meta/__config_modpacks.dm` новый дефайн `RU_CRAYONS` и сразу присваеваем ему значение `1` = включен. + +```byond +#define RU_CRAYONS 1 +``` + +Далее заходим в интересующий нас файл и там оборачиваем изменяемый блок строчек в `#if – #else – #endif` конструкцию, где `RU_CRAYONS` – это **ID** вашего модуля. + +```byond +/obj/item/toy/crayon/proc/crayon_text_strip(text) + text = copytext(text, 1, MAX_MESSAGE_LEN) +#if RU_CRAYONS // MASSMETA EDIT + var/static/regex/crayon_regex = new /regex(@"[^\wА-Яа-яЁё!?,.=&%#+/\-]", "ig") +#else + var/static/regex/crayon_regex = new /regex(@"[^\w!?,.=&%#+/\-]", "ig") +#endif + return LOWER_TEXT(crayon_regex.Replace(text, "")) +``` + +Когда модуль включен `1` – то будет подставляться верхняя строчка кода, когда выключен `0` – оригинальная. + +⚠️ Такие измения уже содержат ID вашего модуля, поэтому достаточно их помечать только конструкцией `// MASSMETA EDIT`. + +## Карты 🗺️ + +Используются карты: +- 🔴 Оригинальные с офф ТГ (без прямых наших изменений) +- 🟡 Заимствованные с других билдов (некоторые желательно не менять, т.к. мы можем подтягивать обновления в других билдов) +- 🟢 Наши самодельные, они же полностью независимые, например, ProtoBoxStation (меняем и чиним как хотим) + +### Новые карты (наши самодельные) + +Все наши Новые карты лежат по пути со всеми остальными в `_maps/map_files/` (не в модульной папке). + +К каждой карте идёт дополнительно `.json` файл-конфигурации в `_maps/`, не забудьте добавить его тоже! + +### Модульное Изменение Карт ТГ (через применение Авто-мапперов) + +⚠️ Не изменяйте оригинальные карты /TG/station напрямую, Вы стокнётесь потом с таким же хаосом, как если вы бы меняли файлы иконок! Для внесения изменений ипользуйте модуль Авто-маппера. + +Когда вы добавляете новый элемент на карту, то вы должны сперва определеить масштаб переделок. + +- Если это **небольшое изменение на 1 предмет**, то используйте простой автоматизатор области. + + Автомаппер простых областей использует записи точек отсчета, чтобы поместить один элемент в область карты, которая имеет определенный смысл. + +- Если речь идет об **изменении целой комнаты**, то используйте автоматизатор шаблонов. + + Автомаппер использует готовые шаблоны для переопределения участков карты, используя координаты для определения начального местоположения. Примеры смотрите в файле automapper_config.toml. + +## Модульный TGUI (TG User Interface) + +**TGUI** - еще один исключительный случай, поскольку он использует Javascript, который не может быть модульным, нежели же код DM. + +ВСЕ файлы TGUI находятся в папке `/tgui/packages/tgui/interfaces/` и ее подкаталогах. Нет какой-то конкретной папки для Наших TGUI файлов! + +Частным примером может служить TGUI файл для оформления меню Модпаков `/tgui/packages/tgui/interfaces/Modpacks.tsx`. + +📌 Пожалуйста, не забудьте записать факт их добавления/изменения под пунктом **"TGUI Files:"** в `readme.md` вашего модуля. + +### Изменение оригинальных файлов /TG/station + +При изменении оригинальных файлов TGUI поступаем аналогично, как и при изменении вышележащего кода DM, однако схема написания комментариев тут несколько иная. + +Вы можете использовать как `// MASSMETA EDIT`, так и `/* MASSMETA EDIT */`, хотя в некоторых случаях вам придется использовать одно вместо другого. (в некотрых языках '//' - могут не являться комментированием, учтите это) + +В целом, старайтесь, чтобы комментарии к изменениям находились на той же строке, что и само изменение. Предпочтительно внутри JSX-тега. Например: + +```js + +``` + +```js + +``` + +```js + +``` + +В крайнем случае Вы можете заключить ваше редактирование в фигурные скобки, например так: + +```js +{/* MASSMETA EDIT ADDITION START */} + + someProp="whatever" + +{/* MASSMETA EDIT ADDITION END */} +``` + +### Создание новых файлов TGUI + +⚠️ При создании нового файла TGUI с нуля, пожалуйста, добавьте **Заголовочный Комментарий** самом верху файла: + +```js +// THIS IS A MASSMETA UI FILE +``` + +Таким образом, они легко идентифицируются как **Наши** модульные файлы TGUI `.tsx` и `.jsx`. + +Собственно ничего больше делать и не нужно, комментарии `// MASSMETA EDIT` в таком файле TGUI излишне. + + + +## В заключении + +Терпение и труд – ТГ к*дера перетрут. Если мы будем последовательны, то в конечном итоге это избавит НАС от будущих болей в области ГМ, когда Нам (Вам) же придется разрешать конфликты вручную. +Благодаря более скрупулезному документированию будет сразу понятно, какие изменения были сделаны, где и с помощью каких функций, и все станет гораздо менее двусмысленным и запутанным. + +Желаю удачи в ТГ кодинге. Помните, что сообщество всегда готово помочь Вам, если вдруг понадобится помощь. + +Оригинальное руководство: Skyrat/NovaSector. Идея модульности: Nebula and Bandastation (SS220). Перевод и дополнения: Artemchik542. Доработка модульной системы: Artemchik542, Huz2e. diff --git a/modular_ss220/module_template.md b/modular_ss220/module_template.md new file mode 100644 index 00000000000000..33683df7de87ed --- /dev/null +++ b/modular_ss220/module_template.md @@ -0,0 +1,20 @@ +## Module ID: YOUR_MODULE_ID + + +### Defines/Helpers: + +- N/A + +(Paste here file path to your module's defines/helpers in `_defines` and/or `_helpers`.) + +### TG Proc/File Changes: + +- N/A + +(Paste here file path to changes in TG `code/` folder.) + +### TGUI Files: + +- N/A + +(Paste here file path there original TGUI file were changed or new TGUI files made in `tgui/` folder.) diff --git a/modular_ss220/readme.md b/modular_ss220/readme.md new file mode 100644 index 00000000000000..e876957c90429d --- /dev/null +++ b/modular_ss220/readme.md @@ -0,0 +1,21 @@ +### Это модульная папка проекта МассМета/NovaRat + +**Здесь находятся:** + +| Папка/Файл | Описание +| ---------------------------------- | -------------------------------------------------------| +| **_\defines** 📁 | "Определения" для вашего модульного и не очень кода | +| **\_helpers** 📁 | "Помощники" для вашего модульного и не очень кода | +| **features** 📁 | Все крутые фичи кодить сюда, и только сюда | +| **perevody** 📁 | Переводы всякого на более понятый язык | +| **reverts** 📁 | Откаты неугодных богу Робаста приколов в ТГ кодинге | +| **tools** 📁 | Инструменты, что позволяют удобнее вести проект | +| **\_\_config_modpaks.dm** 📄 | Конфигуратор модпаков, все что там есть - отключаемо | +| **\_modpacks_subsystem.dm** 📄 | Подсистема модпаков | +| **main_modular_include.dm** 📄 | Тут описаны все наши модули, что мы добавляем в проект | +| **modularization_guide_ru.md** 📝 | Инструкция по модульному ведению проекта (на Русском) | +| **module_template.md** 📝 | Шаблон пометок изменений кода вне модуля | + +⚠️ Все Defines и Helpers, применяющиеся в вашем модуле, должны быть реализованы в папке **\_defines** и **\_helpers** соответсвенно! ⚠️ + +📚 Вам предоставлено пособие на Русском, о том как модульно вести проект. **modularization_guide_ru.md** diff --git a/tgstation.dme b/tgstation.dme index 9dc8aedb0f7ff1..a1a707d9843882 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -262,6 +262,7 @@ #include "code\__DEFINES\wounds.dm" #include "code\__DEFINES\xenobiology.dm" #include "code\__DEFINES\zoom.dm" +#include "code\__DEFINES\~meta_defines_include.dm" #include "code\__DEFINES\ai\ai.dm" #include "code\__DEFINES\ai\ai_blackboard.dm" #include "code\__DEFINES\ai\bot_keys.dm" @@ -628,6 +629,7 @@ #include "code\_globalvars\silo.dm" #include "code\_globalvars\tgui.dm" #include "code\_globalvars\time_vars.dm" +#include "code\_globalvars\~meta_globalvars_include.dm" #include "code\_globalvars\lists\achievements.dm" #include "code\_globalvars\lists\ambience.dm" #include "code\_globalvars\lists\basic_ai.dm" @@ -8751,6 +8753,7 @@ #include "modular_nova\modules\xenoarchartifacts\obj\geosample.dm" #include "modular_nova\modules\xenoarchartifacts\obj\particle_battery.dm" #include "modular_nova\modules\xenoarchartifacts\obj\wave_scanner.dm" +#include "modular_ss220\modular_includes.dme" #include "modular_ss220\master_files_skyrat\code\modules\client\preferences\emote_panel.dm" #include "modular_ss220\modules\auto_cryo\autocryo.dm" #include "modular_ss220\modules\auto_cryo\autocryo_config.dm" diff --git a/tgui/packages/tgui/interfaces/Modpacks.tsx b/tgui/packages/tgui/interfaces/Modpacks.tsx new file mode 100644 index 00000000000000..1ab191c544dc96 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Modpacks.tsx @@ -0,0 +1,361 @@ +// THIS IS A MASSMETA UI FILE + +import { useState } from 'react'; + +import { useBackend, useLocalState } from '../backend'; +import { + Box, + Collapsible, + Input, + LabeledList, + NoticeBox, + Section, + Stack, + Table, + Tabs, +} from '../components'; +import { Window } from '../layouts'; + +type Data = { + categories: string[]; + features: Modpack[]; + event: Modpack[]; + misc: Modpack[]; +}; + +type Modpack = { + name: string; + desc: string; + author: string; + icon_class: string; + id: string; +}; + +export const Modpacks = (props) => { + const { data } = useBackend(); + const { categories } = data; + const [selectedCategory, setSelectedCategory] = useState(categories[0]); + return ( + + + + В данный момент идёт наполнение меню модпаков, в игре модицикаций + больше чем вы можете тут видеть. + + + setSelectedCategory('Features')} + > + Фичи и Приколы + + setSelectedCategory('Event')} + > + Ивентовое + + setSelectedCategory('Misc')} + > + Разное + + + {(selectedCategory === 'Features' && ) || + (selectedCategory === 'Event' && ) || + (selectedCategory === 'Misc' && )} + + + ); +}; + +const FeaturesTable = () => { + const { data } = useBackend(); + const { features } = data; + + const [searchText, setSearchText] = useLocalState('searchText', ''); + + const searchBar = ( + setSearchText(value)} + /> + ); + + if (features.length === 0) { + return ( + + Этот сервер не использует какие-либо прикольные Фичи + + ); + } + + return ( + + +
{searchBar}
+
+ +
0 ? ( + Результаты поиска "{searchText}": + ) : ( + Суммарно модификации — {features.length} + ) + } + > + + + {features + .filter( + (feature) => + feature.name && + (searchText.length > 0 + ? feature.name + .toLowerCase() + .includes(searchText.toLowerCase()) || + feature.desc + .toLowerCase() + .includes(searchText.toLowerCase()) || + feature.author + .toLowerCase() + .includes(searchText.toLowerCase()) + : true), + ) + .map((feature) => ( + {feature.name}} + > + + + + + + + + {feature.desc} + + + {feature.author} + + + + + + ))} + + +
+
+
+ ); +}; + +const EventTable = () => { + const { data } = useBackend(); + const { event } = data; + + const [searchText, setSearchText] = useLocalState('searchText', ''); + + const searchBar = ( + setSearchText(value)} + /> + ); + + if (Event.length === 0) { + return ( + + Этот сервер не использует какие-либо переводы на Русский + + ); + } + + return ( + + +
{searchBar}
+
+ +
0 ? ( + Результаты поиска "{searchText}": + ) : ( + Суммарно модификации — {Event.length} + ) + } + > + + + {event + .filter( + (translate) => + translate.name && + (searchText.length > 0 + ? translate.name + .toLowerCase() + .includes(searchText.toLowerCase()) || + translate.desc + .toLowerCase() + .includes(searchText.toLowerCase()) || + translate.author + .toLowerCase() + .includes(searchText.toLowerCase()) + : true), + ) + .map((translate) => ( + {translate.name} + } + > + + + + + + + + {translate.desc} + + + {translate.author} + + + + + + ))} + + +
+
+
+ ); +}; + +const MiscTable = () => { + const { data } = useBackend(); + const { misc } = data; + + const [searchText, setSearchText] = useLocalState('searchText', ''); + + const searchBar = ( + setSearchText(value)} + /> + ); + + if (misc.length === 0) { + return ( + + Этот сервер не использует какие-либо модификации баланса или ревертов + + ); + } + + return ( + + +
{searchBar}
+
+ +
0 ? ( + Результаты поиска "{searchText}": + ) : ( + Суммарно модификации — {misc.length} + ) + } + > + + + {misc + .filter( + (revert) => + revert.name && + (searchText.length > 0 + ? revert.name + .toLowerCase() + .includes(searchText.toLowerCase()) || + revert.desc + .toLowerCase() + .includes(searchText.toLowerCase()) || + revert.author + .toLowerCase() + .includes(searchText.toLowerCase()) + : true), + ) + .map((revert) => ( + {revert.name}} + > + + + + + + + + {revert.desc} + + + {revert.author} + + + + + + ))} + + +
+
+
+ ); +}; From 948eb1526b82c73ee5c66e03702e7c48e1dbd82f Mon Sep 17 00:00:00 2001 From: SantaKO1 Date: Sat, 23 Nov 2024 11:58:33 +0200 Subject: [PATCH 2/8] =?UTF-8?q?=D0=92=D1=82=D0=BE=D1=80=D0=BE=D0=B9=20?= =?UTF-8?q?=D1=8D=D1=82=D0=B0=D0=BF=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8F?= =?UTF-8?q?=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=D0=BA=D1=83?= =?UTF-8?q?=D1=84=D1=80=D0=B0=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...include.dm => ~novarat_defines_include.dm} | 0 code/__HELPERS/~novarat_helpers_include.dm | 3 + ...lude.dm => ~novarat_globalvars_include.dm} | 0 modular_ss220/_modpack.dm | 4 + .../feature_example/feature_example.dm | 43 ++ tgstation.dme | 5 +- tgui/packages/tgui/interfaces/Modpacks.tsx | 512 +++++++----------- .../tgui/interfaces/Modpacks_original.tsx | 396 ++++++++++++++ 8 files changed, 638 insertions(+), 325 deletions(-) rename code/__DEFINES/{~meta_defines_include.dm => ~novarat_defines_include.dm} (100%) create mode 100644 code/__HELPERS/~novarat_helpers_include.dm rename code/_globalvars/{~meta_globalvars_include.dm => ~novarat_globalvars_include.dm} (100%) create mode 100644 tgui/packages/tgui/interfaces/Modpacks_original.tsx diff --git a/code/__DEFINES/~meta_defines_include.dm b/code/__DEFINES/~novarat_defines_include.dm similarity index 100% rename from code/__DEFINES/~meta_defines_include.dm rename to code/__DEFINES/~novarat_defines_include.dm diff --git a/code/__HELPERS/~novarat_helpers_include.dm b/code/__HELPERS/~novarat_helpers_include.dm new file mode 100644 index 00000000000000..07a45df07091fb --- /dev/null +++ b/code/__HELPERS/~novarat_helpers_include.dm @@ -0,0 +1,3 @@ +// THIS IS A NOVARAT FILE + +#include "..\..\modular_ss220\_helpers\_main_modular_helpers_include.dm" diff --git a/code/_globalvars/~meta_globalvars_include.dm b/code/_globalvars/~novarat_globalvars_include.dm similarity index 100% rename from code/_globalvars/~meta_globalvars_include.dm rename to code/_globalvars/~novarat_globalvars_include.dm diff --git a/modular_ss220/_modpack.dm b/modular_ss220/_modpack.dm index 12a08029480510..bee4b4fec3dbad 100644 --- a/modular_ss220/_modpack.dm +++ b/modular_ss220/_modpack.dm @@ -11,6 +11,8 @@ var/author /// A string with group of this modpack. Choose between "Features", "Translations" and "Reverts" var/group + /// A group accessible only to admins. + var/admin_group /// A list of your modpack's dependencies. If you use obj from another modpack - put it here. var/list/mod_depends = list() @@ -62,6 +64,7 @@ .["features"] = list() .["event"] = list() .["misc"] = list() + .["updates"] = list() var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/modpacks) for(var/datum/modpack/modpack as anything in SSmodpacks.loaded_modpacks) @@ -74,6 +77,7 @@ "author" = modpack.author, "icon_class" = assets.icon_class_name("modpack-[modpack.id]"), "id" = modpack.id, + "updates" = modpack.update_data, ) if (modpack.group == "Фичи" || modpack.group == "Features") diff --git a/modular_ss220/features/feature_example/feature_example.dm b/modular_ss220/features/feature_example/feature_example.dm index 4d390a019e7dcc..7e6796db62b9a6 100644 --- a/modular_ss220/features/feature_example/feature_example.dm +++ b/modular_ss220/features/feature_example/feature_example.dm @@ -5,3 +5,46 @@ group = "Features" desc = "Пример написания модпаков для фич." author = "К*дер" + + update_data = list( + list( + "version" = "1.0", + "description" = "Initial release of the feature example modpack", + "date" = "2024-03-15" + ), + list( + "version" = "1.1", + "description" = "Added new functionality and bug fixes", + "date" = "2024-04-01" + ), + list( + "version" = "1.2", + "description" = "Optimized performance and added new features", + "date" = "2024-05-10" + ), + list( + "version" = "1.3", + "description" = "И всё работает отлично!", + "date" = "2024-06-20" + ), + list( + "version" = "1.4", + "description" = "updates overflow", + "date" = "2024-06-21" + ), + list( + "version" = "1.5", + "description" = "updates overflow", + "date" = "2024-06-22" + ), + list( + "version" = "1.6", + "description" = "updates overflow", + "date" = "2024-06-23" + ), + list( + "version" = "1.7", + "description" = "updates overflow", + "date" = "2024-06-24" + ) + ) diff --git a/tgstation.dme b/tgstation.dme index a1a707d9843882..d1d7134bdb655d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -262,7 +262,7 @@ #include "code\__DEFINES\wounds.dm" #include "code\__DEFINES\xenobiology.dm" #include "code\__DEFINES\zoom.dm" -#include "code\__DEFINES\~meta_defines_include.dm" +#include "code\__DEFINES\~novarat_defines_include.dm" #include "code\__DEFINES\ai\ai.dm" #include "code\__DEFINES\ai\ai_blackboard.dm" #include "code\__DEFINES\ai\bot_keys.dm" @@ -582,6 +582,7 @@ #include "code\__HELPERS\view.dm" #include "code\__HELPERS\visual_effects.dm" #include "code\__HELPERS\weakref.dm" +#include "code\__HELPERS\~novarat_helpers_include.dm" #include "code\__HELPERS\logging\_logging.dm" #include "code\__HELPERS\logging\admin.dm" #include "code\__HELPERS\logging\antagonists.dm" @@ -629,7 +630,7 @@ #include "code\_globalvars\silo.dm" #include "code\_globalvars\tgui.dm" #include "code\_globalvars\time_vars.dm" -#include "code\_globalvars\~meta_globalvars_include.dm" +#include "code\_globalvars\~novarat_globalvars_include.dm" #include "code\_globalvars\lists\achievements.dm" #include "code\_globalvars\lists\ambience.dm" #include "code\_globalvars\lists\basic_ai.dm" diff --git a/tgui/packages/tgui/interfaces/Modpacks.tsx b/tgui/packages/tgui/interfaces/Modpacks.tsx index 1ab191c544dc96..e9f05ffb13e6db 100644 --- a/tgui/packages/tgui/interfaces/Modpacks.tsx +++ b/tgui/packages/tgui/interfaces/Modpacks.tsx @@ -1,18 +1,16 @@ -// THIS IS A MASSMETA UI FILE +// THIS IS NOVARAT TGUI ADDITION import { useState } from 'react'; -import { useBackend, useLocalState } from '../backend'; +import { useBackend } from '../backend'; import { Box, - Collapsible, + Button, Input, LabeledList, NoticeBox, Section, Stack, - Table, - Tabs, } from '../components'; import { Window } from '../layouts'; @@ -29,333 +27,201 @@ type Modpack = { author: string; icon_class: string; id: string; + updates?: ModpackUpdate[]; }; -export const Modpacks = (props) => { - const { data } = useBackend(); - const { categories } = data; - const [selectedCategory, setSelectedCategory] = useState(categories[0]); - return ( - - - - В данный момент идёт наполнение меню модпаков, в игре модицикаций - больше чем вы можете тут видеть. - - - setSelectedCategory('Features')} - > - Фичи и Приколы - - setSelectedCategory('Event')} - > - Ивентовое - - setSelectedCategory('Misc')} - > - Разное - - - {(selectedCategory === 'Features' && ) || - (selectedCategory === 'Event' && ) || - (selectedCategory === 'Misc' && )} - - - ); -}; - -const FeaturesTable = () => { - const { data } = useBackend(); - const { features } = data; - - const [searchText, setSearchText] = useLocalState('searchText', ''); - - const searchBar = ( - setSearchText(value)} - /> - ); - - if (features.length === 0) { - return ( - - Этот сервер не использует какие-либо прикольные Фичи - - ); - } - - return ( - - -
{searchBar}
-
- -
0 ? ( - Результаты поиска "{searchText}": - ) : ( - Суммарно модификации — {features.length} - ) - } - > - - - {features - .filter( - (feature) => - feature.name && - (searchText.length > 0 - ? feature.name - .toLowerCase() - .includes(searchText.toLowerCase()) || - feature.desc - .toLowerCase() - .includes(searchText.toLowerCase()) || - feature.author - .toLowerCase() - .includes(searchText.toLowerCase()) - : true), - ) - .map((feature) => ( - {feature.name}} - > - - - - - - - - {feature.desc} - - - {feature.author} - - - - - - ))} - - -
-
-
- ); +type ModpackUpdate = { + version: string; + description: string; + date: string; }; -const EventTable = () => { +export const Modpacks = (props) => { const { data } = useBackend(); - const { event } = data; - - const [searchText, setSearchText] = useLocalState('searchText', ''); - - const searchBar = ( - setSearchText(value)} - /> + const { categories, features, event, misc } = data; + const [selectedCategory, setSelectedCategory] = useState(categories[0]); + const [selectedModpack, setSelectedModpack] = useState(null); + const [searchText, setSearchText] = useState(''); + + const getCategoryModpacks = () => { + switch (selectedCategory) { + case 'Features': + return features; + case 'Event': + return event; + case 'Misc': + return misc; + default: + return []; + } + }; + + const filteredModpacks = getCategoryModpacks().filter( + (modpack) => + modpack.name && + (searchText.length === 0 || + modpack.name.toLowerCase().includes(searchText.toLowerCase()) || + modpack.desc.toLowerCase().includes(searchText.toLowerCase()) || + modpack.author.toLowerCase().includes(searchText.toLowerCase())), ); - if (Event.length === 0) { - return ( - - Этот сервер не использует какие-либо переводы на Русский - - ); - } - return ( - - -
{searchBar}
-
- -
0 ? ( - Результаты поиска "{searchText}": - ) : ( - Суммарно модификации — {Event.length} - ) - } - > - - - {event - .filter( - (translate) => - translate.name && - (searchText.length > 0 - ? translate.name - .toLowerCase() - .includes(searchText.toLowerCase()) || - translate.desc - .toLowerCase() - .includes(searchText.toLowerCase()) || - translate.author - .toLowerCase() - .includes(searchText.toLowerCase()) - : true), - ) - .map((translate) => ( - {translate.name} - } - > - - - - - - - - {translate.desc} - - - {translate.author} - - - - - + + + + {/* Left Column - Categories */} + +
+ + {categories.map((category) => ( + + + ))} - - -
-
-
+
+
+
+ + {/* Middle Column - Modpack List */} + +
+ + + setSearchText(value)} + mb={1} + /> + + {filteredModpacks.length === 0 ? ( + Модпаки не найдено + ) : ( + filteredModpacks.map((modpack) => ( + + + + )) + )} + +
+
+ + {/* Right Column - Modpack Details */} + +
+ {selectedModpack ? ( + + + + {selectedModpack.name} + + + + + + {selectedModpack.desc} + + + {selectedModpack.author} + + + + + + + История обновлений: + + {selectedModpack.updates && + selectedModpack.updates.length > 0 ? ( + selectedModpack.updates.map((update) => ( + + Версия {update.version} + {update.description} + + {update.date} + + + )) + ) : ( + + История обновлений отсутствует для этого модпака + + )} + + + + ) : ( + + Выберете модпак из списка для просмотра деталей + + )} +
+
+
+ + ); }; -const MiscTable = () => { - const { data } = useBackend(); - const { misc } = data; - - const [searchText, setSearchText] = useLocalState('searchText', ''); - - const searchBar = ( - setSearchText(value)} - /> - ); - - if (misc.length === 0) { - return ( - - Этот сервер не использует какие-либо модификации баланса или ревертов - - ); - } - - return ( - - -
{searchBar}
-
- -
0 ? ( - Результаты поиска "{searchText}": - ) : ( - Суммарно модификации — {misc.length} - ) - } - > - - - {misc - .filter( - (revert) => - revert.name && - (searchText.length > 0 - ? revert.name - .toLowerCase() - .includes(searchText.toLowerCase()) || - revert.desc - .toLowerCase() - .includes(searchText.toLowerCase()) || - revert.author - .toLowerCase() - .includes(searchText.toLowerCase()) - : true), - ) - .map((revert) => ( - {revert.name}} - > - - - - - - - - {revert.desc} - - - {revert.author} - - - - - - ))} - - -
-
-
- ); -}; +export default Modpacks; diff --git a/tgui/packages/tgui/interfaces/Modpacks_original.tsx b/tgui/packages/tgui/interfaces/Modpacks_original.tsx new file mode 100644 index 00000000000000..09fad7d8fd3613 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Modpacks_original.tsx @@ -0,0 +1,396 @@ +// THIS IS A MASSMETA UI FILE + +import { useState } from 'react'; + +import { useBackend, useLocalState } from '../backend'; +import { + Box, + Collapsible, + Input, + LabeledList, + NoticeBox, + Section, + Stack, + Table, + Tabs, +} from '../components'; +import { Window } from '../layouts'; + +type Data = { + categories: string[]; + features: Modpack[]; + event: Modpack[]; + misc: Modpack[]; +}; + +type Modpack = { + name: string; + desc: string; + author: string; + icon_class: string; + id: string; + updates?: ModpackUpdate[]; +}; + +type ModpackUpdate = { + version: string; + description: string; + date: string; +}; + +export const Modpacks = (props) => { + const { data } = useBackend(); + const { categories } = data; + const [selectedCategory, setSelectedCategory] = useState(categories[0]); + return ( + + + + В данный момент идёт наполнение меню модпаков, в игре модицикаций + больше чем вы можете тут видеть. + + + setSelectedCategory('Features')} + > + Фичи и Приколы + + setSelectedCategory('Event')} + > + Ивентовое + + setSelectedCategory('Misc')} + > + Разное (фиксы и удалённое) + + + {(selectedCategory === 'Features' && ) || + (selectedCategory === 'Event' && ) || + (selectedCategory === 'Misc' && )} + + + ); +}; + +const FeaturesTable = () => { + const { data } = useBackend(); + const { features } = data; + + const [searchText, setSearchText] = useLocalState('searchText', ''); + + const searchBar = ( + setSearchText(value)} + /> + ); + + if (features.length === 0) { + return ( + + Этот сервер не использует какие-либо прикольные Фичи + + ); + } + + return ( + + +
{searchBar}
+
+ +
0 ? ( + Результаты поиска "{searchText}": + ) : ( + Суммарно модификации — {features.length} + ) + } + > + + + {features + .filter( + (feature) => + feature.name && + (searchText.length > 0 + ? feature.name + .toLowerCase() + .includes(searchText.toLowerCase()) || + feature.desc + .toLowerCase() + .includes(searchText.toLowerCase()) || + feature.author + .toLowerCase() + .includes(searchText.toLowerCase()) + : true), + ) + .map((feature) => ( + {feature.name}} + > + + + + + + + + {feature.desc} + + + {feature.author} + + + {feature.updates && feature.updates.length > 0 && ( + + + + История обновлений: + + {feature.updates.map((update) => ( + + + {update.description} + + {update.date} + + + + ))} + + + )} + + + + ))} + + +
+
+
+ ); +}; + +const EventTable = () => { + const { data } = useBackend(); + const { event } = data; + + const [searchText, setSearchText] = useLocalState('searchText', ''); + + const searchBar = ( + setSearchText(value)} + /> + ); + + if (Event.length === 0) { + return ( + + Этот сервер не использует какие-либо переводы на Русский + + ); + } + + return ( + + +
{searchBar}
+
+ +
0 ? ( + Результаты поиска "{searchText}": + ) : ( + Суммарно модификации — {Event.length} + ) + } + > + + + {event + .filter( + (translate) => + translate.name && + (searchText.length > 0 + ? translate.name + .toLowerCase() + .includes(searchText.toLowerCase()) || + translate.desc + .toLowerCase() + .includes(searchText.toLowerCase()) || + translate.author + .toLowerCase() + .includes(searchText.toLowerCase()) + : true), + ) + .map((translate) => ( + {translate.name} + } + > + + + + + + + + {translate.desc} + + + {translate.author} + + + + + + ))} + + +
+
+
+ ); +}; + +const MiscTable = () => { + const { data } = useBackend(); + const { misc } = data; + + const [searchText, setSearchText] = useLocalState('searchText', ''); + + const searchBar = ( + setSearchText(value)} + /> + ); + + if (misc.length === 0) { + return ( + + Этот сервер не использует какие-либо модификации баланса или ревертов + + ); + } + + return ( + + +
{searchBar}
+
+ +
0 ? ( + Результаты поиска "{searchText}": + ) : ( + Суммарно модификации — {misc.length} + ) + } + > + + + {misc + .filter( + (revert) => + revert.name && + (searchText.length > 0 + ? revert.name + .toLowerCase() + .includes(searchText.toLowerCase()) || + revert.desc + .toLowerCase() + .includes(searchText.toLowerCase()) || + revert.author + .toLowerCase() + .includes(searchText.toLowerCase()) + : true), + ) + .map((revert) => ( + {revert.name}} + > + + + + + + + + {revert.desc} + + + {revert.author} + + + + + + ))} + + +
+
+
+ ); +}; From 1971270bddc0ba72e3da7cfb9caee6bbbb64bb1d Mon Sep 17 00:00:00 2001 From: SantaKO1 Date: Sun, 24 Nov 2024 13:56:19 +0200 Subject: [PATCH 3/8] =?UTF-8?q?=D0=A2=D1=80=D0=B5=D1=82=D0=B8=D0=B9=20?= =?UTF-8?q?=D1=8D=D1=82=D0=B0=D0=BF=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8F?= =?UTF-8?q?=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=D0=BA=D1=83?= =?UTF-8?q?=D1=84=D1=80=D0=B0=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modular_ss220/_modpack.dm | 25 +- .../events/event_example/event_example.dm | 7 - .../events/event_example/event_example.dme | 8 - .../feature_example/feature_example.dm | 43 -- .../features/feature_example/readme.md | 2 +- modular_ss220/modular_includes.dme | 4 +- modular_ss220/modularization_guide_ru.md | 176 +++--- modular_ss220/{misc => reverts}/readme.md | 0 .../{events => translations}/readme.md | 0 .../translation_example}/code/m_crayon.dm | 2 +- .../translation_example}/code/readme.md | 0 .../icons/crayondecal.dmi | Bin .../translation_example}/icons/readme.md | 0 .../translation_example}/preview.dmi | Bin .../translation_example}/readme.md | 2 +- .../translation_example.dm | 7 + .../translation_example.dme | 8 + tgui/packages/tgui/interfaces/Modpacks.tsx | 516 +++++++++++------- .../tgui/interfaces/Modpacks_original.tsx | 396 -------------- 19 files changed, 422 insertions(+), 774 deletions(-) delete mode 100644 modular_ss220/events/event_example/event_example.dm delete mode 100644 modular_ss220/events/event_example/event_example.dme rename modular_ss220/{misc => reverts}/readme.md (100%) rename modular_ss220/{events => translations}/readme.md (100%) rename modular_ss220/{events/event_example => translations/translation_example}/code/m_crayon.dm (54%) rename modular_ss220/{events/event_example => translations/translation_example}/code/readme.md (100%) rename modular_ss220/{events/event_example => translations/translation_example}/icons/crayondecal.dmi (100%) rename modular_ss220/{events/event_example => translations/translation_example}/icons/readme.md (100%) rename modular_ss220/{events/event_example => translations/translation_example}/preview.dmi (100%) rename modular_ss220/{events/event_example => translations/translation_example}/readme.md (76%) create mode 100644 modular_ss220/translations/translation_example/translation_example.dm create mode 100644 modular_ss220/translations/translation_example/translation_example.dme delete mode 100644 tgui/packages/tgui/interfaces/Modpacks_original.tsx diff --git a/modular_ss220/_modpack.dm b/modular_ss220/_modpack.dm index bee4b4fec3dbad..dd4c20e03ae255 100644 --- a/modular_ss220/_modpack.dm +++ b/modular_ss220/_modpack.dm @@ -11,17 +11,12 @@ var/author /// A string with group of this modpack. Choose between "Features", "Translations" and "Reverts" var/group - /// A group accessible only to admins. - var/admin_group /// A list of your modpack's dependencies. If you use obj from another modpack - put it here. var/list/mod_depends = list() - /// Is modpack visible, (for "Events" tab ONLY) + /// Is modpack visible, var/visible = TRUE // by default set to TRUE - /// List of updates for modpack - var/list/update_data = list() - // Modpacks initialization steps /datum/modpack/proc/pre_initialize() // Basic modpack fuctions if(!name) @@ -60,15 +55,14 @@ /datum/modpack/ui_static_data(mob/user) . = ..() - .["categories"] = list("Features", "Event", "Misc") + .["categories"] = list("Features", "Event", "Translations") .["features"] = list() - .["event"] = list() - .["misc"] = list() - .["updates"] = list() + .["translations"] = list() + .["reverts"] = list() var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/modpacks) for(var/datum/modpack/modpack as anything in SSmodpacks.loaded_modpacks) - if (!modpack.visible) // Временное решение (в будущем будет переписано для категории "Ивентовое") + if (!modpack.visible) // needed for examples (or for some kind of event) continue var/list/modpack_data = list( @@ -77,14 +71,13 @@ "author" = modpack.author, "icon_class" = assets.icon_class_name("modpack-[modpack.id]"), "id" = modpack.id, - "updates" = modpack.update_data, ) if (modpack.group == "Фичи" || modpack.group == "Features") .["features"] += list(modpack_data) - else if (modpack.group == "Ивентовое" || modpack.group == "Event") - .["event"] += list(modpack_data) - else if (modpack.group == "Разное" || modpack.group == "Misc") - .["misc"] += list(modpack_data) + else if (modpack.group == "Переводы" || modpack.group == "Translations") + .["translations"] += list(modpack_data) + else if (modpack.group == "Баланс" || modpack.group == "Reverts") + .["reverts"] += list(modpack_data) else CRASH("Modpack [modpack.name] has bad group name or queued for deletion.") diff --git a/modular_ss220/events/event_example/event_example.dm b/modular_ss220/events/event_example/event_example.dm deleted file mode 100644 index bdfb6756b8b2d6..00000000000000 --- a/modular_ss220/events/event_example/event_example.dm +++ /dev/null @@ -1,7 +0,0 @@ -/datum/modpack/event_example - id = "event_example" - icon = 'modular_ss220/events/event_example/preview.dmi' - name = "event_example" - group = "Event" - desc = "Пример модпака для ивета." - author = "К*дер" diff --git a/modular_ss220/events/event_example/event_example.dme b/modular_ss220/events/event_example/event_example.dme deleted file mode 100644 index a228c02b5acec1..00000000000000 --- a/modular_ss220/events/event_example/event_example.dme +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef EVENT_EXAMPLE -#define EVENT_EXAMPLE - -#include "event_example.dm" - -#include "code\m_crayon.dm" - -#endif diff --git a/modular_ss220/features/feature_example/feature_example.dm b/modular_ss220/features/feature_example/feature_example.dm index 7e6796db62b9a6..4d390a019e7dcc 100644 --- a/modular_ss220/features/feature_example/feature_example.dm +++ b/modular_ss220/features/feature_example/feature_example.dm @@ -5,46 +5,3 @@ group = "Features" desc = "Пример написания модпаков для фич." author = "К*дер" - - update_data = list( - list( - "version" = "1.0", - "description" = "Initial release of the feature example modpack", - "date" = "2024-03-15" - ), - list( - "version" = "1.1", - "description" = "Added new functionality and bug fixes", - "date" = "2024-04-01" - ), - list( - "version" = "1.2", - "description" = "Optimized performance and added new features", - "date" = "2024-05-10" - ), - list( - "version" = "1.3", - "description" = "И всё работает отлично!", - "date" = "2024-06-20" - ), - list( - "version" = "1.4", - "description" = "updates overflow", - "date" = "2024-06-21" - ), - list( - "version" = "1.5", - "description" = "updates overflow", - "date" = "2024-06-22" - ), - list( - "version" = "1.6", - "description" = "updates overflow", - "date" = "2024-06-23" - ), - list( - "version" = "1.7", - "description" = "updates overflow", - "date" = "2024-06-24" - ) - ) diff --git a/modular_ss220/features/feature_example/readme.md b/modular_ss220/features/feature_example/readme.md index 1f3678104b64e5..84b544585ab1bf 100644 --- a/modular_ss220/features/feature_example/readme.md +++ b/modular_ss220/features/feature_example/readme.md @@ -1,4 +1,4 @@ -## Module ID: CHEBUREK_CAR +## Module ID: FEATURES_EXAMPLE ### Defines: diff --git a/modular_ss220/modular_includes.dme b/modular_ss220/modular_includes.dme index 5540d60ae383ec..63ab955b3d5b66 100644 --- a/modular_ss220/modular_includes.dme +++ b/modular_ss220/modular_includes.dme @@ -10,8 +10,8 @@ #include "features\feature_example\feature_example.dme" -/* --EVENTS-- */ +/* --TRANSLATIONS-- */ -#include "events\event_example\event_example.dme" +#include "translations\translation_example\translation_example.dme" /* --MISC-- */ diff --git a/modular_ss220/modularization_guide_ru.md b/modular_ss220/modularization_guide_ru.md index eab7098e6c87bf..0d8bb8fb5601b5 100644 --- a/modular_ss220/modularization_guide_ru.md +++ b/modular_ss220/modularization_guide_ru.md @@ -8,7 +8,9 @@ Несоблюдение данного руководства приведёт к стагнации код-базы проекта, как это было до. Осознав наши прошлые ошибки – было принято решение привести проект к модульности подобно той, как на серверах Skyrat, однако доработанной под наши нужды. [Оригинальное руководство](https://github.com/NovaSector/NovaSector/blob/master/modular_nova/readme.md) (на англ.) -⚠️ **Все Баг-фиксы и рефакторы немодульного кода, изменение не Наших карт – Вам нужно будет заливать именно в апстрим /TG/station !** +Сервер **Skyrat** проекта **SS1984** по предварительному соглашению будет поддерживать такую же модульность как и на МассМете, а также участвовать в её развитии. + +⚠️ **Все Баг-фиксы и рефакторы немодульного кода, изменение не Наших карт – Вам нужно будет заливать именно в апстрим Nova Sector !** ### Про тестирование своих [PR'ов](https://ru.stackoverflow.com/questions/134183/%D0%A7%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5-pull-request) 🔬 @@ -30,7 +32,7 @@ Начнём сразу с показательного примера. -Предположим, что в какой-то строчке оригинального файла `foobar.dm` /TG/station было так: +Предположим, что в какой-то строчке оригинального файла `foobar.dm` Nova Sector было так: ```byond var/something = 1 @@ -40,10 +42,10 @@ var/something = 1 ```diff - var/something = 1 -+ var/something = 2 //MASSMETA EDIT ++ var/something = 2 //SS1984 EDIT ``` -Но неожиданно апстрим /TG/station вносит свои изменения (commit: their-feature) в эту же строку файла, меняя её у себя с **1** на **4**, +Но неожиданно апстрим Nova Sector вносит свои изменения (commit: their-feature) в эту же строку файла, меняя её у себя с **1** на **4**, ```diff - var/something = 1 @@ -53,18 +55,18 @@ var/something = 1 ```byond <<<<<< HEAD:foobar.dm -var/something = 2 //MASSMETA EDIT +var/something = 2 //SS1984 EDIT ====== var/something = 4 >>>>>> their-feature:foobar.dm ``` -В данном случае в череду коммитов /TG/station внедряется дополнительный, про который известно только нам самим. ГитХаб, видя подобное несоответствие - даёт нам сделать выбор. +В данном случае в череду коммитов Nova Sector внедряется дополнительный, про который известно только нам самим. ГитХаб, видя подобное несоответствие - даёт нам сделать выбор. Например, нам нужно оставить только Наше изменение, то просто удаляем все что нам добавил ГитХаб и оставляем только нужное, ```byond -var/something = 2 //MASSMETA EDIT +var/something = 2 //SS1984 EDIT ``` Подобного рода конфликты разрешаются именно ручками, однако есть другие подходы в виде модульного кода, о которых мы расскажем далее в данном руководстве. @@ -77,9 +79,9 @@ var/something = 2 //MASSMETA EDIT Этим уникальным **ID** Вы затем назовёте: -- Свою модульную папку `modular_meta/features/shuttle_toggle/`, в которой вы будете локально работать. +- Свою модульную папку `modular_ss220/features/shuttle_toggle/`, в которой вы будете локально работать. -- А также в дальнейшем Вы будете помечать все **Немодульные** изменения в коде /TG/station. +- А также в дальнейшем Вы будете помечать все **Немодульные** изменения в коде Nova Sector. - В редких случаях он может пригодится как некая метка для включения/отключения модуля. Об этом будет отдельно рассказано ниже. @@ -87,7 +89,7 @@ var/something = 2 //MASSMETA EDIT ### Не Модульные Изменения -Иногда наступает момент, когда редактирование **оригинальных** файлов /TG/station намного удобнее и практичее. +Иногда наступает момент, когда редактирование **оригинальных** файлов Nova Sector намного удобнее и практичее. 📌 Пожалуйста, не забудьте записать факт их изменения под пунктом **"TG Proc/File Changes"** в `readme.md` вашего модуля. @@ -96,46 +98,46 @@ var/something = 2 //MASSMETA EDIT - **Добавление:** ```byond - //MASSMETA EDIT ADDITION BEGIN (shuttle_toggle) + //SS1984 EDIT ADDITION BEGIN (shuttle_toggle) var/adminEmergencyNoRecall = FALSE var/lastMode = SHUTTLE_IDLE var/lastCallTime = 6000 - //MASSMETA EDIT ADDITION END + //SS1984 EDIT ADDITION END ``` - **Удаление:** ```byond - //MASSMETA EDIT REMOVAL BEGIN (shuttle_toggle) + //SS1984 EDIT REMOVAL BEGIN (shuttle_toggle) /* for(var/obj/docking_port/stationary/S in stationary) if(S.id = id) return S */ - //MASSMETA EDIT REMOVAL END + //SS1984 EDIT REMOVAL END ``` - **Изменения:** ```byond - //MASSMETA EDIT CHANGE BEGIN (shuttle_toggle) + //SS1984 EDIT CHANGE BEGIN (shuttle_toggle) /* ORIGINAL if(SHUTTLE_STRANDED, SHUTTLE_ESCAPE) */ if(SHUTTLE_STRANDED, SHUTTLE_ESCAPE, SHUTTLE_DISABLED) - //MASSMETA EDIT CHANGE END + //SS1984 EDIT CHANGE END return 1 ``` - 💡 Если предполагается **"Масштабное"** изменений или удаление кода, то его уже можно переместить в модуль. На месте удаления обязательно допишите куда оно было перемещенно: (Moved to: `modular_meta/features/shuttle_toggle/randomverbs.dm`) + 💡 Если предполагается **"Масштабное"** изменений или удаление кода, то его уже можно переместить в модуль. На месте удаления обязательно допишите куда оно было перемещенно: (Moved to: `modular_ss220/features/shuttle_toggle/randomverbs.dm`) ⚠️ Обязательно оставляем все что было до вашего вмешательства под пометкой **ORIGINAL**! ### Полностью Модульные Изменения -В нашем проекте присутствует папка `modular_meta/`, там будут храниться все наши **"Модульные"** изменения кода /TG/station. +В нашем проекте присутствует папка `modular_ss220/`, там будут храниться все наши **"Модульные"** изменения кода Nova Sector. -💡 Она полностью независима и этим мы гарантируем, что кодеры с /TG/station не будут туда вмешиваться. +💡 Она полностью независима и этим мы гарантируем, что кодеры с Nova Sector не будут туда вмешиваться. В этой папке есть ещё несколько подпапок и файлов: @@ -145,11 +147,13 @@ var/something = 2 //MASSMETA EDIT | **\_globalvars** 📁 | | **\_helpers** 📁 | | **features** 📁 | -| **perevody** 📁 | +| **translations** 📁 | | **reverts** 📁 | -| **tools** 📁 | +| **\_assets_modpacks.dm** 📄 | +| **\_modpack.dm** 📄 | | **\_modpacks_subsystem.dm** 📄 | -| **main_modular_include.dm** 📄 | +| **mods_icon_placeholder.dmi** 📄 | +| **modular_includes.dme** 📄 | | **modularization_guide_ru.md** 📝 | | **module_template.md** 📝 | @@ -164,9 +168,9 @@ var/something = 2 //MASSMETA EDIT Храните D/G/H своего модуля только в одном файле, назовите его также как `ID` вашего модуля. Все файлы в папках включены под `#include` в - - **`modular_meta/_defines/_main_modular_defines_include.dm`** - - **`modular_meta/_globalvars/_main_modular_globalvars_include.dm`** - - **`modular_meta/_helpers/_main_modular_helpers_include.dm`** + - **`modular_ss220/_defines/_main_modular_defines_include.dm`** + - **`modular_ss220/_globalvars/_main_modular_globalvars_include.dm`** + - **`modular_ss220/_helpers/_main_modular_helpers_include.dm`** 📌 Пожалуйста, не забудьте записать факт их добавления под пунктом **"Defines/Helpers:"** в `readme.md` вашего модуля. @@ -185,7 +189,7 @@ var/something = 2 //MASSMETA EDIT Подробнее про строение папок модуля расскажем чуть ниже. -- **`perevody/`** 📂 +- **`translations/`** 📂 Папка аналогичная `features/`, но в ней уже лежит перевод на Русский всякого в игре. @@ -195,7 +199,7 @@ var/something = 2 //MASSMETA EDIT - **`reverts/`** 📂 - Папка аналогичная `features/`, но там располагаются недавние откаты плохих и возвраты хороших по нашему мнению фич, введёных апстримом /TG/station. + Папка аналогичная `features/`, но там располагаются недавние откаты плохих и возвраты хороших по нашему мнению фич, введёных апстримом Nova Sector. ❗ Если фича была уже выпилена давно или же апстрим произвел её полное удаление сразу, то она уже может рассматриваться как самостоятельный модуль в `features/`! @@ -203,29 +207,22 @@ var/something = 2 //MASSMETA EDIT ⚠️ Укажите в `readme.md` модуля ссылку на пиар, который откатываевается! -- **`tools/`** 📂 - - Тут лежат все дополнительные инструменты проверки нашего кода. - - Они проверяют только файлы в модульной папке, помогая нам не совершать дополнительных ошибкок. - - К ним идет прямое обращение только в файле: `.github/workflows/ci_suite.yml`. - -## Подробнее про наполнение папок (features/ perevody/ reverts/) +## Подробнее про наполнение папок (features/ translations/ reverts/) Чтобы сохранить общий стиль и обеспечить удобную навигацию по большинству модулей, а также контролировать количество файлов и папок в репозитории, Вы должны располагать определённые типы файлов по своим папкам. ⚠️ Каждый модуль обязан содержать в себе файл документации модуля – `readme.md`. -| Папка/Файл | Содержимое | -| ------------------ | --------------------------------------------------------------------- | -| **code/** 📁 | Файлы кода: **`.dm`** | -| **icon/** 📁 | Файлы иконок и картинок: **`.dmi`** и **`.png`** | -| **sound/** 📁 | Звуковые файлы: **`.ogg`** и **`.waw`** | -| **includes.dm** 📄 | Инклюд всех файлов в папке **code/** и объект модуля `/datum/modpack/`| -| **readme.md** 📝 | Полная документация к модулю, [пример](module_template.md) | +| Папка/Файл | Содержимое | +| ------------------- | --------------------------------------------------------------------- | +| **code/** 📁 | Файлы кода: **`.dm`** | +| **icon/** 📁 | Файлы иконок и картинок: **`.dmi`** и **`.png`** | +| **sound/** 📁 | Звуковые файлы: **`.ogg`** и **`.waw`** | +| **includes.dm** 📄 | объект модуля `/datum/modpack/` | +| **includes.dme** 📄 | Инклюд всех файлов в папке **code/** | +| **readme.md** 📝 | Полная документация к модулю, [пример](module_template.md) | -⚠️ Файлы строк: `.txt` и `.json` вы помещаете в папку `strings/meta/`, т.к. код /TG/station не может работать нормально со всеми файлами-строк вне этой папки. +⚠️ Файлы строк: `.txt` и `.json` вы помещаете в папку `strings/ss220/`, т.к. код Nova Sector не может работать нормально со всеми файлами-строк вне этой папки. ⛔ У проектов **Skyrat** присуствует папка `master_files/`, однако у нас в проекте её НЕТ! Все переопредения кода у нас помещаются полностью в модуль с особыми пометками! Пояснения будут позже. @@ -245,23 +242,23 @@ var/something = 2 //MASSMETA EDIT Не забывайте проставлять все пути к иконкам и звуку правильно! -#### Переопределение объектов и функций /TG/station 🔀 +#### Переопределение объектов и функций Nova Sector 🔀 -С помощью этих файлов мы косвенно изменяем основной код /TG/station. Это позволяет нам очень изящно внедрять свои коррективы, не вмешиваясь напрямую в основной код. Тем самым не нарушая их череду коммитов и не создавая для нас самих в будущем **Конфликтов Слияния**. +С помощью этих файлов мы косвенно изменяем основной код Nova Sector. Это позволяет нам очень изящно внедрять свои коррективы, не вмешиваясь напрямую в основной код. Тем самым не нарушая их череду коммитов и не создавая для нас самих в будущем **Конфликтов Слияния**. -Однако это является и минусом такого подхода. Гитхаб не сможет нам оперативно подсказать где файл поменялся из-за вмешательства апстрима и где следует учесть измененое или дополнительное переопределение. Иногда прямые изменения кода через `//MASSMETA EDIT` предпочтительнее. Старайтесь использовать здравый смысл в этом вопросе. +Однако это является и минусом такого подхода. Гитхаб не сможет нам оперативно подсказать где файл поменялся из-за вмешательства апстрима и где следует учесть измененое или дополнительное переопределение. Иногда прямые изменения кода через `//SS1984 EDIT` предпочтительнее. Старайтесь использовать здравый смысл в этом вопросе. -⚠️ У данных файлов **Не надо соблюдать Иерархию** аналогичной папки `code/` у /TG/station! Просто ложите вместе со всеми файлами в модуль. +⚠️ У данных файлов **Не надо соблюдать Иерархию** аналогичной папки `code/` у Nova Sector! Просто ложите вместе со всеми файлами в модуль. -Эти файлы выносите в "отдельную группу" с помощью пометки `"m_"` в названии (от слова master), например: `m_tg_filename.dm`. +Эти файлы выносите в "отдельную группу" с помощью пометки `"m_"` в названии (от слова master), например: `m_nova_filename.dm`. -⚠️ Над каждым блоком таких функций/объектов **подписывайте в каком оригинальном файле /TG/station они расположены**. Таким образом нам будет проще смекнуть что к чему. +⚠️ Над каждым блоком таких функций/объектов **подписывайте в каком оригинальном файле Nova Sector они расположены**. Таким образом нам будет проще смекнуть что к чему. - **Пример модульного переопределения объекта** 💡 Например, Вы решили модульно переопределить иконку и описание у мольберта (easel). - Оригинальный объект в коде /TG/station по пути `code/modules/art/paintings.dm`: + Оригинальный объект в коде Nova Sector по пути `code/modules/art/paintings.dm`: ```byond /obj/structure/easel @@ -283,11 +280,11 @@ var/something = 2 //MASSMETA EDIT //ORIGINAL FILE: code/modules/art/paintings.dm /obj/structure/easel desc = "Let yourself draw!" - icon = 'modular_meta/features/art/icons/artstuff.dmi' + icon = 'modular_ss220/features/art/icons/artstuff.dmi' icon_state = "new_easel" ``` - Теперь при компилировании проекта, данное переопределение подменит эти переменные у оригинального объекта мольберта. Тем самым в готовом проекте у мольберта будет уже Новая иконка и описание. Даже код /TG/station менять не пришлось! 🎉 + Теперь при компилировании проекта, данное переопределение подменит эти переменные у оригинального объекта мольберта. Тем самым в готовом проекте у мольберта будет уже Новая иконка и описание. Даже код Nova Sector менять не пришлось! 🎉 - **Пример модульного добавления фичи в функцию** 💡 @@ -321,7 +318,7 @@ var/something = 2 //MASSMETA EDIT Тут мы обязательно вызываем такую конструкцию `. = ..()`, она по сути своей говорит, что мы **наследуемся от родительской функции**. После данной конструкции добаляем весь наш новый код. - Теперь при компилировании проекта, данные "добавления" допишутся в оригинальный объект и функцию. Этим мы добились того, что оружие при выстреле ещё и искрит, при том не вмешиваясь в функции /TG/station напрямую! 🎉 + Теперь при компилировании проекта, данные "добавления" допишутся в оригинальный объект и функцию. Этим мы добились того, что оружие при выстреле ещё и искрит, при том не вмешиваясь в функции Nova Sector напрямую! 🎉 ### **`. = ..()`** для чайников @@ -339,61 +336,24 @@ var/something = 2 //MASSMETA EDIT Так же в случае, если Вы не хотите возвращать родительский вывод, то Вы можете сохранить его в любую переменную или просто использовать `..()` -Мы так же можем вообще не вызывать `..()` - тогда функция оверрайдится полностью. Но я бы не рекомендовал делать подобное, ради перемещения ориг. функции в модуль и дальнейшей модификации её уже там, ибо когда /TG/station поменяет её у себя, то пиши пропало. +Мы так же можем вообще не вызывать `..()` - тогда функция оверрайдится полностью. Но я бы не рекомендовал делать подобное, ради перемещения ориг. функции в модуль и дальнейшей модификации её уже там, ибо когда Nova Sector поменяет её у себя, то пиши пропало. Также учтите, что вы не сможете при модульном дополнии функции использовать те переменные, которые были объявленны в функции оригинале! ## Отключаемые модули -В папке модуля есть файл `modular_meta/__config_modpacks.dm`. +В папке модуля есть файл `modular_ss220/modular_includes.dme`. -В нем перечисленны все модули, что можно отключить в игре. Для отключения требуется перекомпиляция проекта, т.к. эти изменения вступают в силу только на этапе работы [препроцессора](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%BE%D1%80). +В нем перечисленны все модули, что на данный момент активны. Для отключения требуется перекомпиляция проекта, т.к. эти изменения вступают в силу только на этапе работы [препроцессора](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%BE%D1%80). Чтобы отключить нужный вам модуль пропишите в начале модуля // #include... Преимущественно там будут присутсвовать модули **"Перевода"** ну и некоторые другие, что были сделаны отключаемыми по желанию автора модуля. В ином случае, модуль можно убрать только если залезать в код напрямую. -### Процедура создания отключаемого модуля - -Изначально мы имеем в оригинале код: - -```byond -/obj/item/toy/crayon/proc/crayon_text_strip(text) - text = copytext(text, 1, MAX_MESSAGE_LEN) - var/static/regex/crayon_regex = new /regex(@"[^\w!?,.=&%#+/\-]", "ig") - - return LOWER_TEXT(crayon_regex.Replace(text, "")) -``` - -Но нам потребовалось изменить одну строчку кода функции, что к сожалению просто так нельзя поместить в модуль. - -Для начала создадим в файле `modular_meta/__config_modpacks.dm` новый дефайн `RU_CRAYONS` и сразу присваеваем ему значение `1` = включен. - -```byond -#define RU_CRAYONS 1 -``` - -Далее заходим в интересующий нас файл и там оборачиваем изменяемый блок строчек в `#if – #else – #endif` конструкцию, где `RU_CRAYONS` – это **ID** вашего модуля. - -```byond -/obj/item/toy/crayon/proc/crayon_text_strip(text) - text = copytext(text, 1, MAX_MESSAGE_LEN) -#if RU_CRAYONS // MASSMETA EDIT - var/static/regex/crayon_regex = new /regex(@"[^\wА-Яа-яЁё!?,.=&%#+/\-]", "ig") -#else - var/static/regex/crayon_regex = new /regex(@"[^\w!?,.=&%#+/\-]", "ig") -#endif - return LOWER_TEXT(crayon_regex.Replace(text, "")) -``` - -Когда модуль включен `1` – то будет подставляться верхняя строчка кода, когда выключен `0` – оригинальная. - -⚠️ Такие измения уже содержат ID вашего модуля, поэтому достаточно их помечать только конструкцией `// MASSMETA EDIT`. - ## Карты 🗺️ Используются карты: -- 🔴 Оригинальные с офф ТГ (без прямых наших изменений) +- 🔴 Оригинальные с офф Новы (без прямых наших изменений) - 🟡 Заимствованные с других билдов (некоторые желательно не менять, т.к. мы можем подтягивать обновления в других билдов) -- 🟢 Наши самодельные, они же полностью независимые, например, ProtoBoxStation (меняем и чиним как хотим) +- 🟢 Наши самодельные, они же полностью независимые (меняем и чиним как хотим) ### Новые карты (наши самодельные) @@ -401,9 +361,9 @@ var/something = 2 //MASSMETA EDIT К каждой карте идёт дополнительно `.json` файл-конфигурации в `_maps/`, не забудьте добавить его тоже! -### Модульное Изменение Карт ТГ (через применение Авто-мапперов) +### Модульное Изменение Карт Новы (через применение Авто-мапперов) -⚠️ Не изменяйте оригинальные карты /TG/station напрямую, Вы стокнётесь потом с таким же хаосом, как если вы бы меняли файлы иконок! Для внесения изменений ипользуйте модуль Авто-маппера. +⚠️ Не изменяйте оригинальные карты Nova Sector напрямую, Вы стокнётесь потом с таким же хаосом, как если вы бы меняли файлы иконок! Для внесения изменений ипользуйте модуль Авто-маппера. Когда вы добавляете новый элемент на карту, то вы должны сперва определеить масштаб переделок. @@ -425,28 +385,28 @@ var/something = 2 //MASSMETA EDIT 📌 Пожалуйста, не забудьте записать факт их добавления/изменения под пунктом **"TGUI Files:"** в `readme.md` вашего модуля. -### Изменение оригинальных файлов /TG/station +### Изменение оригинальных файлов Nova Sector При изменении оригинальных файлов TGUI поступаем аналогично, как и при изменении вышележащего кода DM, однако схема написания комментариев тут несколько иная. -Вы можете использовать как `// MASSMETA EDIT`, так и `/* MASSMETA EDIT */`, хотя в некоторых случаях вам придется использовать одно вместо другого. (в некотрых языках '//' - могут не являться комментированием, учтите это) +Вы можете использовать как `// SS1984 EDIT`, так и `/* SS1984 EDIT */`, хотя в некоторых случаях вам придется использовать одно вместо другого. (в некотрых языках '//' - могут не являться комментированием, учтите это) В целом, старайтесь, чтобы комментарии к изменениям находились на той же строке, что и само изменение. Предпочтительно внутри JSX-тега. Например: ```js ``` ```js ``` @@ -457,11 +417,11 @@ var/something = 2 //MASSMETA EDIT В крайнем случае Вы можете заключить ваше редактирование в фигурные скобки, например так: ```js -{/* MASSMETA EDIT ADDITION START */} +{/* SS1984 EDIT ADDITION START */} someProp="whatever" -{/* MASSMETA EDIT ADDITION END */} +{/* SS1984 EDIT ADDITION END */} ``` ### Создание новых файлов TGUI @@ -469,12 +429,12 @@ var/something = 2 //MASSMETA EDIT ⚠️ При создании нового файла TGUI с нуля, пожалуйста, добавьте **Заголовочный Комментарий** самом верху файла: ```js -// THIS IS A MASSMETA UI FILE +// THIS IS A SS1984 UI FILE ``` Таким образом, они легко идентифицируются как **Наши** модульные файлы TGUI `.tsx` и `.jsx`. -Собственно ничего больше делать и не нужно, комментарии `// MASSMETA EDIT` в таком файле TGUI излишне. +Собственно ничего больше делать и не нужно, комментарии `// SS1984 EDIT` в таком файле TGUI излишне.