From 10c1085abad32de98e9acc686232fb860ff6cb47 Mon Sep 17 00:00:00 2001 From: BrettMayson Date: Wed, 28 Jun 2023 04:39:08 -0600 Subject: [PATCH] Repair - Add wheel patching (#8835) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * patch wheels on vehicles * all tire patching working Co-authored-by: mp-singh * doc * remove unused string * Update CfgVehicles.hpp * use strintable * setting for where the wheel can be patched * localize * Update stringtable.xml * can't patch a missing tire * removal > replacement * Update addons/repair/initSettings.sqf Co-authored-by: PabstMirror * Apply suggestions from code review Co-authored-by: Filip Maciejewski * Apply suggestions from code review Co-authored-by: PabstMirror * Apply suggestions from code review Co-authored-by: Jouni Järvinen * patch icon * use PATCH_WHEEL_STEP_TIME * fix wheel translation --------- Co-authored-by: mp-singh Co-authored-by: PabstMirror Co-authored-by: Filip Maciejewski Co-authored-by: Jouni Järvinen --- addons/repair/ACE_Repair.hpp | 11 +++ addons/repair/CfgVehicles.hpp | 13 ++++ addons/repair/XEH_PREP.hpp | 5 ++ .../repair/functions/fnc_addRepairActions.sqf | 30 +++++--- .../functions/fnc_canPatchRemovedWheel.sqf | 27 +++++++ addons/repair/functions/fnc_canPatchWheel.sqf | 31 ++++++++ .../functions/fnc_doPatchWheelProgress.sqf | 35 ++++++++++ .../functions/fnc_getPatchWheelTime.sqf | 25 +++++++ .../functions/fnc_patchRemovedWheel.sqf | 39 +++++++++++ addons/repair/initSettings.sqf | 45 ++++++++++++ addons/repair/script_component.hpp | 2 + addons/repair/stringtable.xml | 66 +++++++++++++++--- addons/repair/ui/patch_ca.paa | Bin 0 -> 5625 bytes docs/wiki/feature/repair.md | 5 ++ 14 files changed, 316 insertions(+), 18 deletions(-) create mode 100644 addons/repair/functions/fnc_canPatchRemovedWheel.sqf create mode 100644 addons/repair/functions/fnc_canPatchWheel.sqf create mode 100644 addons/repair/functions/fnc_doPatchWheelProgress.sqf create mode 100644 addons/repair/functions/fnc_getPatchWheelTime.sqf create mode 100644 addons/repair/functions/fnc_patchRemovedWheel.sqf create mode 100644 addons/repair/ui/patch_ca.paa diff --git a/addons/repair/ACE_Repair.hpp b/addons/repair/ACE_Repair.hpp index 3a3be9213c..ae436791ad 100644 --- a/addons/repair/ACE_Repair.hpp +++ b/addons/repair/ACE_Repair.hpp @@ -30,6 +30,17 @@ class ACE_Repair { callbackSuccess = QFUNC(doRemoveWheel); claimObjects[] = {}; }; + class PatchWheel: ReplaceWheel { + displayName = CSTRING(PatchWheel); + displayNameProgress = CSTRING(PatchingWheel); + condition = QFUNC(canPatchWheel); + repairingTime = QFUNC(getPatchWheelTime); + callbackProgress = QFUNC(doPatchWheelProgress); + items = QGVAR(patchWheelRequiredItems); + requiredEngineer = QGVAR(engineerSetting_Wheel); + callbackSuccess = ""; + claimObjects[] = {}; + }; class MiscRepair: ReplaceWheel { displayName = CSTRING(Repairing); // let's make empty string an auto generated string displayNameProgress = CSTRING(RepairingHitPoint); diff --git a/addons/repair/CfgVehicles.hpp b/addons/repair/CfgVehicles.hpp index 6d7a053df2..c1641e52ec 100644 --- a/addons/repair/CfgVehicles.hpp +++ b/addons/repair/CfgVehicles.hpp @@ -403,6 +403,19 @@ class CfgVehicles { }; editorPreview = QPATHTOF(data\preview_wheel.jpg); + + class ACE_Actions: ACE_Actions { + class ACE_MainActions: ACE_MainActions { + class GVAR(Patch) { + displayName = CSTRING(PatchWheel); + distance = 4; + condition = QUOTE([ARR_2(_player, _target)] call FUNC(canPatchRemovedWheel)); + statement = QUOTE([ARR_2(_player, _target)] call FUNC(patchRemovedWheel)); + exceptions[] = {"isNotDragging", "isNotCarrying", "isNotOnLadder", "isNotSwimming", "isNotSitting"}; + icon = QPATHTOF(ui\patch_ca.paa); + }; + }; + }; }; // disable vanilla repair diff --git a/addons/repair/XEH_PREP.hpp b/addons/repair/XEH_PREP.hpp index 45fcb0206b..7c82632f87 100644 --- a/addons/repair/XEH_PREP.hpp +++ b/addons/repair/XEH_PREP.hpp @@ -2,12 +2,15 @@ PREP(addRepairActions); PREP(addSpareParts); PREP(canMiscRepair); +PREP(canPatchRemovedWheel); +PREP(canPatchWheel); PREP(canRemove); PREP(canRepair); PREP(canRepairTrack); PREP(canReplaceTrack); PREP(canReplaceWheel); PREP(doFullRepair); +PREP(doPatchWheelProgress); PREP(doRemoveTrack); PREP(doRemoveWheel); PREP(doRepair); @@ -16,6 +19,7 @@ PREP(doReplaceTrack); PREP(doReplaceWheel); PREP(getClaimObjects); PREP(getHitPointString); +PREP(getPatchWheelTime); PREP(getPostRepairDamage); PREP(getRepairItems); PREP(getWheelHitPointsWithSelections); @@ -32,6 +36,7 @@ PREP(moduleAssignRepairVehicle); PREP(moduleAssignRepairFacility); PREP(moduleRepairSettings); PREP(normalizeHitPoints); +PREP(patchRemovedWheel); PREP(repair); PREP(repair_failure); PREP(repair_success); diff --git a/addons/repair/functions/fnc_addRepairActions.sqf b/addons/repair/functions/fnc_addRepairActions.sqf index b3d2bb91df..f78863985d 100644 --- a/addons/repair/functions/fnc_addRepairActions.sqf +++ b/addons/repair/functions/fnc_addRepairActions.sqf @@ -56,14 +56,6 @@ private _turretPaths = ((fullCrew [_vehicle, "gunner", true]) + (fullCrew [_vehi TRACE_3("Adding Wheel Actions",_hitpoint,_forEachIndex,_selection); - // An action to remove the wheel is required - private _name = format ["Remove_%1_%2", _forEachIndex, _hitpoint]; - private _text = localize LSTRING(RemoveWheel); - private _condition = {[_this select 1, _this select 0, _this select 2 select 0, "RemoveWheel"] call DFUNC(canRepair)}; - private _statement = {[_this select 1, _this select 0, _this select 2 select 0, "RemoveWheel"] call DFUNC(repair)}; - private _action = [_name, _text, _icon, _statement, _condition, {}, [_hitpoint], _position, 2, nil, FUNC(modifySelectionInteraction)] call EFUNC(interact_menu,createAction); - [_type, 0, [], _action] call EFUNC(interact_menu,addActionToClass); - // An action to replace the wheel is required _name = format ["Replace_%1_%2", _forEachIndex, _hitpoint]; _text = localize LSTRING(ReplaceWheel); @@ -72,6 +64,28 @@ private _turretPaths = ((fullCrew [_vehicle, "gunner", true]) + (fullCrew [_vehi _action = [_name, _text, _icon, _statement, _condition, {}, [_hitpoint], _position, 2] call EFUNC(interact_menu,createAction); [_type, 0, [], _action] call EFUNC(interact_menu,addActionToClass); + // Create a wheel interaction + private _root = format ["Wheel_%1_%2", _forEachIndex, _hitpoint]; + private _action = [_root, localize LSTRING(Wheel), ["","#FFFFFF"], {}, {true}, {}, [_hitpoint], _position, 2, nil, LINKFUNC(modifySelectionInteraction)] call EFUNC(interact_menu,createAction); + [_type, 0, [], _action] call EFUNC(interact_menu,addActionToClass); + + // An action to remove the wheel is required + private _name = format ["Remove_%1_%2", _forEachIndex, _hitpoint]; + private _text = localize LSTRING(RemoveWheel); + private _condition = {[_this select 1, _this select 0, _this select 2 select 0, "RemoveWheel"] call DFUNC(canRepair)}; + private _statement = {[_this select 1, _this select 0, _this select 2 select 0, "RemoveWheel"] call DFUNC(repair)}; + private _action = [_name, _text, _icon, _statement, _condition, {}, [_hitpoint], _position, 2] call EFUNC(interact_menu,createAction); + [_type, 0, [_root], _action] call EFUNC(interact_menu,addActionToClass); + + // An action to patch the wheel is required. + private _name = format ["Patch_%1_%2", _forEachIndex, _hitpoint]; + private _patchIcon = QPATHTOF(ui\patch_ca.paa); + private _text = localize LSTRING(PatchWheel); + private _condition = {("vehicle" in GVAR(patchWheelLocation)) && {[_this select 1, _this select 0, _this select 2 select 0, "PatchWheel"] call DFUNC(canRepair)}}; + private _statement = {[_this select 1, _this select 0, _this select 2 select 0, "PatchWheel"] call DFUNC(repair)}; + private _action = [_name, _text, _patchIcon, _statement, _condition, {}, [_hitpoint], _position, 2] call EFUNC(interact_menu,createAction); + [_type, 0, [_root], _action] call EFUNC(interact_menu,addActionToClass); + _processedSelections pushBack _selection; } else { // Empty hitpoints don't contain enough information diff --git a/addons/repair/functions/fnc_canPatchRemovedWheel.sqf b/addons/repair/functions/fnc_canPatchRemovedWheel.sqf new file mode 100644 index 0000000000..870f6919c6 --- /dev/null +++ b/addons/repair/functions/fnc_canPatchRemovedWheel.sqf @@ -0,0 +1,27 @@ +#include "script_component.hpp" +/* + * Author: commy2, Brett Mayson + * Check if the unit can patch a wheel not on a vehicle. + * + * Arguments: + * 0: Unit that does the patching + * 1: Wheel to patch + * + * Return Value: + * Can patch wheel? + * + * Example: + * [unit, vehicle] call ace_repair_fnc_canPatchRemovedWheel + * + * Public: No + */ + +params ["_unit", "_target"]; +TRACE_2("params",_unit,_target); + +if (GVAR(patchWheelEnabled) == -1) exitWith {false}; +if !("ground" in GVAR(patchWheelLocation)) exitWith {false}; +if ((GVAR(patchWheelRequiredItems) isEqualTo [ANY_TOOLKIT_FAKECLASS]) && {!([_unit, [GVAR(allToolKits)]] call FUNC(hasItems))}) exitWith {false}; +if !([_unit, GVAR(patchWheelEnabled)] call FUNC(isEngineer)) exitWith {false}; + +(damage _target > GVAR(patchWheelMaximumRepair)) diff --git a/addons/repair/functions/fnc_canPatchWheel.sqf b/addons/repair/functions/fnc_canPatchWheel.sqf new file mode 100644 index 0000000000..46d73e9cba --- /dev/null +++ b/addons/repair/functions/fnc_canPatchWheel.sqf @@ -0,0 +1,31 @@ +#include "script_component.hpp" +/* + * Author: commy2, Brett Mayson + * Check if the unit can patch given wheel of the vehicle. + * + * Arguments: + * 0: Unit that does the patching + * 1: Vehicle to patch + * 2: Selected wheel hitpoint + * + * Return Value: + * Can patch wheel? + * + * Example: + * [unit, vehicle, "hitpoint"] call ace_repair_fnc_canPatchWheel + * + * Public: No + */ + +params ["_unit", "_target", "_hitPoint"]; +TRACE_3("params",_unit,_target,_hitPoint); + +if (GVAR(patchWheelEnabled) == -1) exitWith {false}; + +private _damage = _target getHitPointDamage _hitPoint; + +if (_damage == 1) exitWith {false}; + +if !([_unit, _target, ["isNotDragging", "isNotCarrying", "isNotOnLadder"]] call EFUNC(common,canInteractWith)) exitWith {false}; + +(_damage > GVAR(patchWheelMaximumRepair)) diff --git a/addons/repair/functions/fnc_doPatchWheelProgress.sqf b/addons/repair/functions/fnc_doPatchWheelProgress.sqf new file mode 100644 index 0000000000..c2561bef0d --- /dev/null +++ b/addons/repair/functions/fnc_doPatchWheelProgress.sqf @@ -0,0 +1,35 @@ +#include "script_component.hpp" +/* + * Author: commy2, Brett Mayson + * Called by repair action / progress bar. Raise events to set the new hitpoint damage. + * + * Arguments: + * 0: Unit that does the patching + * 1: Vehicle to patch + * 2: Selected wheel hitpoint + * + * Return Value: + * Should patching continue? + * + * Example: + * [unit, vehicle, "hitpoint"] call ace_repair_fnc_doPatchWheelProgress + * + * Public: No + */ + +params ["_args", "_elapsedTime", "_totalTime"]; +_args params ["_unit", "_vehicle", "_hitPoint"]; +TRACE_3("params",_unit,_vehicle,_hitPoint); + +// get current hitpoint damage +private _hitPointDamage = _vehicle getHitPointDamage _hitPoint; + +private _iterationsRemaining = ceil ((_hitPointDamage - GVAR(patchWheelMaximumRepair)) / PATCH_WHEEL_STEP_TIME) - 1; +if ((_totalTime - _elapsedTime) > _iterationsRemaining * GVAR(patchWheelTime)) exitWith {true}; + +_hitPointDamage = (_hitPointDamage - PATCH_WHEEL_STEP_TIME) max GVAR(patchWheelMaximumRepair); + +// raise event to set the new hitpoint damage +[QGVAR(setWheelHitPointDamage), [_vehicle, _hitPoint, _hitPointDamage], _vehicle] call CBA_fnc_targetEvent; + +(_hitPointDamage > GVAR(patchWheelMaximumRepair)) diff --git a/addons/repair/functions/fnc_getPatchWheelTime.sqf b/addons/repair/functions/fnc_getPatchWheelTime.sqf new file mode 100644 index 0000000000..5447ef4211 --- /dev/null +++ b/addons/repair/functions/fnc_getPatchWheelTime.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Author: Brett Mayson + * Calculate the time to patch the wheel + * + * Arguments: + * 0: Unit that does the patching + * 1: Vehicle to patch + * 2: Selected wheel hitpoint + * + * Return Value: + * Patching time + * + * Example: + * [unit, vehicle, "hitpoint"] call ace_repair_fnc_getPatchWheelTime + * + * Public: No + */ + +params ["_unit", "_vehicle", "_hitPoint"]; + +// get current hitpoint damage +private _hitPointDamage = (_vehicle getHitPointDamage _hitPoint) - GVAR(patchWheelMaximumRepair); + +ceil (_hitPointDamage / PATCH_WHEEL_STEP_TIME) * GVAR(patchWheelTime) diff --git a/addons/repair/functions/fnc_patchRemovedWheel.sqf b/addons/repair/functions/fnc_patchRemovedWheel.sqf new file mode 100644 index 0000000000..4aab7c72c3 --- /dev/null +++ b/addons/repair/functions/fnc_patchRemovedWheel.sqf @@ -0,0 +1,39 @@ +#include "script_component.hpp" +/* + * Author: Brett Mayson + * Patch a wheel that is not on a vehicle + * + * Arguments: + * 0: Unit that does the patching + * 1: Wheel to patch + * + * Return Value: + * None + * + * Example: + * [unit, vehicle] call ace_repair_fnc_patchRemovedWheel + * + * Public: No + */ + +params ["_unit", "_target"]; + +[_unit, "Acts_carFixingWheel"] call EFUNC(common,doAnimation); + +private _wheelDamage = (damage _target) - GVAR(patchWheelMaximumRepair); + +[ceil (_wheelDamage / 0.05) * GVAR(patchWheelTime), [_target], {}, {}, LLSTRING(PatchingWheel), { + params ["_args"]; + _args params ["_target"]; + private _damage = damage _target; + + private _iterationsRemaining = ceil ((_damage - GVAR(patchWheelMaximumRepair)) / 0.05) - 1; + if ((_totalTime - _elapsedTime) > _iterationsRemaining * GVAR(patchWheelTime)) exitWith {true}; + + _damage = (_damage - 0.05) max GVAR(patchWheelMaximumRepair); + + _target setDamage _damage; + + _damage > GVAR(patchWheelMaximumRepair) +}] call ace_common_fnc_progressBar; + diff --git a/addons/repair/initSettings.sqf b/addons/repair/initSettings.sqf index fddd22b541..82a411c91d 100644 --- a/addons/repair/initSettings.sqf +++ b/addons/repair/initSettings.sqf @@ -25,6 +25,32 @@ {[QGVAR(engineerSetting_wheel), _this] call EFUNC(common,cbaSettings_settingChanged)} ] call CBA_fnc_addSetting; +[ + QGVAR(patchWheelEnabled), "LIST", + [LSTRING(patchWheelEnabled_name), LSTRING(patchWheelEnabled_description)], + [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + [[-1,0,1,2],["str_player_none", LSTRING(engineerSetting_anyone), LSTRING(engineerSetting_EngineerOnly), LSTRING(engineerSetting_AdvancedOnly)],1], // default value + true // isGlobal +] call CBA_fnc_addSetting; + +[ + QGVAR(patchWheelRequiredItems), + "LIST", + [LSTRING(patchWheelRequiredItems_DisplayName), LSTRING(patchWheelRequiredItems_Description)], + [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + [[[], [ANY_TOOLKIT_FAKECLASS]], ["STR_A3_None", "STR_A3_CfgWeapons_Toolkit0"], 1], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(patchWheelLocation), + "LIST", + [LSTRING(patchWheelLocation_DisplayName), LSTRING(patchWheelLocation_Description)], + [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + [[["ground", "vehicle"], ["vehicle"], ["ground"]], ["str_difficulty_any", LSTRING(patchWheelOnVehicle), LSTRING(patchWheelOnGround)], 0], + true +] call CBA_fnc_addSetting; + [ QGVAR(repairDamageThreshold), "SLIDER", [LSTRING(repairDamageThreshold_name), LSTRING(repairDamageThreshold_description)], @@ -124,3 +150,22 @@ true, // isGlobal {[QGVAR(autoShutOffEngineWhenStartingRepair), _this] call EFUNC(common,cbaSettings_settingChanged)} ] call CBA_fnc_addSetting; + + +[ + QGVAR(patchWheelTime), + "SLIDER", + [LSTRING(patchWheelTime_DisplayName), LSTRING(patchWheelTime_Description)], + [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + [0.1, 60, 5, 1], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(patchWheelMaximumRepair), + "SLIDER", + [LSTRING(patchWheelMaximumRepair_DisplayName), LSTRING(patchWheelMaximumRepair_Description)], + [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + [0, 1, 0.3, 1, true], + true +] call CBA_fnc_addSetting; diff --git a/addons/repair/script_component.hpp b/addons/repair/script_component.hpp index 35ab2f4f8c..2a6196c6fc 100644 --- a/addons/repair/script_component.hpp +++ b/addons/repair/script_component.hpp @@ -21,3 +21,5 @@ #define DAMAGE_COLOR_SCALE ["#FFFFFF", "#FFFF7E", "#FFEC4D", "#FFD52C", "#FCB121", "#FF9916", "#FF7D16", "#FF4400", "#FF0000"] #define ANY_TOOLKIT_FAKECLASS QGVAR(anyToolKit) + +#define PATCH_WHEEL_STEP_TIME 0.05 diff --git a/addons/repair/stringtable.xml b/addons/repair/stringtable.xml index 2224f33bdf..3b650f13d5 100644 --- a/addons/repair/stringtable.xml +++ b/addons/repair/stringtable.xml @@ -35,6 +35,11 @@ 備用輪胎 Yedek Tekerlek + + Wheel + Rad + Rueda + Change Wheel Reifen wechseln @@ -1206,19 +1211,10 @@ Sadece Gelişmiş Mühendis - Allow Wheel + Allow Wheel Replacement Erlaube Radwechsel Wymiana kół - Permite rodas Разрешить замену колес - Možnost Výměny Kol - Permitir rueda - Consenti Ruota - Roues autorisées pour - タイヤを許可 - 바퀴 허가 - 允许轮胎 - 允許輪胎 Who can remove and replace wheels? @@ -1235,6 +1231,14 @@ 谁可维修轮胎? 誰可維修輪胎? + + Allow Wheel Patching + Erlaube Radflicken + + + Who can patch wheels? + Wer kann Radflicken durchführen? + Allow Repair Erlaube Reperatur @@ -1885,6 +1889,14 @@ 需要特定物品来移除/更换车轮 需要特定物品來移除/更換車輪 + + Wheel Patch Requirements + Bedingungen für die Radflicken + + + Items required to patch a wheel. + Gegenstänge, die zum Reifenflicken benötigt werden. + Misc Repair Requirements Sonstige Reparaturbedingungen @@ -2050,5 +2062,39 @@ Apagar el motor automáticamente al efectuar una reparación 수리 시 엔진을 자동으로 끕니다. + + Patch Wheel + Radflicken + + + Patching Wheel... + Radflicken... + + + Patch wheel time + + + Time it takes to patch a wheel by 5%. + + + Patch wheel maximum repair + + + Maximum level a wheel can be patched. + + + Wheel Patch Location + + + Where the wheel can be patched. + + + On the ground + Auf dem Boden + + + On a vehicle + An einem Fahrzeug + diff --git a/addons/repair/ui/patch_ca.paa b/addons/repair/ui/patch_ca.paa new file mode 100644 index 0000000000000000000000000000000000000000..4aadbc2174b03c9cbf98239b6941967779cda3e2 GIT binary patch literal 5625 zcmeI0e{5S<6~}MVcGM|p$^?_pQl&s6)}&b`)R>7@Bw<^lPLwd#7e`V^WmKP ze4n3R)=d=>NU&R_&8O$ybMN<@bAQ&?!lk_O^m(xamEHef#dP#_F4mLU<^Iv8cisEr++`>|F?L|V*dU9 zig)A#ak=9@fYAlU=6d&!B6nTKa&Re(W#Oskkw2C~9zh1l`fcPP!CA?gMrOyhYW)GK z-!0@qdk8-BHrZ3T{>xB)!Px(%i6`K%Y|jt2HWwcNn8fuRk_HB`zdW*5<8f)f0+dPbd^58ODRvXKp-mCH=;O{k}*ghO_#0sP|YmrsU4K0 zyI(}Tr(8T9WhEOdZ(Nc)z9B!!_4hiRy<)se$t1S;ySfCIC!db2pdZ0AvEi626bKj@ zFd(<$cp?~+K+(r*3|Y~i^Xa_*cw#7${UCn|==y`Wo_jrPz3^vG24i)Sq}zG?i`Q6M zj`zD}yejG+Q7omUrT1KwJ9o;Rmnh$J;qaUo?@(r9;RdHuFzFqCt+qxmt=El$jeOC> zU}AIsd3e2GJYWam?@VGkg7S6d@sXzV_|baM|54Fjv)h&gQ#?E#|MvyEl;M~K1^2z? zRcHFuX#jh21Geq=Te}r&Pm=Uw`gOfjzfNpoJk3{AGd>~sOk!!(`poM!vbq0iUahx1 z{gj?R(x<9~S!9T#>7>c@1KI-m2pru(V>T?)mc@gZyX4Y`}^ z@5Lti+mpiu`pN%bG#V9lbADFqDU8nt+Y9xt_Frh9iQ#?<9@p2y^WV~96YCp}{jwlG z4rM69^><+Ldo}&Uw!_x$g@c-YW4>v>9O^rCNcf}q1`Yd(<^vUvmE5bryk4@8jTPo+ zauO>%*PoAv`GWibWL04|`{Q~g_?578y$(h9NBRC9f468S{H^qV)$_9JJm3E=1?5}% ze`M{R7xiz*|1LZ~jPW4<#n{CB=Jnk!bNz02NciI{Ny~!G{0p4>+`ASeUyJ~|G)ARx%-lJb^V+5(|n-`%BU7!^ZF=16H}62k7m1>Ne><- zdj8(ao`tkt57d8N#qNgsQQYkXjOF=VVW)T~H{boeVA}r{Y#p7r`7s2v|Ec~5PaLH4 zkDvd>{ZIA3fB&@;Vtg?6bfl!DdV8Mfey4Y`77v;afAb5b^{VO9dOUdkLb%*p#LukX z%zV6JbBXa3?~qM3@Nn!JOzQq=y+=LaFNpqho?D96ux6)MoyILd_aEusC>`v+B!4ST z^)NT*_rsHixG!5&(p_g^Z-*k@<%=)4=B zNK_xo==DRJm(Q*d|s%fxXr@4bw(DM5%Ktt^Uwp+2KArfV-dHnu@99m5j7 zyWfvp;MT8k-hUQH;8vuza%CtILi)v@>sO}SE9U@$v8w8A+g|?hSw)S|{Fww!ug_28 z$H!Iu8ob?SB#m#Epk2&Q*OcdEDs?jGUU{>A1jY~ZKJn~CEI2|4^nWM>!h4Aw({z41HbM!H6^J^}&u>$i}PICcA(-yjbWXA)8D zf7j)DHT;}*&D7OYYr(|7u~#m#+JF70coTgUSj0;Ij#tIm!&n9m{B~!tt?5zZPu|2; z;I?6t(!CENfAgO?4oKyfkv-~1G<#KO$0Lg&PsNmjIGB%4U9eXs{F!M zltI~MS(~l#lzj0nnhCSZ#s;I!KgTt8b@FW6Y+1H%0*!kpyTMpA8p88*EyZ)k)Ujjr POKU8o=ANl3^iS