From a0accf2adc76bb1fd0f1a1e91b4c3afa2d14e07b Mon Sep 17 00:00:00 2001 From: iTracerFacer <134304944+iTracerFacer@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:54:58 -0600 Subject: [PATCH] Updated with performance tweaks. --- .../Moose_DualCoalitionZoneCapture.lua | 317 +++++++++++------- Moose_TADC/Moose_TADC_CargoDispatcher.lua | 29 +- Moose_TADC/Moose_TADC_Load2nd.lua | 249 +++++++++++--- Moose_TADC/TADC_Example.miz | Bin 971641 -> 972924 bytes 4 files changed, 425 insertions(+), 170 deletions(-) diff --git a/Moose_DualCoalitionZoneCapture/Moose_DualCoalitionZoneCapture.lua b/Moose_DualCoalitionZoneCapture/Moose_DualCoalitionZoneCapture.lua index e70944a..f2e70cf 100644 --- a/Moose_DualCoalitionZoneCapture/Moose_DualCoalitionZoneCapture.lua +++ b/Moose_DualCoalitionZoneCapture/Moose_DualCoalitionZoneCapture.lua @@ -1,6 +1,21 @@ --- Setup Capture Missions & Zones -- Refactored version with configurable zone ownership +-- ========================================== +-- MESSAGE AND TIMING CONFIGURATION +-- ========================================== +local MESSAGE_CONFIG = { + STATUS_BROADCAST_FREQUENCY = 3602, -- Zone status broadcast cadence (seconds) + STATUS_BROADCAST_START_DELAY = 10, -- Delay before first broadcast (seconds) + COLOR_VERIFICATION_FREQUENCY = 240, -- Zone color verification cadence (seconds) + COLOR_VERIFICATION_START_DELAY = 60, -- Delay before first color check (seconds) + TACTICAL_UPDATE_FREQUENCY = 180, -- Tactical marker update cadence (seconds) + TACTICAL_UPDATE_START_DELAY = 30, -- Delay before first tactical update (seconds) + STATUS_MESSAGE_DURATION = 15, -- How long general status messages stay onscreen + VICTORY_MESSAGE_DURATION = 300, -- How long victory/defeat alerts stay onscreen + CAPTURE_MESSAGE_DURATION = 15, -- Duration for capture/guard/empty notices + ATTACK_MESSAGE_DURATION = 15 -- Duration for attack alerts +} + -- ========================================== -- ZONE COLOR CONFIGURATION (Centralized) -- ========================================== @@ -164,7 +179,7 @@ end -- Logging configuration: toggle logging behavior for this module -- Set `CAPTURE_ZONE_LOGGING.enabled = false` to silence module logs if not CAPTURE_ZONE_LOGGING then - CAPTURE_ZONE_LOGGING = { enabled = true, prefix = "[CAPTURE Module]" } + CAPTURE_ZONE_LOGGING = { enabled = false, prefix = "[CAPTURE Module]" } end local function log(message, detailed) @@ -254,43 +269,63 @@ end local totalZones = InitializeZones() --- Helper functions for tactical information - -- Global cached unit set - created once and maintained automatically by MOOSE local CachedUnitSet = nil +-- Utility guard to safely test whether a unit is inside a zone without throwing +local function IsUnitInZone(unit, zone) + if not unit or not zone then + return false + end + + local ok, point = pcall(function() + return unit:GetPointVec3() + end) + + if not ok or not point then + return false + end + + local inZone = false + pcall(function() + inZone = zone:IsPointVec3InZone(point) + end) + + return inZone +end + -- Initialize the cached unit set once local function InitializeCachedUnitSet() if not CachedUnitSet then CachedUnitSet = SET_UNIT:New() :FilterCategories({"ground", "plane", "helicopter"}) -- Only scan relevant unit types - :FilterOnce() -- Don't filter continuously, we'll use the live set + :FilterStart() -- Keep the set updated by MOOSE without recreating it log("[PERFORMANCE] Initialized cached unit set for zone scanning") end end local function GetZoneForceStrengths(ZoneCapture) - if not ZoneCapture then - return {red = 0, blue = 0, neutral = 0} + if not ZoneCapture then + return { red = 0, blue = 0, neutral = 0 } end - local success, zone = pcall(function() return ZoneCapture:GetZone() end) - if not success or not zone then - return {red = 0, blue = 0, neutral = 0} + local success, zone = pcall(function() + return ZoneCapture:GetZone() + end) + + if not success or not zone then + return { red = 0, blue = 0, neutral = 0 } end - + local redCount = 0 - local blueCount = 0 + local blueCount = 0 local neutralCount = 0 - - -- Get all units in the zone using MOOSE's zone scanning - local unitsInZone = SET_UNIT:New() - :FilterZones({zone}) - :FilterOnce() - - if unitsInZone then - unitsInZone:ForEachUnit(function(unit) - if unit and unit:IsAlive() then + + InitializeCachedUnitSet() + + if CachedUnitSet then + CachedUnitSet:ForEachUnit(function(unit) + if unit and unit:IsAlive() and IsUnitInZone(unit, zone) then local unitCoalition = unit:GetCoalition() if unitCoalition == coalition.side.RED then redCount = redCount + 1 @@ -302,10 +337,10 @@ local function GetZoneForceStrengths(ZoneCapture) end end) end - - log(string.format("[TACTICAL] Zone %s scan result: R:%d B:%d N:%d", + + log(string.format("[TACTICAL] Zone %s scan result: R:%d B:%d N:%d", ZoneCapture:GetZoneName(), redCount, blueCount, neutralCount)) - + return { red = redCount, blue = blueCount, @@ -314,63 +349,62 @@ local function GetZoneForceStrengths(ZoneCapture) end local function GetEnemyUnitMGRSCoords(ZoneCapture, enemyCoalition) - local zone = ZoneCapture:GetZone() - if not zone then return {} end - + if not ZoneCapture or not enemyCoalition then + return {} + end + + local success, zone = pcall(function() + return ZoneCapture:GetZone() + end) + + if not success or not zone then + return {} + end + + InitializeCachedUnitSet() + local coords = {} - - -- Get all units in the zone using MOOSE's zone scanning - local unitsInZone = SET_UNIT:New() - :FilterZones({zone}) - :FilterOnce() - local totalUnits = 0 local enemyUnits = 0 local unitsWithCoords = 0 - - if unitsInZone then - unitsInZone:ForEachUnit(function(unit) - totalUnits = totalUnits + 1 - if unit and unit:IsAlive() then + + if CachedUnitSet then + CachedUnitSet:ForEachUnit(function(unit) + if unit and unit:IsAlive() and IsUnitInZone(unit, zone) then + totalUnits = totalUnits + 1 local unitCoalition = unit:GetCoalition() - - -- Process units of the specified enemy coalition + if unitCoalition == enemyCoalition then enemyUnits = enemyUnits + 1 local coord = unit:GetCoordinate() - + if coord then - -- Try multiple methods to get coordinates local mgrs = nil local success_mgrs = false - - -- Method 1: Try ToStringMGRS + success_mgrs, mgrs = pcall(function() return coord:ToStringMGRS(5) end) - - -- Method 2: Try ToStringMGRS without precision parameter + if not success_mgrs or not mgrs then success_mgrs, mgrs = pcall(function() return coord:ToStringMGRS() end) end - - -- Method 3: Try ToMGRS + if not success_mgrs or not mgrs then success_mgrs, mgrs = pcall(function() return coord:ToMGRS() end) end - - -- Method 4: Fallback to Lat/Long + if not success_mgrs or not mgrs then success_mgrs, mgrs = pcall(function() local lat, lon = coord:GetLLDDM() return string.format("N%s E%s", lat, lon) end) end - + if success_mgrs and mgrs then unitsWithCoords = unitsWithCoords + 1 local unitType = unit:GetTypeName() or "Unknown" @@ -389,13 +423,13 @@ local function GetEnemyUnitMGRSCoords(ZoneCapture, enemyCoalition) end end) end - - log(string.format("[TACTICAL DEBUG] %s - Total units scanned: %d, Enemy units: %d, units with MGRS: %d", + + log(string.format("[TACTICAL DEBUG] %s - Total units scanned: %d, Enemy units: %d, units with MGRS: %d", ZoneCapture:GetZoneName(), totalUnits, enemyUnits, unitsWithCoords)) - - log(string.format("[TACTICAL] Found %d enemy units with coordinates in %s", + + log(string.format("[TACTICAL] Found %d enemy units with coordinates in %s", #coords, ZoneCapture:GetZoneName())) - + return coords end @@ -463,19 +497,39 @@ local function CreateTacticalInfoMarker(ZoneCapture) if coord then local offsetCoord = coord:Translate(200, 45) -- 200m NE + local function removeMarker(markerID) + if not markerID then + return + end + + local removed = pcall(function() + offsetCoord:RemoveMark(markerID) + end) + + if not removed then + removed = pcall(function() + trigger.action.removeMark(markerID) + end) + end + + if not removed then + pcall(function() + coord:RemoveMark(markerID) + end) + end + end + -- Remove legacy single marker if present if ZoneCapture.TacticalMarkerID then log(string.format("[TACTICAL] Removing old marker ID %d for %s", ZoneCapture.TacticalMarkerID, zoneName)) - pcall(function() offsetCoord:RemoveMark(ZoneCapture.TacticalMarkerID) end) - pcall(function() trigger.action.removeMark(ZoneCapture.TacticalMarkerID) end) - pcall(function() coord:RemoveMark(ZoneCapture.TacticalMarkerID) end) + removeMarker(ZoneCapture.TacticalMarkerID) ZoneCapture.TacticalMarkerID = nil end -- BLUE Coalition Marker if ZoneCapture.TacticalMarkerID_BLUE then log(string.format("[TACTICAL] Removing old BLUE marker ID %d for %s", ZoneCapture.TacticalMarkerID_BLUE, zoneName)) - pcall(function() offsetCoord:RemoveMark(ZoneCapture.TacticalMarkerID_BLUE) end) + removeMarker(ZoneCapture.TacticalMarkerID_BLUE) ZoneCapture.TacticalMarkerID_BLUE = nil end local successBlue, markerIDBlue = pcall(function() @@ -492,7 +546,7 @@ local function CreateTacticalInfoMarker(ZoneCapture) -- RED Coalition Marker if ZoneCapture.TacticalMarkerID_RED then log(string.format("[TACTICAL] Removing old RED marker ID %d for %s", ZoneCapture.TacticalMarkerID_RED, zoneName)) - pcall(function() offsetCoord:RemoveMark(ZoneCapture.TacticalMarkerID_RED) end) + removeMarker(ZoneCapture.TacticalMarkerID_RED) ZoneCapture.TacticalMarkerID_RED = nil end local successRed, markerIDRed = pcall(function() @@ -516,16 +570,18 @@ local function OnEnterGuarded(ZoneCapture, From, Event, To) ZoneCapture:Smoke( SMOKECOLOR.Blue ) -- Update zone visual markers to BLUE ZoneCapture:UndrawZone() - ZoneCapture:DrawZone(-1, ZONE_COLORS.BLUE_CAPTURED, 0.5, ZONE_COLORS.BLUE_CAPTURED, 0.2, 2, true) - US_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) - RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) + local color = ZONE_COLORS.BLUE_CAPTURED + ZoneCapture:DrawZone(-1, {0, 0, 0}, 1, color, 0.2, 2, true) + US_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) + RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) else ZoneCapture:Smoke( SMOKECOLOR.Red ) -- Update zone visual markers to RED ZoneCapture:UndrawZone() - ZoneCapture:DrawZone(-1, ZONE_COLORS.RED_CAPTURED, 0.5, ZONE_COLORS.RED_CAPTURED, 0.2, 2, true) - RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) - US_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) + local color = ZONE_COLORS.RED_CAPTURED + ZoneCapture:DrawZone(-1, {0, 0, 0}, 1, color, 0.2, 2, true) + RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) + US_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) end -- Create/update tactical information marker CreateTacticalInfoMarker(ZoneCapture) @@ -536,9 +592,10 @@ local function OnEnterEmpty(ZoneCapture) ZoneCapture:Smoke( SMOKECOLOR.Green ) -- Update zone visual markers to GREEN (neutral) ZoneCapture:UndrawZone() - ZoneCapture:DrawZone(-1, ZONE_COLORS.EMPTY, 0.5, ZONE_COLORS.EMPTY, 0.2, 2, true) - US_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) - RU_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) + local color = ZONE_COLORS.EMPTY + ZoneCapture:DrawZone(-1, {0, 0, 0}, 1, color, 0.2, 2, true) + US_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) + RU_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) -- Create/update tactical information marker CreateTacticalInfoMarker(ZoneCapture) end @@ -551,14 +608,14 @@ local function OnEnterAttacked(ZoneCapture) local color if Coalition == coalition.side.BLUE then color = ZONE_COLORS.BLUE_ATTACKED - US_CC:MessageTypeToCoalition( string.format( "%s is under attack by Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) - RU_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) + US_CC:MessageTypeToCoalition( string.format( "%s is under attack by Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.ATTACK_MESSAGE_DURATION ) + RU_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.ATTACK_MESSAGE_DURATION ) else color = ZONE_COLORS.RED_ATTACKED - RU_CC:MessageTypeToCoalition( string.format( "%s is under attack by the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) - US_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) + RU_CC:MessageTypeToCoalition( string.format( "%s is under attack by the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.ATTACK_MESSAGE_DURATION ) + US_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.ATTACK_MESSAGE_DURATION ) end - ZoneCapture:DrawZone(-1, color, 0.5, color, 0.2, 2, true) + ZoneCapture:DrawZone(-1, {0, 0, 0}, 1, color, 0.2, 2, true) -- Create/update tactical information marker CreateTacticalInfoMarker(ZoneCapture) end @@ -591,13 +648,13 @@ local function CheckVictoryCondition() "VICTORY! All capture zones have been secured by coalition forces!\n\n" .. "Operation Polar Shield is complete. Outstanding work!\n" .. "Mission will end in 60 seconds.", - MESSAGE.Type.Information, 30 + MESSAGE.Type.Information, MESSAGE_CONFIG.VICTORY_MESSAGE_DURATION ) RU_CC:MessageTypeToCoalition( "DEFEAT! All strategic positions have been lost to coalition forces.\n\n" .. "Operation Polar Shield has failed. Mission ending in 60 seconds.", - MESSAGE.Type.Information, 30 + MESSAGE.Type.Information, MESSAGE_CONFIG.VICTORY_MESSAGE_DURATION ) -- Add victory celebration effects @@ -632,13 +689,13 @@ local function CheckVictoryCondition() "VICTORY! All strategic positions secured for the Motherland!\n\n" .. "NATO forces have been repelled. Outstanding work!\n" .. "Mission will end in 60 seconds.", - MESSAGE.Type.Information, 30 + MESSAGE.Type.Information, MESSAGE_CONFIG.VICTORY_MESSAGE_DURATION ) US_CC:MessageTypeToCoalition( "DEFEAT! All capture zones have been lost to Russian forces.\n\n" .. "Operation Polar Shield has failed. Mission ending in 60 seconds.", - MESSAGE.Type.Information, 30 + MESSAGE.Type.Information, MESSAGE_CONFIG.VICTORY_MESSAGE_DURATION ) -- Add victory celebration effects @@ -673,15 +730,17 @@ local function OnEnterCaptured(ZoneCapture) if Coalition == coalition.side.BLUE then -- Update zone visual markers to BLUE for captured ZoneCapture:UndrawZone() - ZoneCapture:DrawZone(-1, ZONE_COLORS.BLUE_CAPTURED, 0.5, ZONE_COLORS.BLUE_CAPTURED, 0.2, 2, true) - RU_CC:MessageTypeToCoalition( string.format( "%s is captured by the USA, we lost it!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) - US_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) + local color = ZONE_COLORS.BLUE_CAPTURED + ZoneCapture:DrawZone(-1, {0, 0, 0}, 1, color, 0.2, 2, true) + RU_CC:MessageTypeToCoalition( string.format( "%s is captured by the USA, we lost it!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) + US_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) else -- Update zone visual markers to RED for captured ZoneCapture:UndrawZone() - ZoneCapture:DrawZone(-1, ZONE_COLORS.RED_CAPTURED, 0.5, ZONE_COLORS.RED_CAPTURED, 0.2, 2, true) - US_CC:MessageTypeToCoalition( string.format( "%s is captured by Russia, we lost it!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) - RU_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information ) + local color = ZONE_COLORS.RED_CAPTURED + ZoneCapture:DrawZone(-1, {0, 0, 0}, 1, color, 0.2, 2, true) + US_CC:MessageTypeToCoalition( string.format( "%s is captured by Russia, we lost it!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) + RU_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information, MESSAGE_CONFIG.CAPTURE_MESSAGE_DURATION ) end ZoneCapture:AddScore( "Captured", "Zone captured: Extra points granted.", ZONE_SETTINGS.captureScore ) @@ -712,17 +771,17 @@ for i, zoneCapture in ipairs(zoneCaptureObjects) do -- Get initial coalition color for this zone local initialCoalition = zoneCapture:GetCoalition() - local colorRGB = {0, 1, 0} -- Default green for neutral - + local colorRGB = ZONE_COLORS.EMPTY + if initialCoalition == coalition.side.RED then - colorRGB = {1, 0, 0} -- Red + colorRGB = ZONE_COLORS.RED_CAPTURED elseif initialCoalition == coalition.side.BLUE then - colorRGB = {0, 0, 1} -- Blue + colorRGB = ZONE_COLORS.BLUE_CAPTURED end - + -- Initialize zone borders with appropriate initial color local drawSuccess, drawError = pcall(function() - zone:DrawZone(-1, colorRGB, 0.5, colorRGB, 0.2, 2, true) + zone:DrawZone(-1, {0, 0, 0}, 1, colorRGB, 0.2, 2, true) end) if not drawSuccess then @@ -834,13 +893,14 @@ local function BroadcastZoneStatus() local fullMessage = reportMessage .. detailMessage -- Broadcast to BOTH coalitions with their specific victory progress - local blueProgressPercent = math.floor((status.blue / status.total) * 100) + local totalZones = math.max(status.total, 1) + local blueProgressPercent = math.floor((status.blue / totalZones) * 100) local blueFullMessage = fullMessage .. string.format("\n\nYour Progress to Victory: %d%%", blueProgressPercent) - US_CC:MessageTypeToCoalition( blueFullMessage, MESSAGE.Type.Information, 15 ) + US_CC:MessageTypeToCoalition( blueFullMessage, MESSAGE.Type.Information, MESSAGE_CONFIG.STATUS_MESSAGE_DURATION ) - local redProgressPercent = math.floor((status.red / status.total) * 100) + local redProgressPercent = math.floor((status.red / totalZones) * 100) local redFullMessage = fullMessage .. string.format("\n\nYour Progress to Victory: %d%%", redProgressPercent) - RU_CC:MessageTypeToCoalition( redFullMessage, MESSAGE.Type.Information, 15 ) + RU_CC:MessageTypeToCoalition( redFullMessage, MESSAGE.Type.Information, MESSAGE_CONFIG.STATUS_MESSAGE_DURATION ) log("[ZONE STATUS] " .. reportMessage:gsub("\n", " | ")) @@ -856,13 +916,13 @@ local ZoneMonitorScheduler = SCHEDULER:New( nil, function() US_CC:MessageTypeToCoalition( string.format("APPROACHING VICTORY! %d more zone(s) needed for complete success!", status.total - status.blue), - MESSAGE.Type.Information, 10 + MESSAGE.Type.Information, MESSAGE_CONFIG.VICTORY_MESSAGE_DURATION ) RU_CC:MessageTypeToCoalition( string.format("CRITICAL SITUATION! Coalition forces control %d/%d zones! We must recapture territory!", status.blue, status.total), - MESSAGE.Type.Information, 10 + MESSAGE.Type.Information, MESSAGE_CONFIG.VICTORY_MESSAGE_DURATION ) end @@ -871,17 +931,17 @@ local ZoneMonitorScheduler = SCHEDULER:New( nil, function() RU_CC:MessageTypeToCoalition( string.format("APPROACHING VICTORY! %d more zone(s) needed for complete success!", status.total - status.red), - MESSAGE.Type.Information, 10 + MESSAGE.Type.Information, MESSAGE_CONFIG.VICTORY_MESSAGE_DURATION ) US_CC:MessageTypeToCoalition( string.format("CRITICAL SITUATION! Russian forces control %d/%d zones! We must recapture territory!", status.red, status.total), - MESSAGE.Type.Information, 10 + MESSAGE.Type.Information, MESSAGE_CONFIG.VICTORY_MESSAGE_DURATION ) end -end, {}, 10, 300 ) -- Start after 10 seconds, repeat every 300 seconds (5 minutes) +end, {}, MESSAGE_CONFIG.STATUS_BROADCAST_START_DELAY, MESSAGE_CONFIG.STATUS_BROADCAST_FREQUENCY ) -- Periodic zone color verification system (every 2 minutes) local ZoneColorVerificationScheduler = SCHEDULER:New( nil, function() @@ -897,8 +957,8 @@ local ZoneColorVerificationScheduler = SCHEDULER:New( nil, function() local zoneColor = GetZoneColor(zoneCapture) -- Force redraw the zone with correct color based on CURRENT STATE - zoneCapture:UndrawZone() - zoneCapture:DrawZone(-1, zoneColor, 0.5, zoneColor, 0.2, 2, true) + zoneCapture:UndrawZone() + zoneCapture:DrawZone(-1, {0, 0, 0}, 1, zoneColor, 0.2, 2, true) -- Log the color assignment for debugging local colorName = "UNKNOWN" @@ -917,20 +977,35 @@ local ZoneColorVerificationScheduler = SCHEDULER:New( nil, function() end end -end, {}, 60, 120 ) -- Start after 60 seconds, repeat every 120 seconds (2 minutes) +end, {}, MESSAGE_CONFIG.COLOR_VERIFICATION_START_DELAY, MESSAGE_CONFIG.COLOR_VERIFICATION_FREQUENCY ) --- Periodic tactical marker update system (every 1 minute) +-- Periodic tactical marker update system with change detection +local __lastForceCountsByZone = {} local TacticalMarkerUpdateScheduler = SCHEDULER:New( nil, function() - log("[TACTICAL] Running periodic tactical marker update...") - - -- Update tactical markers for all zones + log("[TACTICAL] Running periodic tactical marker update (change-detected)...") + for i, zoneCapture in ipairs(zoneCaptureObjects) do if zoneCapture then - CreateTacticalInfoMarker(zoneCapture) + local zoneName = zoneCapture.GetZoneName and zoneCapture:GetZoneName() or (zoneNames[i] or ("Zone " .. i)) + local counts = GetZoneForceStrengths(zoneCapture) + local last = __lastForceCountsByZone[zoneName] + local changed = (not last) + or (last.red ~= counts.red) + or (last.blue ~= counts.blue) + or (last.neutral ~= counts.neutral) + + if changed then + __lastForceCountsByZone[zoneName] = { + red = counts.red, + blue = counts.blue, + neutral = counts.neutral + } + CreateTacticalInfoMarker(zoneCapture) + end end end - -end, {}, 30, 60 ) -- Start after 30 seconds, repeat every 60 seconds (1 minute) + +end, {}, MESSAGE_CONFIG.TACTICAL_UPDATE_START_DELAY, MESSAGE_CONFIG.TACTICAL_UPDATE_FREQUENCY ) -- Function to refresh all zone colors based on current ownership local function RefreshAllZoneColors() @@ -945,11 +1020,11 @@ local function RefreshAllZoneColors() -- Get color for current state/ownership local zoneColor = GetZoneColor(zoneCapture) - -- Clear existing drawings - zoneCapture:UndrawZone() + -- Clear existing drawings + zoneCapture:UndrawZone() - -- Redraw with correct color - zoneCapture:DrawZone(-1, zoneColor, 0.5, zoneColor, 0.2, 2, true) + -- Redraw with correct color + zoneCapture:DrawZone(-1, {0, 0, 0}, 1, zoneColor, 0.2, 2, true) -- Log the color assignment for debugging local colorName = "UNKNOWN" @@ -969,8 +1044,8 @@ local function RefreshAllZoneColors() end -- Notify BOTH coalitions - US_CC:MessageTypeToCoalition("Zone visual markers have been refreshed!", MESSAGE.Type.Information, 5) - RU_CC:MessageTypeToCoalition("Zone visual markers have been refreshed!", MESSAGE.Type.Information, 5) + US_CC:MessageTypeToCoalition("Zone visual markers have been refreshed!", MESSAGE.Type.Information, MESSAGE_CONFIG.STATUS_MESSAGE_DURATION) + RU_CC:MessageTypeToCoalition("Zone visual markers have been refreshed!", MESSAGE.Type.Information, MESSAGE_CONFIG.STATUS_MESSAGE_DURATION) end -- Manual zone status commands for players (F10 radio menu) - BOTH COALITIONS @@ -982,7 +1057,8 @@ local function SetupZoneStatusCommands() MENU_COALITION_COMMAND:New( coalition.side.BLUE, "Check Victory Progress", USMenu, function() local status = GetZoneOwnershipStatus() - local progressPercent = math.floor((status.blue / status.total) * 100) + local totalZones = math.max(status.total, 1) + local progressPercent = math.floor((status.blue / totalZones) * 100) US_CC:MessageTypeToCoalition( string.format( @@ -998,7 +1074,7 @@ local function SetupZoneStatusCommands() progressPercent >= 50 and "GOOD PROGRESS!" or "KEEP FIGHTING!" ), - MESSAGE.Type.Information, 10 + MESSAGE.Type.Information, MESSAGE_CONFIG.STATUS_MESSAGE_DURATION ) end ) @@ -1013,7 +1089,8 @@ local function SetupZoneStatusCommands() MENU_COALITION_COMMAND:New( coalition.side.RED, "Check Victory Progress", RUMenu, function() local status = GetZoneOwnershipStatus() - local progressPercent = math.floor((status.red / status.total) * 100) + local totalZones = math.max(status.total, 1) + local progressPercent = math.floor((status.red / totalZones) * 100) RU_CC:MessageTypeToCoalition( string.format( @@ -1029,7 +1106,7 @@ local function SetupZoneStatusCommands() progressPercent >= 50 and "GOOD PROGRESS!" or "KEEP FIGHTING!" ), - MESSAGE.Type.Information, 10 + MESSAGE.Type.Information, MESSAGE_CONFIG.STATUS_MESSAGE_DURATION ) end ) diff --git a/Moose_TADC/Moose_TADC_CargoDispatcher.lua b/Moose_TADC/Moose_TADC_CargoDispatcher.lua index e033542..908c018 100644 --- a/Moose_TADC/Moose_TADC_CargoDispatcher.lua +++ b/Moose_TADC/Moose_TADC_CargoDispatcher.lua @@ -5,7 +5,8 @@ ═══════════════════════════════════════════════════════════════════════════════ DESCRIPTION: - This script monitors RED and BLUE squadrons for low aircraft counts and automatically dispatches CARGO aircraft from a list of supply airfields to replenish them. It spawns cargo aircraft and routes them to destination airbases. Delivery detection and replenishment is handled by the main TADC system. + This script monitors RED and BLUE squadrons for low aircraft counts and automatically dispatches CARGO aircraft from a list of supply airfields to replenish them. + It spawns cargo aircraft and routes them to destination airbases. Delivery detection and replenishment is handled by the main TADC system. CONFIGURATION: - Update static templates and airfield lists as needed for your mission. @@ -19,6 +20,13 @@ REQUIRES: ═══════════════════════════════════════════════════════════════════════════════ ]] +-- Single-run guard to prevent duplicate dispatcher loops if script is reloaded +if _G.__TDAC_DISPATCHER_RUNNING then + env.info("[TDAC] CargoDispatcher already running; aborting duplicate load") + return +end +_G.__TDAC_DISPATCHER_RUNNING = true + --[[ GLOBAL STATE AND CONFIGURATION -------------------------------------------------------------------------- @@ -264,9 +272,9 @@ local function cleanupCargoMissions() for _, coalitionKey in ipairs({"red", "blue"}) do for i = #cargoMissions[coalitionKey], 1, -1 do local m = cargoMissions[coalitionKey][i] - if m.status == "failed" then + if m.status == "failed" or m.status == "completed" then if not (m.group and m.group:IsAlive()) then - log("Cleaning up failed cargo mission: " .. (m.group and m.group:GetName() or "nil group") .. " status: failed") + log("Cleaning up " .. m.status .. " cargo mission: " .. (m.group and m.group:GetName() or "nil group")) table.remove(cargoMissions[coalitionKey], i) end end @@ -444,12 +452,21 @@ local function dispatchCargo(squadron, coalitionKey) rat:SetDeparture(origin) rat:SetDestination(destination) rat:NoRespawn() - rat:InitUnControlled(false) -- ensure template-level 'Uncontrolled' flag does not leave transports parked + rat:InitUnControlled(false) -- force departing transports to spawn in a controllable state rat:InitLateActivated(false) rat:SetSpawnLimit(1) rat:SetSpawnDelay(1) - -- Ensure RAT takes off immediately from the runway (hot start) instead of staying parked - if rat.SetTakeoffHot then rat:SetTakeoffHot() end + + -- CRITICAL: Force takeoff from runway to prevent aircraft getting stuck at parking + -- SetTakeoffRunway() ensures aircraft spawn directly on runway and take off immediately + if rat.SetTakeoffRunway then + rat:SetTakeoffRunway() + log("DEBUG: Configured cargo to take off from runway at " .. origin, true) + else + log("WARNING: SetTakeoffRunway() not available - falling back to SetTakeoffHot()", true) + if rat.SetTakeoffHot then rat:SetTakeoffHot() end + end + -- Ensure RAT will look for parking and not despawn the group immediately on landing. -- This makes the group taxi to parking and come to a stop so other scripts (e.g. Load2nd) -- that detect parked/stopped cargo aircraft can register the delivery. diff --git a/Moose_TADC/Moose_TADC_Load2nd.lua b/Moose_TADC/Moose_TADC_Load2nd.lua index f67e6b4..749b23c 100644 --- a/Moose_TADC/Moose_TADC_Load2nd.lua +++ b/Moose_TADC/Moose_TADC_Load2nd.lua @@ -140,15 +140,15 @@ local TADC_SETTINGS = { -- Timing settings (applies to both coalitions) checkInterval = 30, -- How often to scan for threats (seconds) monitorInterval = 30, -- How often to check interceptor status (seconds) - statusReportInterval = 120, -- How often to report airbase status (seconds) - squadronSummaryInterval = 600, -- How often to broadcast squadron summary (seconds) + statusReportInterval = 1805, -- How often to report airbase status (seconds) + squadronSummaryInterval = 1800, -- How often to broadcast squadron summary (seconds) cargoCheckInterval = 15, -- How often to check for cargo deliveries (seconds) -- RED Coalition Settings red = { maxActiveCAP = 24, -- Maximum RED fighters airborne at once squadronCooldown = 600, -- RED cooldown after squadron launch (seconds) - interceptRatio = 0.8, -- RED interceptors per threat aircraft + interceptRatio = 1.2, -- RED interceptors per threat aircraft cargoReplenishmentAmount = 4, -- RED aircraft added per cargo delivery emergencyCleanupTime = 7200, -- RED force cleanup time (seconds) rtbFlightBuffer = 300, -- RED extra landing time before cleanup (seconds) @@ -158,7 +158,7 @@ local TADC_SETTINGS = { blue = { maxActiveCAP = 24, -- Maximum BLUE fighters airborne at once squadronCooldown = 600, -- BLUE cooldown after squadron launch (seconds) - interceptRatio = 0.8, -- BLUE interceptors per threat aircraft + interceptRatio = 1.2, -- BLUE interceptors per threat aircraft cargoReplenishmentAmount = 4, -- BLUE aircraft added per cargo delivery emergencyCleanupTime = 7200, -- BLUE force cleanup time (seconds) rtbFlightBuffer = 300, -- BLUE extra landing time before cleanup (seconds) @@ -276,6 +276,105 @@ local airbaseHealthStatus = { blue = {} } +local function coalitionKeyFromSide(side) + if side == coalition.side.RED then return "red" end + if side == coalition.side.BLUE then return "blue" end + return nil +end + +local function cleanupInterceptorEntry(interceptorName, coalitionKey) + if not interceptorName or not coalitionKey then return end + if activeInterceptors[coalitionKey] then + activeInterceptors[coalitionKey][interceptorName] = nil + end + if aircraftSpawnTracking[coalitionKey] then + aircraftSpawnTracking[coalitionKey][interceptorName] = nil + end +end + +local function destroyInterceptorGroup(interceptor, coalitionKey, delaySeconds) + if not interceptor then return end + + local name = nil + if interceptor.GetName then + local ok, value = pcall(function() return interceptor:GetName() end) + if ok then name = value end + end + + local resolvedKey = coalitionKey + if not resolvedKey and interceptor.GetCoalition then + local ok, side = pcall(function() return interceptor:GetCoalition() end) + if ok then + resolvedKey = coalitionKeyFromSide(side) + end + end + + local function doDestroy() + if interceptor and interceptor.IsAlive and interceptor:IsAlive() then + pcall(function() interceptor:Destroy() end) + end + if name and resolvedKey then + cleanupInterceptorEntry(name, resolvedKey) + end + end + + if delaySeconds and delaySeconds > 0 then + timer.scheduleFunction(function() + doDestroy() + return + end, {}, timer.getTime() + delaySeconds) + else + doDestroy() + end +end + +local function finalizeCargoMission(cargoGroup, squadron, coalitionKey) + if not cargoMissions or not coalitionKey or not squadron or not squadron.airbaseName then + return + end + + local coalitionBucket = cargoMissions[coalitionKey] + if type(coalitionBucket) ~= "table" then + return + end + + local groupName = nil + if cargoGroup and cargoGroup.GetName then + local ok, value = pcall(function() return cargoGroup:GetName() end) + if ok then groupName = value end + end + + for idx = #coalitionBucket, 1, -1 do + local mission = coalitionBucket[idx] + if mission and mission.destination == squadron.airbaseName then + local missionGroupName = nil + if mission.group and mission.group.GetName then + local ok, value = pcall(function() return mission.group:GetName() end) + if ok then missionGroupName = value end + end + + if not groupName or missionGroupName == groupName then + mission.status = "completed" + mission.completedAt = timer.getTime() + + if mission.group and mission.group.Destroy then + local targetGroup = mission.group + timer.scheduleFunction(function() + pcall(function() + if targetGroup and targetGroup.IsAlive and targetGroup:IsAlive() then + targetGroup:Destroy() + end + end) + return + end, {}, timer.getTime() + 90) + end + + table.remove(coalitionBucket, idx) + end + end + end +end + -- Logging function local function log(message, detailed) if not detailed or ADVANCED_SETTINGS.enableDetailedLogging then @@ -709,6 +808,8 @@ local function processCargoDelivery(cargoGroup, squadron, coalitionSide, coaliti MESSAGE:New(msg, 10):ToCoalition(coalitionSide) USERSOUND:New("Cargo_Delivered.ogg"):ToCoalition(coalitionSide) end + + finalizeCargoMission(cargoGroup, squadron, coalitionKey) end -- Event handler for cargo aircraft landing (backup for actual landings) @@ -1271,10 +1372,9 @@ local function monitorStuckAircraft() -- Mark airbase as having stuck aircraft airbaseHealthStatus[coalitionKey][trackingData.airbase] = "stuck-aircraft" - -- Remove the stuck aircraft - trackingData.group:Destroy() - activeInterceptors[coalitionKey][aircraftName] = nil - aircraftSpawnTracking[coalitionKey][aircraftName] = nil + -- Remove the stuck aircraft and clear tracking + pcall(function() trackingData.group:Destroy() end) + cleanupInterceptorEntry(aircraftName, coalitionKey) -- Reassign squadron to alternative airbase reassignSquadronToAlternativeAirbase(trackingData.squadron, coalitionKey) @@ -1342,9 +1442,14 @@ local function sendInterceptorHome(interceptor, coalitionSide) SCHEDULER:New(nil, function() local coalitionKey = (coalitionSide == coalition.side.RED) and "red" or "blue" - if activeInterceptors[coalitionKey][interceptor:GetName()] then - activeInterceptors[coalitionKey][interceptor:GetName()] = nil - log("Cleaned up " .. coalitionName .. " " .. interceptor:GetName() .. " after RTB", true) + local name = nil + if interceptor and interceptor.GetName then + local ok, value = pcall(function() return interceptor:GetName() end) + if ok then name = value end + end + if name and activeInterceptors[coalitionKey][name] then + destroyInterceptorGroup(interceptor, coalitionKey, 0) + log("Cleaned up " .. coalitionName .. " " .. name .. " after RTB", true) end end, {}, flightTime) else @@ -1616,6 +1721,7 @@ local function launchInterceptor(threatGroup, coalitionSide) log("ERROR: Failed to create SPAWN object for " .. coalitionName .. " " .. squadron.templateName) return end + spawn:InitCleanUp(900) local interceptors = {} @@ -1674,11 +1780,14 @@ local function launchInterceptor(threatGroup, coalitionSide) -- Emergency cleanup (safety net) SCHEDULER:New(nil, function() - if activeInterceptors[coalitionKey][interceptor:GetName()] then - log("Emergency cleanup of " .. coalitionName .. " " .. interceptor:GetName() .. " (should have RTB'd)") - activeInterceptors[coalitionKey][interceptor:GetName()] = nil - -- Also clean up spawn tracking - aircraftSpawnTracking[coalitionKey][interceptor:GetName()] = nil + local name = nil + if interceptor and interceptor.GetName then + local ok, value = pcall(function() return interceptor:GetName() end) + if ok then name = value end + end + if name and activeInterceptors[coalitionKey][name] then + log("Emergency cleanup of " .. coalitionName .. " " .. name .. " (should have RTB'd)") + destroyInterceptorGroup(interceptor, coalitionKey, 0) end end, {}, coalitionSettings.emergencyCleanupTime) end @@ -2182,20 +2291,29 @@ end initializeSystem() -- Add F10 menu command for squadron summary -local menuRoot = MENU_MISSION:New("TADC Utilities") +-- Use MenuManager to create coalition-specific menus (not mission-wide) +local menuRootBlue, menuRootRed -MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Squadron Resource Summary", menuRoot, function() +if MenuManager then + menuRootBlue = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "TADC Utilities") + menuRootRed = MenuManager.CreateCoalitionMenu(coalition.side.RED, "TADC Utilities") +else + menuRootBlue = MENU_COALITION:New(coalition.side.BLUE, "TADC Utilities") + menuRootRed = MENU_COALITION:New(coalition.side.RED, "TADC Utilities") +end + +MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Squadron Resource Summary", menuRootRed, function() local summary = getSquadronResourceSummary(coalition.side.RED) MESSAGE:New(summary, 20):ToCoalition(coalition.side.RED) end) -MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Squadron Resource Summary", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Squadron Resource Summary", menuRootBlue, function() local summary = getSquadronResourceSummary(coalition.side.BLUE) MESSAGE:New(summary, 20):ToCoalition(coalition.side.BLUE) end) -- 1. Show Airbase Status Report -MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Airbase Status Report", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Airbase Status Report", menuRootRed, function() local report = "=== RED Airbase Status ===\n" for _, squadron in pairs(RED_SQUADRON_CONFIG) do local usable, status = isAirbaseUsable(squadron.airbaseName, coalition.side.RED) @@ -2212,7 +2330,7 @@ MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Airbase Status Report", men MESSAGE:New(report, 20):ToCoalition(coalition.side.RED) end) -MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Airbase Status Report", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Airbase Status Report", menuRootBlue, function() local report = "=== BLUE Airbase Status ===\n" for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do local usable, status = isAirbaseUsable(squadron.airbaseName, coalition.side.BLUE) @@ -2230,7 +2348,7 @@ MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Airbase Status Report", me end) -- 2. Show Active Interceptors -MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Active Interceptors", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Active Interceptors", menuRootRed, function() local lines = {"Active RED Interceptors:"} for name, data in pairs(activeInterceptors.red) do if data and data.group and data.group:IsAlive() then @@ -2240,7 +2358,7 @@ MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Active Interceptors", menuR MESSAGE:New(table.concat(lines, "\n"), 20):ToCoalition(coalition.side.RED) end) -MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Active Interceptors", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Active Interceptors", menuRootBlue, function() local lines = {"Active BLUE Interceptors:"} for name, data in pairs(activeInterceptors.blue) do if data and data.group and data.group:IsAlive() then @@ -2251,7 +2369,7 @@ MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Active Interceptors", menu end) -- 3. Show Threat Summary -MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Threat Summary", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Threat Summary", menuRootRed, function() local lines = {"Detected BLUE Threats:"} if cachedSets.blueAircraft then cachedSets.blueAircraft:ForEach(function(group) @@ -2263,7 +2381,7 @@ MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Threat Summary", menuRoot, MESSAGE:New(table.concat(lines, "\n"), 20):ToCoalition(coalition.side.RED) end) -MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Threat Summary", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Threat Summary", menuRootBlue, function() local lines = {"Detected RED Threats:"} if cachedSets.redAircraft then cachedSets.redAircraft:ForEach(function(group) @@ -2276,18 +2394,18 @@ MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Threat Summary", menuRoot, end) -- 4. Request Immediate Squadron Summary Broadcast -MENU_COALITION_COMMAND:New(coalition.side.RED, "Broadcast Squadron Summary Now", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.RED, "Broadcast Squadron Summary Now", menuRootRed, function() local summary = getSquadronResourceSummary(coalition.side.RED) MESSAGE:New(summary, 20):ToCoalition(coalition.side.RED) end) -MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Broadcast Squadron Summary Now", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Broadcast Squadron Summary Now", menuRootBlue, function() local summary = getSquadronResourceSummary(coalition.side.BLUE) MESSAGE:New(summary, 20):ToCoalition(coalition.side.BLUE) end) -- 5. Show Cargo Delivery Log -MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Cargo Delivery Log", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Cargo Delivery Log", menuRootRed, function() local lines = {"Recent RED Cargo Deliveries:"} if _G.processedDeliveries then for key, timestamp in pairs(_G.processedDeliveries) do @@ -2299,7 +2417,7 @@ MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Cargo Delivery Log", menuRo MESSAGE:New(table.concat(lines, "\n"), 20):ToCoalition(coalition.side.RED) end) -MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Cargo Delivery Log", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Cargo Delivery Log", menuRootBlue, function() local lines = {"Recent BLUE Cargo Deliveries:"} if _G.processedDeliveries then for key, timestamp in pairs(_G.processedDeliveries) do @@ -2312,7 +2430,7 @@ MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Cargo Delivery Log", menuR end) -- 6. Show Zone Coverage Map -MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Zone Coverage Map", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Zone Coverage Map", menuRootRed, function() local lines = {"RED Zone Coverage:"} for _, squadron in pairs(RED_SQUADRON_CONFIG) do local zones = {} @@ -2324,7 +2442,7 @@ MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Zone Coverage Map", menuRoo MESSAGE:New(table.concat(lines, "\n"), 20):ToCoalition(coalition.side.RED) end) -MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Zone Coverage Map", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Zone Coverage Map", menuRootBlue, function() local lines = {"BLUE Zone Coverage:"} for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do local zones = {} @@ -2336,40 +2454,71 @@ MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Zone Coverage Map", menuRo MESSAGE:New(table.concat(lines, "\n"), 20):ToCoalition(coalition.side.BLUE) end) --- 7. Request Emergency Cleanup (admin/global) -MENU_MISSION_COMMAND:New("Emergency Cleanup Interceptors", menuRoot, function() +-- 7. Admin/Debug Commands - Create submenus under each coalition's TADC Utilities +local menuAdminBlue = MENU_COALITION:New(coalition.side.BLUE, "Admin / Debug", menuRootBlue) +local menuAdminRed = MENU_COALITION:New(coalition.side.RED, "Admin / Debug", menuRootRed) + +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Emergency Cleanup Interceptors", menuAdminBlue, function() local cleaned = 0 - for _, interceptors in pairs(activeInterceptors.red) do + for name, interceptors in pairs(activeInterceptors.red) do if interceptors and interceptors.group and not interceptors.group:IsAlive() then - interceptors.group = nil + cleanupInterceptorEntry(name, "red") cleaned = cleaned + 1 end end - for _, interceptors in pairs(activeInterceptors.blue) do + for name, interceptors in pairs(activeInterceptors.blue) do if interceptors and interceptors.group and not interceptors.group:IsAlive() then - interceptors.group = nil + cleanupInterceptorEntry(name, "blue") cleaned = cleaned + 1 end end - MESSAGE:New("Cleaned up " .. cleaned .. " dead interceptor groups.", 20):ToAll() + MESSAGE:New("Cleaned up " .. cleaned .. " dead interceptor groups.", 20):ToBlue() +end) + +MENU_COALITION_COMMAND:New(coalition.side.RED, "Emergency Cleanup Interceptors", menuAdminRed, function() + local cleaned = 0 + for name, interceptors in pairs(activeInterceptors.red) do + if interceptors and interceptors.group and not interceptors.group:IsAlive() then + cleanupInterceptorEntry(name, "red") + cleaned = cleaned + 1 + end + end + for name, interceptors in pairs(activeInterceptors.blue) do + if interceptors and interceptors.group and not interceptors.group:IsAlive() then + cleanupInterceptorEntry(name, "blue") + cleaned = cleaned + 1 + end + end + MESSAGE:New("Cleaned up " .. cleaned .. " dead interceptor groups.", 20):ToRed() end) -- 9. Show System Uptime/Status local systemStartTime = timer.getTime() -MENU_MISSION_COMMAND:New("Show TADC System Status", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show TADC System Status", menuAdminBlue, function() local uptime = math.floor((timer.getTime() - systemStartTime) / 60) local status = string.format("TADC System Uptime: %d minutes\nCheck Interval: %ds\nMonitor Interval: %ds\nStatus Report Interval: %ds\nSquadron Summary Interval: %ds\nCargo Check Interval: %ds", uptime, TADC_SETTINGS.checkInterval, TADC_SETTINGS.monitorInterval, TADC_SETTINGS.statusReportInterval, TADC_SETTINGS.squadronSummaryInterval, TADC_SETTINGS.cargoCheckInterval) - MESSAGE:New(status, 20):ToAll() + MESSAGE:New(status, 20):ToBlue() +end) + +MENU_COALITION_COMMAND:New(coalition.side.RED, "Show TADC System Status", menuAdminRed, function() + local uptime = math.floor((timer.getTime() - systemStartTime) / 60) + local status = string.format("TADC System Uptime: %d minutes\nCheck Interval: %ds\nMonitor Interval: %ds\nStatus Report Interval: %ds\nSquadron Summary Interval: %ds\nCargo Check Interval: %ds", uptime, TADC_SETTINGS.checkInterval, TADC_SETTINGS.monitorInterval, TADC_SETTINGS.statusReportInterval, TADC_SETTINGS.squadronSummaryInterval, TADC_SETTINGS.cargoCheckInterval) + MESSAGE:New(status, 20):ToRed() end) -- 10. Check for Stuck Aircraft (manual trigger) -MENU_MISSION_COMMAND:New("Check for Stuck Aircraft", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Check for Stuck Aircraft", menuAdminBlue, function() monitorStuckAircraft() - MESSAGE:New("Stuck aircraft check completed", 10):ToAll() + MESSAGE:New("Stuck aircraft check completed", 10):ToBlue() +end) + +MENU_COALITION_COMMAND:New(coalition.side.RED, "Check for Stuck Aircraft", menuAdminRed, function() + monitorStuckAircraft() + MESSAGE:New("Stuck aircraft check completed", 10):ToRed() end) -- 11. Show Airbase Health Status -MENU_MISSION_COMMAND:New("Show Airbase Health Status", menuRoot, function() +MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Airbase Health Status", menuAdminBlue, function() local lines = {"Airbase Health Status:"} for _, coalitionKey in ipairs({"red", "blue"}) do local coalitionName = (coalitionKey == "red") and "RED" or "BLUE" @@ -2378,7 +2527,19 @@ MENU_MISSION_COMMAND:New("Show Airbase Health Status", menuRoot, function() table.insert(lines, " " .. airbaseName .. ": " .. status) end end - MESSAGE:New(table.concat(lines, "\n"), 20):ToAll() + MESSAGE:New(table.concat(lines, "\n"), 20):ToBlue() +end) + +MENU_COALITION_COMMAND:New(coalition.side.RED, "Show Airbase Health Status", menuAdminRed, function() + local lines = {"Airbase Health Status:"} + for _, coalitionKey in ipairs({"red", "blue"}) do + local coalitionName = (coalitionKey == "red") and "RED" or "BLUE" + table.insert(lines, coalitionName .. " Coalition:") + for airbaseName, status in pairs(airbaseHealthStatus[coalitionKey]) do + table.insert(lines, " " .. airbaseName .. ": " .. status) + end + end + MESSAGE:New(table.concat(lines, "\n"), 20):ToRed() end) -- Initialize airbase health status for all configured airbases diff --git a/Moose_TADC/TADC_Example.miz b/Moose_TADC/TADC_Example.miz index 5790ba718a1d19faeda57dff3a7bab2c87a2d086..bd22a9f1c4d3339dc119c3df42108e0f1349b6a0 100644 GIT binary patch delta 31754 zcmV((K;XamraSzwJFv@F5oJtlTm2Z;ZpJH04tZlhyojv_g5ExBS&)o7ua{` zqjAtMA-P%`cJAcR0GHCr!fMG4N!`K_7Q%>Zl4A|0dDAnJbq62v2mz8HfAVyGl2lcH z&kQMPcb!~d!SG5m-PP6gUDego?Ot9!fA-6N{@ZWozvs_b<>##sl3&e^2g9pjoUg=j zBDZldU-P_|ma!T?C=0QFi3`49=VFz}BAH7zy_W^wu%*b^Y;Zhe(?6H-A{QB(@NLSo zM6NeHD}Hn2eg5qDWICLjy_=nlM@Nc$vvnewoaf25U>lJog~%nFoE)<_Td-H>7bi?= z;z^=sD(+aEk^FB6_FBr62V4U<^0=9`|v^Q>S1@(!?=qa-+hxOgtgf=e2X02Um?AOmDXhU2bd z$z_in^EA2TIUvUiK36Yst}hY{h_QyJDTvecJpyE#ILVY0Ns_7_(s4K*y*@j=m<&8h zcG<;t0fNY6fplPhFm{{91=sY^JEWb%Bgrz(7koiPy%%N9Hi?vgRL?-0@&XXZxm=5M zp@7t=m1K&(SJR7k@6La?Qg~SqDDLsR0i(O5ShILulyS;dK)tQ^jyOa+R|qk_K&c=^ za~=c!iUyODf4MlDoJ{TZ-;T%A6PO-v_?^gaScEgC?*{LGM;$geo4guKPdbbj^IqG` z7;ljcaeBnwo=s=8!h-WH$kp~9rNbhH>nSPzjr{lg*@q9f=`P4YlC4tS&C86f$~a%3 zgl}_xi{iQ{w`np*p|cu?Dk`=h8cU?3oR}Kbc%j zE=HrX(J3;2K2z-B*=;Y$mLh6h;@}UgVuD$mf;cSh8DN$Hj^DEQTI8U3D-SJPwx#)< z^PA%b1kXgFCcZU)^Bk4K=Y959AkzZ>{kg-g(~{G# z>}O3i5{TpKf-Fk1g6Fq!iUvV)c)E~nyR=7OMKf=C0Jq@FxJ(OXVMGW4At=MeWSXqj z1=yVwaD_!zIogyh&l5qy7|+QMkorpW4-(+;T)xP7frdb;rRr$lZtbdl_RV2O!G5Pe z_t}?!Uxz!N#!Ft@vt=5uj@TV+f-U1z@(x=FHX6_HuasQ_&k!wg7buNn(2}gX;Tw_P zTcg5?_&Np0pdh2M6(n}3Ibe`knJr)w1=0YmVR6Uf8@}k;>34BX;E71{Io}pEE>Gl5 zk7!@paBpyaK7N1o8vc1T7{0lhjjzr|Z%^KTUd>M4zB@;`?)O=iq#l1h28JSvM>nHF z$`vbD@xP%yNYtv!>Sz_RWX!-_$c}c!dbH>EP^fr3UxTQF>+$p|6%cAPc|Q~@V{jJk z5ByQ!2L=}sz}8gE*GYE(K0j}DSnIT0Kr9pEuha5oU2c-@tN6N<>o})}qd0}}H~8y+ zD=yPG!{6S<_cvnOeFL$ByovAQ76bJKvC6drCVxv_9z!dIh6zm+1ssw@pP3;~pS9E; zVdB+b^yN3Lj@H{|*p=ugAhTP;5*Xkg_WpRt_Ws~vXwuj0G;OW;l-)jP3@v$S08 zCbx=UKZf2o^J@6=t3x9AZs31c1b_bQXH@x6?Js6$=V!AYz;#}{n*KVPE_n(e^#qhy zYz$YA*t>+hC(G%%wE=z(phML-%fN-q`AiIrnjf)gQC^e&gY8}cHJu4yz(5&)uVCYP z{-nkmahI_a62iF)wL^#8gtBLNmw*l})if?LTRDdlfLbaB7s;cGPNE%G%jvT|Se zZ`pV3&|B5~s8EUdBQGJlTK5zwqnDi!O#6eIPiDUY2%L%u>K2}3a3pSE0E_ZHR85we zsxHoGg3&>GaHA@0V1EAA1t+YMQ)Qx_u`dV)gD&GI7H!DuwT7!6N8u*(+f z^+1^m>Bm?0kHB3c>5IqhN!@Z45M9;1*Ht!%&mtYRyxK z$sC)wD?4u66F1wW5AhYm|$~2kpu4c2rz0htR2XEG3aLlwIzo81q5DOs5Qk6v#sm$vw$S+EAO+n0zNL>G@ zWzu*ee8T?LX9whf4oK>aQ-F8QStyVYSWv>LO*UxLO^4lLR=EXtBNe!XsC&Fya&!JF zRfi@oZ$7ZT!}ZoP@0{|L#__iej?Mm}HvAolGJw!v|jj6fa${2(wZtdM;eD>9oG|E#(%E5 zBDH24g|J+Sr`dyEq4#ZZ2wMtya_$`rfO1uF?DI$Jmz@jK+3b&u_x5*%jwf&2!ql=P|%9PF5lKD9}- zjbu)-`q)uL$no`$dxX^Pq6v|@m|78~6^xwPb(*DrwcGWGwTbU(&NX*LV{t*T87q2k7i;3LhJ$5LX69F$U)U9{7AHyq6AF?8kbxieKO`DG)&qQlVRjM z_L+=z#P*KOX+O2~T$Gtu_e-=*2DJJwl_YL!TWG4x=mQJ`*2;t*5uNMZsKeHjm)oG7Xu}PVKsn~*Stl6UTqE6}_j7mUA#Z5&p10iUQ z-?W;<;ISJHt11x7n(mEh5f`P5Og}=0HH5X@&-SC>O$ceJs9feb&x#HZ`X_y0K}A}z zbmd>FrP-DV)4QM!lnOjeG*H|WkgZq=XNQ%lgP_2s3|+mP&qcn#BjEd9fYOnHG!&(O zMoRxiSoy^sD-KU2rkZUK?z?T7M&0uD;hoLr+$Ho7Y3F<+Zn-{vAYG&~puppOac46A zUZdiC92M#80+{%I)UiCWz3xzF}Yzf8Q!f3mbqL|(p&k%Whnh+{_q zUX##c6o0)|3*C-|s)6llY&>Ci{2<33t|U$A^drgiKy@6FEyP`YH6=qdNlC6L;n#ZbGp~5{vncc(-QkH9 z_=XjC3c2Rd=L=WZ6K!ryN6dK%$7zYx2lqDcB0s!*VG61 z$X-ds3h7Ipo0u#umVH;}*q^hR34gA&i#0~k{<$WdTk44#x;igZl@dS~$Wi1X)z@F< zBF}+jju}nnTEQD=aQ&t;H#KC(IYuyjPv-Z2Fu}V4M{FE!;2><)(rb~iJpd2G6%S~e zJy5uN6{-uYK=gV&6YM=qTBgaHEJK{pP`gGpYPJZ4_jB(_OI66K=2+G}`G54SH~NDz zgT%tj61h{6@A|%a`}zq2$uqE{a~}3*c$(olGa4E)5Jp3TM5UV7f5aG(+XD;Nck!5+ zmrXjVjbO4=V0L7K_zqIRoed=o2R_rAWV@wXH5w-y!Zx9ya~n%ql@!^e#=nOXa57NG zCNvX2HVKqoicT*mPhV9~Jq@~cDkf(^3nm)}tNlg4xtxo)#x{`EiDjeojZhiThwjACI~ zUq2a-%;6`P@hEtG1iq5zQ5zBfYLBM?^--g{FaPNYqn|REE=IIjH8}T4y0-%;@@3A= zH%ei_gy8FT{Mdo18gYLGZOP3+{5rnn&0-XSsPqOPkREF^5B7c_r^bMqt@l-l?I^0u z&D~Ja*M3DtaPg}`Tz?UU-rm#;dKxsWA08fd{s=#9OdTxYjG48^>N+EG>sB)Qrc6HM zomCsvA5qB*TmYn6NH8`Fu57UJ===wwJvB^NXd?jvSD`^dVZDly6^p(#cP>jzACj!Q zgj|%JmvQyh8Pn}o5G@TSgV!^Aee3EU{94KJ$*T(xEA55IP=C^{5I>bBvV#VK9pFB- z@iQYl${nys?Y;@Yc6juz-TjLrb1yKl+EAjqK569Zc=4McM}S&{MGRf zqruy=;eO4o)BTZ{Oiqp;fe2dpCm`Z+WtfVLhsv%qb9d(Q0@f|OYg^Qr&9~@BW1F7S z9TdFB1#umF&kxcx1fsyAjJy+I9hL?5s+RLPmvULAT7P<;KWjSW^RCAMsB){K@XqsR zwcuR+;=KvOAMY=3j0uXo_{e{&>tw8@&nsHqOQ6Ico3Amxx2GKvPhF^643z!OMeIlk z{T&Iqhz^<8HbL!mL<|7yOCajo0qdA7>Kd=QAV~1}AnkuqoEv9k(Mc|-y%#U~_8hR< zc@V}?VSfPb?di%gsY>Rn@DB20Plo*7-0y9Nj@^OY`wo%tCBhbL%v@c^8{kYLK}2)- zf=JP^;^z7Iu!7!a{)GA{pBCmy4HCRoCXSazTb zVlBq=T;%#XC5j7&bkYL0lQLn#r13;$M0c6{LQa#QYZZU{kl~t+_3!DU64-6)N~T^% zC$$U}LNy~VvBp9H*)=cFqNeKJfSK1UH+hrs^IyG_e+oo(!(Q4*^K zO)MiE52sFfGgU@WJjzrb;U?a;8`8uFOv)#i6dk}vaZP$eM%V;41x-3+yv{+|^H0=l zeM;%~9@RVjmkVC>+20P6858TKuX3uPQnm1# z0m=@c3V7GVKkf`8D3(5~;EmONHTmyPt($8t|F3jfA?|DSz7reEQZ?vfH2Yx&H|cW` zoXD87YAMQm&YRWum~P`3u2P2slG%~BtB$wb#fUp>8ia-HD*o{-Ya)MUSdt#`JL)gq zbAJhZJYC8NqL_|~S^Nu3wwQ`{khCx(`(``mKi<56A3sSG{*O84OSG^Gj9OmHBF|{r5nA71yg`%>)Y?6c~M?da|zh z!WJciX0(lJ11er*LwnpBQAc1K@m0r|3tAPC@=jxH%7b>n9@b**S2^eq z{y99M#>=>{3k0}R-hZBKk|KK9@a7o%LF^kx&&k>BY&bYS!b^YESW<~^xL7WgjnB*M zF246q3rseKfvzex7bS2zF3g#P1q|!X6s!qBiQ2joMLHmA`-=o~0}vsRnHA~cCCp}s zO3eu*OWawnx&o=9-kG=iDi&^lpNUzyvgxgG82H41t$MaQP~3)kvOV@`KqUBL1MAYK z>5!!6>vV3)_o9D@+I4eZqr>;Al_aZdou)S&;5>g8_N1UjkvB+%PzQ;csl*SRtq{vx zG1E5z_#u`cp9at~6^PuZZ?Bp!iN-%AxI-WOTx=+J19dLM7H{t>Z=_C3B#U@&)nn%( zUVLf&MYpROR@CQO2^?R%!1-ItNB1S#7{54x-~Z+6hRA$C=^F@NHjgIq9J%w2r)9lEWc691drmkj=L3)81RC`Bgbztg^hxk;jv2W>*ha8IFOk^Z3jmuhrb)H^>U9oR!Nd z;x9`jX#kB;g|s1x0>PR0cFD_fZ&#zDps>ck2{o^ufhgefI~X}=0OxLnu%ghs{P_ec zAF_X{{etLZafFDZ(@W-)2%i9RN49ZX!h~BD*<2P0M-ZNh7Y;cb8pRn2u5|Z7X^Uvw z?%TG+f^Ny;1Cc5ur(oNe?auJT92Buho}AzJo9A9TY`QnSdrgZYxhnWanX_lGgX1+# ziB~xQwTM$w4+se(0H^pG4AN3Rh4F${<+*>o{7^K}^lJfgEV}^_^+O?_%~zxx-3-_h za5)E0wU5Ky{;mA~Zn*o;rkbnk0-=J?R9*eDTxFSTYzM<^ck_Qd7f%H~xb+|0=?8gJ z6UO~;X-C@V$jhv;e3M5(sGz6og5AxR;r?fSk#Q^oFdvD^A$iXnFRXCI%)wx7zHp3LrQNDV|1|G@6x zL1O$wKn$Q|zeP|RkcP@vZEH3lcl6;_1=J6-t)0D#KK#gOh7PxOo?nQoXB84WDdB)N z+^Twg#Y(Pd|aTI25U$K9QzGEY-0%#?29B&tfcux+19glW7xZGZpB(Zw^ z#{HAJ-$3nR)-95{>^?+{k)D8 zi&RJ*k8!93KR1GfdH{q6po-i_s5ajR_^GR9q|8EHTkh7RqnV+?_j7r3IrD!nP~j-2 zwXGd-==(C5K(s~q8*{{1gWrO+9u*6`FYet_C1)Uhi<(v81c>a~i<(dIZ#7m^TRva2kXUP`I{H;g(oq$?)wOk|U=wM^tp+8NV( zxnK6An6)ZzAEcltr$oJQ>tX$v`XPa|uxJ#P$oJItKh%71JH12;1-Xs;H8}DDh0p7STXeSi8yWxtMUdDsXQ|x{Kvc&y@(rroGt)a+* zFbZgxz}Fzr&gp;O-|WA9JsxRH?~;2I6+YdIE^OLS88L!ACXP$lYkMOP+PQ>xllwJrxdS(=1a9m`u6x+0r-tN z<(3QMj{ScfT9+za5556PAjQN?oy3B*+sb2r7M+<$7FMbTN5`!pilY}~^9v1_%66%m zaO|uqpb?4ljTS~9*9~$dwUZ1@5iv+K5w$OjIljJ}c`a=l4ldu_8rt1arf%Wy zc^kiu+sf;9*Yi#GdNo!^FVuTcwr_aBCYk-E#! zP}>W3Q?Q;n`+#c>L#3tZF;} zAsD6vA-FOwyfe|)B$By6Ch@M3N)6zYWD4Z_E~c}cP`L%KFF63u$eV``Lxtc6S0zLt zOqYLi+_&+JZ}CKj3a}K?A6^z}t|5>mUA#-hCDf;7fs#xNIP%mOMfRFZ5-Q}B^2>k@ zs`eqL1Gv}?Q?*H!#m6QvZ1sHwoh6(Rk4p)f8guHlFYf99SG$VNNL@|Bl!?h#$h}o9 zitOVFUXKJTgflCJ+bm6Ctv7Q9X$!wQO{9N2(85R`LIQH_$=ol5ODta#0A@YJB`0qk z7buZd_(jSWy}YyVR&KjmA)zcv=mjv%V6 zLC^=km@g@W%|NX`**_YwICF749>1c$Y_IJ@^}W7PxeTtR^97F+p=1Lk_^RW+WAcA; zC%{9ePHMjsS({$Ef8>OI!d>x}LBwywLr~fBy#Sw-y{!JkCr*`r0FLRR__)H9Aag_+ zhj{E|xuQTmFxuKE>YD{!^v~#eQ{6!}x*Tk7)O#vMW(6x#=h1;oJKUc@s6wO`alV{Z zKNEI}rf&vajd^x>D|Z_&UoqIIs8oMh%L%L)2d0WI<&;N72n#V+D~7LdaSAG$7nG$} zQ!V=p`(kYC!Y&}-<~qSBMd%-S&0>VaOw{Ks^=}hi9_@cS8lO&%zJWaYr{m)T@!i4k ztAp2%lwVsf#tIY3nEZwY5*qeG0CCbOg0jQC=a}FUJt|ZhBqhqp830veWeYdfi}w~GwfiVg8nvzMKkZgx8$G8E$8Sb2no7Az z-5 zE0c;IN7jWwE%8d&N30 zKG-3)j)!V~*Ludb^BvvXxVJt~s{^!`H;Q4D33x5rzMFNlCkvj@EUD{zCl#R8mcP02 zz>T_LV}8I!T(j{$WTUOySW}{l&5suIW7{6I&`CD4ThfEJqAq{iqcS-|yF4yRwa@Om zb>;Na$RQP6L;k|e9^B-+iF3TGf2JnselD8s*mmWT_RZbe-utSOyqZz3dbT@~_H=e^ z%ePiO;gEuuzG=DF%9~piJ1J(JiYfQo9{;sn1>~y@S1b6%J#w?%E11FF#5KEH?M~|? zkFGG;dodgI^7?;bqRCz(^h`#NSJyyvzsj>OXqU68gu4-HVy z_zeq<&g9^9@|KM3z-r4UC*;jHe)DE?=NiT_%)6SJQ>cTgMkMmmt<^KhVXgS^^8O}D zL}Rx}KjX3R&=>blsSN`29RS{%*VRY}qE^9p#g+@<_~@FW@rX5JgZIdZU_{ooJBE!B ziLHVVZ4iHAv_<2<2JA_W9+o&;6u+H0P6Et9h}GF1Mkio!0F6#OZi&?5a4+gd@_Wa> z>Fi+_^Q_|&mimh|KGd@=?j#_nkD-i9vabBFk7)o=G||@%5HatPXSt-9<)8Tl)c~Wx zCqwXH=QVggP0IR&ZbArmPPwHr+5q;C&L-39!`XkaY{U(j^FP6YaO+bE6pLBt=@;pJ zbQIfo9H!fM!QiCuGf(L(x)z`WH7DX6Z=&#;bPtqLk-K85JI$I$lexC-gv1reCC0<= z)0j$*1SYK{zU8@m4fq+WCdtl_k&ns1PMcM@QMK-_iEOsnEiP|+Eh0t#?|#t2{oub) zO9O-6e23nA0k__K0@4c(nL}(_(~Zl{lvDr!!vB}PhypBsa^gsq?~d?4WUvn_dI4YR z(pAKGA}GLB#Ms~g+!Nik6%~n;fW|_im69<%-4pu>^D;lMpSEAJ=bXGKC1h9ibnU}N zHBo^<(m8qVw@j+te*gaX=zsqCkFV$7@ezCSbvqt?H=NFTS8U!J^y{7;y}7y_GT*39vSce#o`vy$iiL3?wgTSbEZTS2rQlhf;)+H? zKcR*eq#>RP&XOhIgD19tCtQSpanHQoWZGmK9`lvh0Hzk>QIzZuzx*-hL5gc;2~%JJ z3&Q16q_iYcsE?1pqOvtCkg+K7IawIezD>g<#Wf;vFCy7tHzHjMKO-*vWV4OL1Bgq+ z93GT^u)>ddnBu}nB#%gBB_S=un%@IX!k!r$1|ekY4r-%6nGV_Ius6S*4rg63$N&0o zW%*WoOy=8dl4fidW@}ADCZfG7OC#YVRv3Cuh_fP0twrx94L3a9pUi|0GQ&@Ek!B%$ zXlk6w2twZoE&wuoTvJgN#z0jRu822Bux6@mzW z*CYy(T`Wm>AXPu*%Zz0y_wPXFT3Gv-Z-6MDr>mrRC>0>4IFxI`EQv`405M}3uJV*G zB2f^^NPJS!KvI)i;fKr6Hv$uElYk|KB9#i!4CGa2HBEA~@)i^Es<2IpPw?nN5$Er4Y^Az~+rBWT3;0@3J@+F`85I)?Mx z8?D|xv9D+b_e2(~fr$WpiFwz5lgVtzwn?;K!7?r8L)?f2d=Q!6F#!)iR#uP09?UO{?Ndg) zm&VsH6Y3JiL$=xy#OSYo$uD$rRDNf}(N6F?$MdG8$iX(4`nD0d2}JTDGHC;GBZDHT z2oPv*(&T32kjF>U;my@>Jes|`9**ZlkaQBZ2&%cUNZdO4XKS9BJfp=1f|e?tX(%kP zm(d=fSH^rtsIe~Ity^OsuQ*OmuGDVDSkXg+%+d6|Y#E=7A|I+C0$ zf`o(=R=jm;_!F{*LzQ2^BEwC-q4ekDBB)f2+?b`f7buG$2fh2`OKIW@DM=_xawCac zD`5oSQ9$=zN3z3zOk1vN_(nFbcQH#+UB*>a+Gb%!s@FCr$z6_*_DPN_V!0fzsz9d1 zHNt?Jg58JQ=(J-3JQfmIaS2A~pin$MD(qIXA(fsWGGUs3w~B<23K0fsaMrWS^4P3M z^-hHAEejR6i_@nJw4r^=6Jk!lblvNJqAQUZ6nC6GG$>M1~NoZj>eGV`@@?# zoA#gwD_nL~YPqWE0HUd%N2Cy^Mpd!W=3ebzSYB2uTgXhERj0*HJFnOUd*zm`Di)oc zR-#}z(Yv94%iF46q07!Xr>!C?bpiX!mJN+~`?;xW6gx8fi87`P18aR=1nL(lhMkSR zv=Y>eq8?SvWW-P@Xkh6`M>1Q?P)xN^6m7)D00nFn&hL2`>3WkMH2sFDyqA#56VxpT z3tiI>$)Zq;RBy1+cs8Hj_OX@5-tDXdA)E(H6&K5Y6x@23q<0v(jZ-ZIu!$v_?YfVT z&O5dz5k;-mHD(Ss)|rYcite}539Jon2UZgS~pGlz|H$ zXMeu!4W^UvN2qKsM{iNFDgZopL8-Yf!Qz$2fnRjkMAyC$2mDHLR;H48!D^(PEZdb@ zJg1O@zJ*~lPVJoLp_t(O|Bjh^9 z%(eNcC=M-(FusPt1Y2_3PU^r&xt;qyq!r)zo6(T3=6FTmanK$ZiO#G>m#QXIePxua zu4+vGll z^RRWl(*2e@#v)|8P20MIHn%l=lcW&VPrv{|rI`}6P8zhR?Pj-$R1r&CRmxRYQZnb> z2rKp8h$sSOEWmG)g%s&MsiHgwg5Ds1#@+@x4e(y*>D20S9WJTZ0J>6|r|_t3c2`Nf zYMY{`Ab_Biv!#OSg1u8aRfio$FI7FL)LP`FV(Nvqay5DT7D5V?vIFq_@n}Ao!q;wf zFmy0?Ls7c(r-n&Cc5H1=M>H%}2OQVjGP5jt`gO9O^d8 zq_vB;+IP*ovPc|o) z5oUkA&C$N*5Yy5V0yMqqr>X;g56kphu&Lr&vzW-(s8G@^w8hXTs2J?VUlh(lupIgX zbi_2p$@o)IJX!pes{X@7@7e6>6tYXb9bkKUv@uJ!&MIoSQi?&P$V|hb+xff6)P`9q z4VTLUi9^R(Ct%a1-irtb2$U;;K$1=PlNz*KYi0%lM?^ql$*1@v!e7=U|-3!(AX87MvTzFSeF6C> z7cHYI4UQcJj*xgHX)YdrXgt_R;i4{Ok77y+_*E}&km)>EkFJW0RxXtzx+5dKMQ<|GdRDhn5fm0o>fjGOA2(+Dy&Jf5{=8yc*Loa@NcGe} zlH`&UaxoUoP`niA4DWqYgU;4IJiq}aBCJkRHM*iw>#IU-j&=0~`}SO$35hNN z+H56lw1G?em<*;vmyK=CzHd)Gb7>65Y@1aFU6`=jK zbf{jCwlAFB3xnoWn{+(2$HJ5?djt5gO@f>$aqID0i>82~pGO%ud@Qoch|2)KZj%hhf~R=8Y@?rDM4EN+u;#f_ zGfDP;V;=f_X?RGTgw< zbxR2!>eF0Ev&mw&rNfQ3HEEi!-}~}|m)YR?di!$B`vRLRQ)TLb9`u2!C`PKLSMRXv z1l%{5SV8WR_}|s0x9WuGoKA-5I>zQyhi=4N-=+5-JE>9}3b8l!U|mSHw`5PZRnMh= z;6ai+)#rcdFoJU~#bVP(93gEtb#YC#;f+}rsBF-Altzm+?1>cof@f0bZ(D!Bb1CX? z8$KM=^yZ{f=@=79O>d0rs<(9;8tb!IQba#{QvT=@t-hrV5xy=UCk7 z3qt7rGq5gpM&~eK5$-F-XyY@FcX*y~xs-IY)TPH4STG#2HN;K6slp5mQ^A=f<~tE3 z6h{~EVaIms&|epZ6H3b`u+%_CQDffS=44%!q6jRm3-+SA4xSR0` zjn`?*JZldV6LZ`=O^?EuI?MC_M1(CKce10NZ$f8;AEIm3of%p+T*dzQ0sQ$q62uUGkNX zmdKkldDx$%eAl)+i-2g6NLr77XoLPKqmdlV-NYha(ZP_t4QHBh4M!NOeCd|!_tbgt zQm2?$e=-=>Kk5x8Y&@B>>&akr`8|6xyqf%l&EJh?>}oWo&8&a^C+esg zE*XAf7Z=5&9lX-P?kB@D)Tzoz4XSrU9DE9p+M*I9(&&Kd%W)VTAJN4_s?}`G@tYwO zynD|Xm7%y=CH4xAaA&>pC?18y%Pv5PNSCoguYK>reIURhoPO$m@%I&4KClbLJY%f9 zZk;JVz4WIcKD~Oq4)m{=9Jbz~_qaMJ%fE#MB;U_U$N8uW9gMLjWK!5sGEkP}l__wj zJ&?vo_OKPHUhnGbUbBYy1F5)xmINfUj($xPDO{a)DnxO()?@K0rl91&enM2?vg9+g za_Pb!*=c2YI`P$!{u-+GNL(6r3%<*g)=Hn*AY3A^MHxwdEwB<9=0gzJf7H3Ph@`U@ zb+E`2VPzRYUJHK`G*HmTbv)|BD7?4{`0?QGJHWCwwCigIS@nCzO}b?PyMj5eYP-f8 z$jSn@u}Y+rBd;YHe@Wz3K%4CE&{<`a$$t$6MF~@ml7$G#ii)qlIc659J||?l^gSLo zv7QS9Y%c$Qts<8eJA-Qo{PEQfib_!$(C&=H=+h!GkVTC^$)L5MDSB)bnYmm!>Fg zm>(7ejmMw1&-BpiDnY7BD*HTr=&1Dwoy_Va!zovPrkv}djH4dxR|aYKRn__9Pcp@% zOB}N3d@tou=lYX*wC?4?Rzm$_FEXGfZax9-#=jcK$I{g6(;A#LcFc$&h`XIsY?Awf z$lSunR6$kZrX0}p3eH5%DDNQmMajz3BeIqq+m|z&9p)$L;Y7YuKiw!8+zkoFX*zucn66 z>15ii{WVr7RY-_erpi!15bCgNeuw`>Ln_#Rb&^Q&Q5}M`!xcOmEkEKf)6QhpiE@sp z6l~4!(K-!79T<^ZwPL+6mfP(k6;Mk-etaj*s zqK|S?r=R~@R?z=`6ECH53+&!krSS(cxcf)Pn3&Xx`Z`O!~7>Njq;@HeZO~u|6|bLLqjoF3#>;ghtPxJ zY(AZQ{{%QXsyiU8AedZU;@G|lUVKu2+;6dMrBiqX*X#t@ZnAH12d}iUdHN3*c$BHa z?{q|mb*d|t!OmY)z^*2LsX~~0lRq^a<8XC`KOU2M*2!!V5AkSO=F!a-YqT%kf9k&^ zV;?Ts{~0%$Phhc@Xe^3plj?o~@aMva@%3{t(VkjC|1Wz_*W0$S?7IQ~hrKF)ib$>O z*lu%iU_=2N+f9u&c3?RzdUJz-5ow#p66r%qvE75-KXD)K%lUQZm)xD%?=Om!s3dK~ zhr}XxXJ=+-XJ=<;XK5K``m!R`@V1P{`ssU#@Z>l*MnHv^SM|eU+EF~hAtq=YETH-* z0y{pakY~iBN`mFth|y}W7kOcS4xvz0T$|Ka4vX)}?TdxXWpX{L#tNi&vAjp^s?C}6p1^W2C3H~trs|f&* zb1BfkSSFz-(~gXSyH3V`w-7kwT6D6OIG5qI1Q(hfxpbP`Eufb=v~XuxMe7Cs9(mY- z;#vbvC0GoT&g;{kPS1Wl?ReSwBAME!b5XZT#C+T|7iz7u(qmV{oaRKdT0{^1uv zJUxbpS1`kF!85}`z)C#r%fP8loiIzFMRJ(#h>@W4x~_^8xV>Xi_dM%Nl5 zTb z9jnzvx>`JDmm8S=8WmuU!0CzEIL6)#3IcN4PsSEM%^5^rjtM_LCN8HrZwq#AV}FY% zbEqZZgY;+W?FJXydRzY$>>4TAc20+p%_{FO=@>3B64n%d<~$H5mk}I20c7k<4elgx z#7YAZd>$~(HSMS)PbedEEDkUonflHS%$X(NgoP?0GL;!1zSjy+llE1eV|Ql3wuPgP zZQD*dwr$(CoxHJa+qP|V(6N(_ZS&;Z`|0dGp0RhW8l!%|syXMv1ubV{Y_H>Eka*AP(GyLtIh&L$K`FkUtvyowf{qK==Yo*uZtzFrz} zQd&BqBVh!5KgoqsWI#5h0~+pN=)tF_A6~13IO3s>%_}=o;I;EnP(r|v5@j*%V#}L9pv3H_q4-b5yY1?R+-=POd0WTAK?ZYM; zfXlTOBi9FE2%x2>*x_4*oF-oCr7nPX<5)2n=y^!JF-?9|qs1t0S&~b27E7jAw~8&_ zf|r4|l}QrER#VpBd6(Mtf+$ppVJOX>2C+0-5wV5y%PA(&MjEY?e;?_x7SbBSE4Va& z&x1d(b7Rq)VAwhGgCw81dOGE#=ukAg02ScuS*p!7T5Gc)=n@#Qpm5^W^vqaEdTh&Y z=wL|V6l&Ppx5k7gLHX|SlpCk#CaXzImwXeLlt+?Y+vkhhd=z%y4)226T_?E5`eZ}L zb9HFfuV_|b7)%wd(doUkV(>FF+)H4|gZ)tPgncVygXM_C!syIUj@322YR%wGfFp}j zv)y{!DMKZ530k6p7_xngz$|t_;3{=mZ5L#)F0ayg7f?LJLm2XW7 z4grRtf(7#kAMT5?tROw|jO$;LyjijX7sC4G;}YN8(r}W2omkeGCETNYbfgQ}TUM5D z*;KgzDfmZM(UtUGrmVc)JaedRz<3h&;z@KoK#j4x6k_ql^LKIonwgm+GFCzVvS~aa z*%Sxx%I~B1HXpYfy>el{f7uPnpL&s+r3K#^=pU2&LZDNCF>376CziIkzsMGSc@0H3 z=?JuRzcO?5mY8usc`&v`Nv+DX@P)uogDDa-K&j-u-iJ|GPkR3`@5ebn0&I&M*md+g zfRL3GAsx2flX%D-(9a8C>SOH@Y*|&ECwl zrhIK5KiUWt!cP^~iwd7K4&m6a<-8#=K2AF@GI;_ zTWRv2XBEJfu<+c*S4wGS0FGaDf^oG~x&&D9Rj`n~oGHV4tsXU@-H=$Yo{CdykgSEU z#gWh=HHG3|OU~{13Ve-lCfTlCxzF;J{6W`?pLfwb6^U1!g$6;U@Q}_S9ZiqL$0#9E z35=&Hz6S~yeu^wzJ6eV7zq*M#L%_I9xCu~~X3A2--F9ok%}#X@0nL*b@)Na$O1<8p zoDA#v@_{T=>sxDq=n`z2(e=9!-`ZR6G-x>LZf<9m{9uQ93zzSmnMf}djnG6P=#PSD zgVI;8beM=j#AoZjEhuU!MZVe(HPqdtFE zLadU?LtJbU!?>e5zz zYF?f-yyu6mH<}D}ecgwwU8v#LtM+!`k4*2jWEF$?=J(9XGT4ly;$s+NVGdKJgqr;H zEOn8t$vfEP;SpNUzlS#!BioVgt^uC}+J{D*a9TiA36BCxPzEHNVQ@WxH$ae#U2Qhn zxeNX^h<`Q#o!|d{JXY}_5?+Brhx*~}H}s<$<-s`bU!Qf~k4BcQ_J);9mq}aD-PJE@ zbXZVbd0U;>p@#&iRy$B4|Jpi@i4#cl%=LPi!dvf434&HJ!oB2F?s5I}E*9KxFW3SW z;$>iRa2p7}Kx)-g)YHbObY?mHR#QQt7ALweHgHt{$OIMd6*98ulq`&K^jH&}WYjxS zQl_|8D$v>Xi4ZBBWHaW7JA*f)-G>}&kubMNg*HL@89I?s<0yj+#~~LAuMq8WBpKUe z5jJQ_c{`;I#d(kFe$L3xR3{rr?ZmgLris4?>_~LiCK3`bq_aHa_Wq&Lf1V00XrO4D zH{31&vU6rH@7xi|zFDT)=3g|$!$^nyN4BjU1l#(AeTf8LKahEA+G6KVsHbklkhx%= z|4|O)xN-fOM}9=&r1GA#US-#zTQ$dJzo3#|07d=FGHI-y zy>-Bv-iB5Kou1|D2RqY;656*sw=g#4W3R6?=w~v~Gv~1Y!G* zR9foJP+Kw_2;y=!f08`KsEx^;LPvy*Eh0yi*bVfrBmJ0ekB^mA90{ylDw-8dL`39Z z{wAl>mIrt;INXevMd^u+#4PJ@6@V*Z%O)|+CTJCPIBc`m`(>nx(v5J-r-}Qu?#Jr` z_?GV?P^$STpnwtX2|*DRo0zDqVH^#hGDJ;7#&o$|V0D79o@6Wk&JmeK`4i;jk}t zXG#$aJ1~HC&rz*!vt16v@??VkCPlvnuuYE4-TRw}!=S1KMDV~ygvVT`+9Tnc#72K7$AKe3nt!rUsW~ZBQR;E)0mX`TX{hrX}gJ(LB;9y z(1J3|x{DyiT8e@`7g9ZA*h3V$qZEazm{kcQTPgyrt;^+K^XNq?))eK%9*W5Wz_ce! z6=^RHm#~Q@Bxe_Wb@uninrKp75~OzRjdPY(Vi0i0RFq@)^fv4c-nWyF*X@;gyZp`~ zcBp#5N*TXW>b-O_v)P29OGfz;s|>Gb=G;;w0wuc-SJMUg)HSoL+`ROmKtGu0O_ex! z!9llK>2mdYK8S`M$;6-O$eAcgKsuu;36Rv1<{-*5E;D0}91B<%P;DO$nTm_Ckqh)V z1k2c1sl^(WsO$@b{%%P^3@Xk5MgU83X_N$GVoAPx#`vTp?oC+`STAdRdu0lo>Ns{P zJakqBM{G*{B5Nus%~IWBRjtGlIt?+s0Q4k!MPJr23RABE#OKi5ikWF0KqZ1=xGb4& zzvaFs6Qu~--?cDf2_ka93R{n_K7@r(`>F&0bQO6HN6lnmJ~Z_V^QTVV{sB^-3JIW9vzUVzp1O;F(O3b= zyc>{hp&ByW(bA`;^{-V&dK{v8H0K>%HomCi8l9KUYJ8?Uqy1OHj;tOPAm{ikVN??3gHyq~eHNc-xe1>mCI>8N}mJDUMG+N`-VK9i_iY z;=DF744*__&n_Jr@aJ%1@MCV!@A7j$QG}q-1r{FNt&L*|@OYo%93O8*+}#u<6X%{H z(}JdV>KwwR(dDHGTXZZuJ+|5`qC*1H#5je^Lp@KUby-vdt1UAYyfcb45dP3+)_l+^ zR6~_eW|7zNenpJIa*F(OA>6wMtQ_dJQhCe1DSxIN&|zbwLs#n8bRWWg!D0?&Iq)!CUY7qpP7`~ z=^7H8OIzEBc{6N>a1BR1XRR4% z$2$G7n4P^z12m-NZgFqrjnc~B=wh1jb5;ihhRgV`1kV zgh-ztD7Z!{y<^9+RJ*&R_4Yu>R1cJRo!`jHh0xZQR$v$9QJk7lQsag zuZ1c9Ymwe&NavGBXU#R^{g-EpE}h>SXICAZ48OK~YrtWdo7Fd>!)>B;Hv;b_d3HsF zrkC&ZahH4)zs8JwgAc`)qhA^-2{nbx>0rt*U?0JwbM`tAdFt{0q!vLyQFBqRu5NV5 zi*;v6YEW;v3YvlD_R%DVwR3h~3KKNmT_VgxU-uiHKX&l&H zKIB+wj+XCA|5R~js`GasGf4%K4R?)O3GxJl-VvZk%3<&}%%jk`vh1CfA?8)M$6UMe@9UH>KwpyGa*g*ypXk>{V z)|zw46cbKX+>L)O9QX))l5FEm+eEb1@huIjOk6j}tK7jE4l!$8kl7D+M_0U!HF$Cc9z_J?O zo4)tjH`il2#7A?s^H<|Ohow4{fd_|tWL%>D3Z9WNcG{I> zhn&zhN^I?NTH9jQms8KgwYnB*)}3|(zp5{nTedn|wO8EE*X>ki*X?RHYMOkO?6*z-i|!FGdYMy;V^zKOk6P zGMx#kj1vLMSR%eGoR)pOCy)N2Z6PECt>z%4gf@w6e=Gt*LRNTW4O55yMnT{dr>$~@ zE#J?|egSuJfV}PL^^!>p#YyN=o<7>V=tm>wB~(6?TkP>K;Ne1s~+uTz-rou z?!9OI&b{UE*lMe9X%nllyf^zVkv#&GoGQrFs$2Kboqs;u+s11o-!|NT6ExWbf z?+9Zrl^t+;Nta)|GwSoLU{50j=*NDmN&stI;JZ}0<;^QGUY($(P?=zQ5V?cpNy*#X z36!IF%Ne-vI%0mliXH;T3P?J(+!wARA-g<7zTk?}__Rb+soZ!ocpx)drdTs6m}Yyg zSohK9+7~(bw~HHLYoo+gJ+N`91Ww$rHr$DYtp_+5VGd{A6Bl5Xi$za?X>a9@QK!eG z5EpDfOv`WE-UVvw&1Qhfi+MIjZjYU`w$n_Z{yjn~{mmrZs`N&u>)%M7AWrrfOSOFz zpN8ez!p8h)o^hMncv6wP2Pp}DA8sApa%Sj*de#p6$_ zr{;wOxB5alY;~TU?xRfp2>ey_HvhpIY$!oh|H*| z_*^0o$-F1zlEWT!ZowJ*L?PY=6*M^(UM$`z{8q&QM~oQ{`?@=A#@_BkP!oSNZ4o;^ zQ&G1=pHC^zK^6lIYQjE=*oX=h?n)GXR@#oG<&is2fl- z>aE^!KR$%~w0g4cg)M9DudU@IqQ9SJmtVkl>$ni?n=?1LA`G0d@}=ngE_Qw1W)EW9 zGlIXOVY8-{h2us^S{RFLyvN8m09MMg>9#4?#7tGHE06Fbym`H}3=C}>d8MAq#^|*m zf>3vf8$=T@6XD2rD|vnty7o)csSMzGzh$<)TRgGr>i({$;sk*w% zU(A;~4-Os=$8JF8rfXWim<+n)g=H*Sgn2qtdeN9U9v9Ko<--$$Qw!5SWtbz}=B2PL2`m(0KG3)Q)rO3a` z;fJwCW8rV1K(a}b+sM|GQYf07BGLSpnNoOeV)lON)(?uk zKlNC#M?5Y^dnyxrF*=so9DPt=!}|7GKZbF-wwgFo>VWYDb}Q$)aSZ~vkz)%ji;r9s z%^=-@^lpet(SJ9fO~ATX_$PfCWvx|yt*oP29GMv1fr6Q)n-zgEk$?)vw#u}71Q7W+ z1a#P!17(};L!=7m+=Z{nraxWz3QcQ)Wei8hNs;$kA?>j|vZd>-m-FMS7G9QZU@_O- zCBpjPHWTNvu?uOBLB|3XFeC_lph)(?U3-S9jCR9J$l?@Y)M@_>>RYh?QzMEKH_icz zf?<=5IbBD3qg~)CjpzCR-^gR2Eyz~*@G;*`|_)i|u%iY88W+=p0ysBSA7G@B_ z^hZIvpy`@);}ZXYYm#=`@1}^=><9rG?b4M^l^6Hz9F~G+<+cs5>^ZArOc5E0$xbOk z7DFqya#pJdW!`DNyG4?vu`!d*b=FQyy$AD%A=w$ugD@gx6n0Rl3|QNM>J9KsPF^4e zl%sV*+L||QBt^@a6&<9?4UVlfdz_pVu8gm#f3lw7lsCTk%F{9U4`fND37k}Xag9l^ z){+NXmT(Si!vn5(X z$X-Q7Z)&)t)>poN_PDON?4JjkAwlyL-l^pZH41H?=8gcPQsY&JLb&Ah+$D?11E2hr z)CTPwFyV=*%Py;NoVw^1DW7t9NPI<;5m}`z>Vm>awiTA`C>0DBMe%9B@E~qq22^aL z!D^@+_msR3+2?hJ8=^x9;S=}o@0*<8E3s+{oiG2qjyS8&_cSwqaK_{sXZY~CHhlfE4v3DB3TV3ZDD zZD>SLQri!D=cdAUXM~i)bPgbc>dG);%O>T-r#OC%0vUpXu|6+!=4BeHl=UP@xLG6J z>F6vd{0!pL*oLEb{WGWDR$avlSa!ng+L>?9_Tzw=CwK0hZ#Xi(Sw7NVOZ1Y=zmUw~ zp%7fEiZtEfk}CYym$c^UH#X}_ql9RGO|VeguUf@pXD&{T%$3Pfxqs3#j48-VYorGMhZ_YOgt@YMYVR zWJ&>u+1g%c@CnjwU`2&N_&h%L6?fm}*P2-y?Fk>X)SnQvbvooLs3a6=i#an+DYO%@ zl_rwXl9IZORM+9SQcXmhn^^Oa+0tLbzU>Up=T~XCowwlFzlXw_JSAFMm?ZQ60%6Da z3BP^ZxVz>VcEuZsEcl~vXE{OD*)xn*B<=yQ`dmd{ra^Hr4PGsx5cw#qZR>mj!Q;p#Te9=f zo6eFrmk34*hz{|^bWn7k_T_b>&PZzgV{S8=bfU=h2qja#^F!ZV)_mFUVS0h3S*Kwj zFMKUrB@8!P3nUpLk~4$Fnd8Sm&8q+jZk>oU+3-iF3Jk%x%>gea1CFNug1d93r|!uJ z+J3<~o@$xRHY&2*ZIjh0*arP3dT?DvYTN7w|N!N_+KQl3#wJ`7X%@QMWH0YZV;AU{49vs$^ka~M7iuO5f6YcG- za{SZwJicm#o%x8DU%<;3^QZ<;PQiTP{F?%ky-z`7UjtteVonRJc>$7GV1oj_2rXwh z`0^q`1Oh<{0Qd#@Y0cbOW;_K9`yyLcCbK_x)|U2&gZ*W#osM%129sI!Z8D|O7#Xvu#h9dz@elQ zD(})8-GFo^{(4=tu(bawxw74>U#>Sj(auBhVN)0~@mV5Gf@l90C_p|~h;WOl5JS%f zl31rZpCJTS&u5CJL7P3~^pV(576DxqzOKqUH!doNLd=N&=U`x~o~BNF5}DnCtbS3& zm{W?vzm9*!;HBZbP{RzUJ;jS%K};TmsLN8YP?^6Q;LF zI52RVFAW!7Bf~cMwM00E+tU=@SlAu~GEmRPhhWfXPT;LLAm=5jh5zO9NXG`=T~*DC9bt0PYgNQ;7UaUHc<__SZuV zYv43E@R4143L;&mWkyPI9S<#1txRrEIg>_9{dVb6&!IXcB)g6l-|Uuu`;egC1%zRK zR^f;xJ}qL{-_dCpq=1}v(r^`)zM7FD)Gjo^@v5si>@P62G9r7czi2M{BWi$_%2t{} zUmMbtoeYx9(HKeqXu`aNd`W~`r{!?b$`i9l#SD$PvL+{k<~wcJ`J0rVuF)nxIsL7? zFq@7+_`%`Gz>2wr)+J*-mmBVQWz!jjjlf0hPOmZ1RyguFWC+}-&WWnO=s#E_>f@K} zG(LJ<1UYrea++V4f*e%E^ur2bj@%)GzG-N!SBoPAaq~_91(V;|n3W{zlo0^qM`twN z5C>PjfcJ^kW+(%j#M1luKchd^ijcwGOt%*kG%kIGWH*-iAR6%67;~7#(fawbmlYln&WD*Bmt2 z{!yFSx}E`mImrGzRwS@*p=f>167UYMUo~Uz07jYNpFp1_oW~$rcO(z2o>q2e&6?v9 za^9szio=ptIa=Tf`T+8|?yd`QFZadan%}Fo&2jMhR$_!R#}avlC!%r9u%3C(va6S6 zV$vP!87(%%-i0-B1`pA#gkfpy37?y$D0~SX+pTYa!=&Tyr`Iwe&m1{oinL{$%$b!O z1}{s2`SNTR&=}%=OI9D+Q;|aCaT;LehB;^l1Cpo|wtA%l{|@xEEQn`nbsWRP^j~9) ziAJdy&OpbJ5SLvS=!t}n7#2onE+N7XM+3NM3EsNn;2YZX@|5?G;&U@=#Zz`%#r=h2 zN7>8(oVjO@!uEcsG>rm|f5$3SS($2tEGD|%0x8Td?u}Ez4^r1R`fNpDFCwn4`UJ^DFs_hcw3>)I+In6E?9_du( z1Z+Cca&sM}CVL|831d*;4P>#4tlhc@&9n?q4;oFo*5v-49R~NPbSZ>n{J9gAVuCXU z2wAvFRqm^C&slfZsYM*1m1`OiZ@3F2&X;dNt?-^Wr^<{km-B+v*kT;p4;i=ipedRs zF@4Y82rjzUcTdY4vY&ja=tJ^uu=8b(e8$2~DD>FIv2EDHv-m>Ukuvp@RuEX3pZ|Gyy>n}~F$sbcRgIbsT#N*B zP6EkWS&%(fJ2&8gw?shZ?WA72ppU85*caaGQltj+C0-^*R~~SGF32uJ*;qRVyk&%l z*6N!VYcn7Yb!aS(Z>!b#h&QENovM{F_foe$g-v+W3+!L$`pu5yo(4(dke`gKCra}D z5!2*qmMy)@J#ydD$ip;sg|sWyJ7RlvZntDp!6|BD!7KELAYLbZ1%~#(d@glW{mK|D zUAV;%xIKJV85TxJ)MTEcZ?ahg5L!j?7Ag2h{>j>>A;iPi@z{`)YXl#Z+mB}^KN5mP zc5D@u0rK%cxdb0=>P}x5wJO0$TBNH?AGrQUn`4$_gO5u9w=^aFwTizSDV!v`@vm+R z5#zX;r8Jin?~w3wO;2k&Kr3eyhlpd@gelkjw>m1~E5U@1gLtrXN+WzQz|lOR>f#Ko zP?@LeiZzh}mfQbU30QYYycTYuc95_WIecMzf+fp%`2Df^^SZ06Q8~IHF~JZkoJrSe ze`t|Sdv`sJ(tx_$HayOe#2y&byE7&rNIqxw;+!<7aXksw1dEBHmljL^0^uaWg_|~k zVvvmlr>G#w*uz^}phn0K0F0MCM`t^JOgZ*28#ily`rxW&fmSbBR#itxpJ4x!E03xy zGX|p6Ive|aEEYyzhW8Fw3kN~fmqk}9!sZU%FYds4PRf&8u}qX#Z@E%K&iYz+278`V zU0C_(dto4an>|j~AdmV(df%xkQx95v0_+{$7Rq03@u>nEp#9Md(B3jkMV%tJ_u%CklzmQ9_scvh+Fh z7ea;#*O{iMba?lwow8*?5IO4`^ht`7oHHyr?#sv)l_)BJwNA-O0tQ9b3KLZ^x_hxC zwG84Mhq^n(h0TT5oYJj6uSb{F2G{(!cv74`SRrANN;5Y~a=`5}k%Xxm0on&CJPjVo_z4D)v&l-=oWcCRojGUG$OMSJW;auQzKH zSi$0U24diVr>9hHW?Xh@Pb<7&u$C_c*=5I&Jn3d^?8A;08LuaS(PdhBY1+NAB~v$> zI4@?{#LVN%<)rO<#VYAcXkisC-KJIK?6#Ljr2XY5I%AnOjLr4T;_RrLfPHnS{w|%0 z7;rd4HXQzRlMowb$uKO6*xR7;kyqb|k@reFEx7TQ=BX03$Lf4;x37V_DQ_;+LLq z3;x<2#`O5`0o#@#C&F*8)W0J-C*#j?$1UiBG(jfN6ut8v6mCTqk6w0!yHDtv# zQq@R+r{p;ebF0^>Ll*aZhfP@IgKY&+T>Wtgm;-;VX7N~%nqe=`ERZCnXUQohf9P?a ztSNg{cYRvotc#|p{VI3+x8oHIVpoG}S8!A8$8^(;5kdCdi(NJbd=9P;6Itc0d#7B~ zP8OD2__$G867G=1Ej^p?<0XvmYyb8AvZHhWDB~T?SM*-mxi`}eNlHdsiklDy!n^V= zue;=dwl$ErsdJM(t7>29a5lD6syqI#wcg;Zoq-dhmI$!X&b++>LBZ0dHyDy2SnOtu z6%6GfipBAg5;SyeLBw!Akvmfg> z4N;KIWTYyvcM~?*naRHAAdQFFwJ#k2!D_IlyR#z~-=0}fO&3McSZ+A79XuIP+BI3e zzTc=yYE%7cXiHT=MZ)~+`FU^KW#GuoNDPwe;jq6{Q`PWbCOJe|eknGta;{p4V6bN~ zD;3n1p-36aJFSM*Z`}%6pL3y_KAv+-x||a3VRL4zo3=s-#UZhkUDBkc+z_-p1uM{58VeXBvmEd|7Ml8_NuO2 zS0fu0Y^&&`iTSb1= zf+Ns}u70`GEKB|Kvg`Mn1$w=L@Wu%KxWK-?;OEseFqgHCUP@tee!BajXFvQ?t|e=< zv^7|uGX0xeO(!RywB>ROU}%D{u_v>VTM%GE0wT1aR2ef>ISO;26659OmDWQ4J2Mg^o3{r3g+ZEg5VC z8GAy=BwVj%uBo&j$tHFaV15*x*AZhslM){UMx5yCeMV14HA)E?Bf;#%o1{uuMVd#w zUS0960n8TME_65sm{tdvL?c!GSr0otE)zf2&%0E7#O21JWGH5dzSTgmZji+~CjvBX z9`9ux*_*yQgCY+S=)mLF*`k(HZ)40-qX=R#U2n9K;IKgzT0$^Uus?ZE;`Oer4$@K_ zf;rO)7G`Yf*~=aAyi@0(##e10>@3R*6Crv z@Ctvi_ldf~svxTq+?$(Hnap{J#{BX4hF5K!_ZSbQ3U?X-RR@9aWE#dj&`jtJOU{zy zns{YkkFo*q==>3C%Lj7i!^ejr)U*m)?e-qh^qC>cVQ#?&v)anfke{`p4drbQY0dL( z=(2v*4O+XQ+UrW6Nai(ne=WQzh@c?yZHKIGvcHMAD?#6L6!$Fb9p0r1fhaY++#y%tyfi6TaJHx zggsmKUau-yol#{fv8C1-7`X;j3 zm$*Ti(9ss=>DSlmVJOB0!XzVmcO=e!x>zWn2Ng}>li4iSrxGA1>P>-r?*)D5Jh4GQ zmv|d%$i8a{TF~yFtV`o7eSbCtV!c`bEEQZIIAH=Ita2+_&P|_%aJPoLGdsEai|V$0 zfi!35ka`3@YVjT=BYN!Bm+H5Q>@0GW0W8+0-nCxNb6o8EjS96*%-i_!6jieX%+W&P76n2RPY@~JwtZ?Gf!%BLjxim}Hg2*{JqSEjKz-fv9<{&c z6{|i-Z{|Il%yJih{lzS3ySa`#0 zK*Wf9madAmzcnQru);v>msf>r;T1RFqHngBlb_qq-O=wY&wi%Ci?|xDRG&-24J)8l z5^U-HaafKJn2ENDJjEuG}XFwGeukXxU=mkF=30 z5`vBb_ADL}C*D|HXk2G|_MsE4qu)si@MusV8gaOUb7pOYrxh%C!3TMS&u%>cTmlm} zjt&9*%Rih`(*B0dz?>UNO+6vanRB;Yw=v8-Uw?UeSGd-RiS|_MMo@ec`i;Okd@imE ztJ-Wt)M872X0pn#8^YDWggFT3rF?o@)mrKwp&jwoW^;A=nED-^xUW(PhvC1XTyLIc zZLl*0Ct|9sd@$gMdv;Hv+z1hX?J!ngiD&}GVcuY4uUoo!_)P@5jnD7x&Ob#Fm5mX` zEsGpRnp+WCgIo1WM3vSNh!q7%kCQA@DScKIvt~Up3wqf}r~(&-7o+Q}L`GU0*Bo}? zhl9OVOaXX`<^6qWqoOKKFw4nzf0Xv`zg^396T2{Jv|DY6=gFO-z^lvvps)QeffGT{ zY&ShzqR`2*NQrx%O*Ye!kWO&IWkx0Q9}OT^u*`y$D3OsB85j}O=HviYQEcscmbYrYA8IK)lZ zsnij;H?+Nq{W_J4+cYBp$i8~fVZ-^NexRNu0~Ta7iQaB59#peAwepIjpp5$tDT=kA zLRVx%|CoRE~43jm=I&Y#j>>AOCekA#1cq&eYl?V7OlHaFHfnOn7 z_aF?wiszzPW(+R4Yb4zng4f<5z4L{n5iH!!Df>YU0WBJeX3+J$au--Ilc?N+hesuP zYB98jJ~o=p;PD~@7G-mgS(Q)uoOt+BmD5#YCi>iAs+NU1Vgx#D!ADleKKYe{spgw< z0CC>G(NflrI?9h$kMau8H(<^O{-!mQr>&AZwF2GU|IDv*aF8wQLAgc2#FN})1!8d* zA>6fRCFBq$C|3D~c`5Mwur4{j#!-ST{acYGKnB*=h{Y5D@Bn0sg>8fSZ9#nj$%r|W z^1AKWO!DcOd#!BYp_G_*R>DNfw1w$ck3Qvw;9cl{%02vy3tIXZTvw^<=-H8T25Ht? zm+Q7;A&Yl_kHQg9KOe39LK|8YvrE>KO%>LlSBV>SFT9rtFRxM%h)E6@mUk126F*h23Q z0Mma%7!FO8_(Bhd1E5^9MS-s}VEw^xaCtb~wtn$KPSaih!2|T5RZv{1kRdM>@?U!q zvtx5M1dhC$Y(@kUpFu;PBqeXUrY!E1>`VgQ0BYY*FcJ_w=ISgZor9cZb|Mxml+uzK* zE5A!)w9n&fPzsm~PUoa2H+VK?7tMw3(BX|MEY&_yMb$O-*6W<^=C!)mX{@IEnSc{S z_I7HME!T7h54|OsGBpxNGr-HUB)1S05uvhRg)Z@BH=g+$-@+9R94y806RE9@mQ(5g zeS*f=JIV>&&VFJzcxBcdi@+YZWS2bR0q>=YuJD}0vlnnkw?n=F25p30=1;z!j(Hs*g`kL4#ac z<2z*^!vV;bmcW@?p7hglN*%a%_B7C*;R|I@43AO&0G*Pc(SS}4MrEjdetNmP+nsLk z=#gzml6U9!R`-;(MdA{2HALwzrD zQDD$Y4C+_wFVK$4x&z>S{cqRxbs3$SaiNn+nH7L&+W(TEU5NV!`NvI;NXNL@C>wef%QU&AMc)udkVb#Hi2#6h~ap=a(*>mcK3RP3N-1X+xJu^bYT$FWOs7wR^T#oZ#g& zcqV|8m#G|+sE)?Cw2Hphrp@tz8;^8AQy6D=3%_bxDLqnwozzh<*C97*(0CYs3kX_u}CM55|}j&C#*#fP0<}(Fa=q+ zp+USmkE171uQKYwV^qYxDYt5MzU$LOz!L!ecNU8TwxiE9@-=$d+zf*=gi5055**^( zFMr)EM*pSHTuRWRdwIf`_#z*2&PX4uJtaz18fFxX@kXBm$eO5A1%+sT({M5zO@xl! zH>#O)TiasQ7q2P0f7^2d%>v?&q|LBcrUXjumi{(i2A6S7z5Z&Ka$(Fb+5}vETD<_I z5`QyV;Upj(@+H)MPitOajTroAfalRo9WVXow{b$Mvs+7zP>vli0qQc%~DJUx2DgvU2n1?0|vMdXUDV6VThPY@|Tm^_5e&Y<%i!idP zcRF`CCRohC`j0Hq1x~>9^n*YSw!ov5pG3eqFsQ{DlOfC{lCRg3b$&)u;*7y6Y zr|zjADoKVEAXGpb9V}aat%|m={@t$GFcIIgKC4+=7zE3kORTt!I>WUFnvi>D&NI^5 z%Tai@3pYo+6qsrA@$Bm{W?^^>^~V~1SQZlldKq8k5FHui{sIWoWZ$C5QkZgo-2Fpc z%3_A0W0la=lRAg7#U9@LyAq3N36%5u!I5N)Rfq42(CylL$v z3riMVjoWpS9!P?HF6Ax^`8JcrffWfuk1cwVhf1WbY`LVP61afZD%rBCEFqP`1b8dn zmsp@&DM%7ndr-}oXZ>#9ukS*JXWk8)3B0MA2x-Hgn`x*W;yo;q6(?~iIiOSApA1qf zikA{voG?6+B3Bm}ACsqQyO6>1D(vIQFzO{!HjBu~+2u#2e0hia$x#JMi=BtZP8$vf z!AzT(hsOyUUIY07^arpXe*FOc1H=!IKS2Ee{R7Moux-O@a1zh(|2soF4f`*=G`v(m z$rp`g>+bNshg@hX{r(&vaK;*LPJ{Pq%w{`SQij|Ml;`oNu*%OnAtb*#KOBy( zMsa>44imYFi}{M@y|j$gfB0cph;>}>1v?cti7b-2WU~iZ@HJbCoShF3M{M?&GG62& zV^h9Kd6vl4nrFqYj=V2l93IU^)04CFlgYTR$alUa)Tj$nGc=Jz7MWf9Jpf1M5Ak2`F5GJQRq9d#Hl z=DoI=G1(v+;ae=@a1_$K$bEBoo?3!W_$LD*q+K^7%h!SlN~MTL+Y zo-SnDE`4|cLU=y_#bcl6BX7Ex`PtQlV0@BEC++Fe=Ds zYy>NEpm?%GgT%^g0h=h0251e6dmi8NMb}Qhk8=V~M4Hd}rl4_oB5!*{``U(k!_(8r z`>Qwbf6wdT=)}8r&p-}FWY4OQLNPZT9aHzrN9pjE+l}h znV7GV?ht%l-s-T{ak&6D9^bIH!l>IEC@I`0Hyf(>TN5-o+2M zV$*#KzEj@D4{?iu`hr;H+5wTjAuoqfg+jx)W<>#qB+=*GkY~VJYL77SYB>J#n^s5b zZ8PjjbP$l)tziia@b`Ov+-Ey~a4|G#?3$x`->;JSN@+`Sg9gF!Eh;GTxX}*or;hs9 zf7r1{81XtTfZ9mTx8k}?c|pI=coyF#-M1pWU8$GAokXg4;wH}0@@6}^Rq*yP^v0Q2 zql2&ZiQwCT|3eY{<%^$CMbZDujago`|DQuzbxD23+hYb&3qfu{>uW6B$`@( zMVV*JA9)Fh#j2-B869*&FzpX+KAHUrAaE)#U)Yz{F*p)8Fn~q*4yqA~p zAZWv|$a3~Af9PoLa*wdxZpdOCx*NgJ6BKGbCGL7jwiHq8ayTAOF2}M&H;0FR9s)TIf3Gyw&OGxBMfH$_k%cVqR4=E43T@B$a%tLm}Zz{3=)mW>EY!4m<<}TTC~OWw9VoIH2rFS-%q^1X6qy?3%6Pt z%ff78SYSk1?5YgZkzw#If&<$RV{}bQgS{#^4_|aehl4Z^h@g#>+`zw=1nf;c=cOSQUvg@+B-O3~juU z=y$Q4l?h4enF6gB{xe`o1eh_EmC|3&3=3fArFX7i>hRG-Kh^Hw>)i%NwW54XJZYw{ZgQ2>$I=b2fZVy5I2f z5o>^$T|v%&J;vw_FiI3kjgWkl%#R|CxJ4L1m<@6}GK7uVMzVC(wf`o`7Ox+0u}H1i zMj>q1;p%Wg+@hM2(Yk!Qe|yxT;$C)R4G?brx6*J9 zlb}A^`>ot-b+$yM-gAw8y8(4lpsf$;7WR3_Qx--7Tg%-8G26WpA8$g>{ntu#r989& znuol@~0kMCQvK1KRI^?H`(I{l9EafJdC!&;y|60kI(gNlrrVnb;4rHlDpQi{K z`_#G+pzw>tpM`n=to_27`$4TTK(R80)(uAh+L<5@f16lJ@P zj@aI^Iqj!5o{KUQ>wby0$$(b>t&+q|Z3|768GV3Zzsi<7$InRm?5c)@bU_nJ%v2^8M zs-@X(0bQLuQ3px|o+cV7ZVJd&tc0_}N_B!>e_%g?u3paPB42_*h~Ed?gNEfvx+8^>paF}J2;Z`Ob>mWc^GW-f4re!R~hh!X6VV%Vn&7=)2F<5!wDfEO4qz@vwt3FEVdJh zJ&{<?6bU)gUVRJW4#Zz{Xb=;RP=e|XXC;GVaeAYa~T z8pC-i6EvvRkk<#V(IeKVf6NQ2!trhct71*T!_->+{k}f&bdd z!AnD;hvx;YpTbP|e`8}F=Hi}bL_AEpuct>5x_lO`ed((Dvi4&}a)iV~7L=PtZdplg zSmhDvRXgcxu*MjDy5NL8)56v?!kmtfU2=mMF3qON0EzEU-p=b zJO`3FW;7XR1#hUq4Vq5a)Q~Nw7Qyr#dEWcsbbK;C?z2g_fxWObOTkl?a}WFpS3IC? z_CVntRA4T!-q7pynyhIvX_+Q(vJ7!X>+RassI?*#f8I~MCoNSWtC}NM_vDvvz0n_( z8Ke*rm#-%ZyLuXZW zY(fLle8S8SMdzx;-Zf1Na@o5)q0{rqqL$!^t6GECC$ViXI@ z`uaI|WR5z)j7P!iKKM$WM{NlAsTrQ4)+deb9{m0pqn|UFu0ga}HA?qMy0hOW@@3A= zH%ei_gy8FT{Lq1^8gYLGZOP5C`zpTU&0-XSsB{7#kREF^51X9dVYmp3Vt`z;^}Z^x ze=Q}Hxw#2R`Z}n{2rhnAh%3U-+nahpPlNXK{r&yUAK|Bsse>h)F|+m%U8hNI-Aex6 zl(&bxvudNdAu4%+3xHI^2*zf?1r0VCpMFoYr}hX7Z7e|GDl|watXEO8V$q+>o#7JG zha~GRA!TEyWn8^u#&ok4L`$RT@Xfisf2?)&JHIk=c=Y-L#7cW1GL*C{#82gpY_EY} zd$><+{LBcCa(gUNyKh3U9UinRInc1Mo#gQdPiu`gr@@^ZO{bG-zX6s`b7_ma;-DTg zL8G|-0i#rmi<0y=t@hTA14B)=9} zvt>PE3;r{6VVO^3GwgMYr{OA>JT`2#lnr9M!9bT>DiroX+5D|8YS_H#CRJ8+_ny$o zUmbow9=8 zw&^+DIKf+65ZAHyd@oHyAPOwXf5@2t>#!`aGqs%0xs=N?)zSm-8_x8+%Ww)5Md6*7 zFKWTL`o((_hCkl_yfG#y^5P@^t*(}_mOhZbfECX2eli!BHed_G9~zbej6GP39-e;3rui`RO4 z4p{B<2ji$P0QdHEWtmhZ^Hq4`_^IbWekbmCwjamtK<|BvNca+A3pQr1uHy}HCXpbb zIebB+=vZ;{e0*3hA25GHeUwiN^Q#;6yn_Jz`N#yS&M!}qEp16sx^zBDxOT4}y5zW* zsX6sDR}d=k(%-xmPLWr$e}IPWP2W-NZ1H>S0@)rjH=SagL}{W`Vh#ibt918eGSlc0-!@`Amuq;G?)E zJt8A)0-J&+9Wq|$Anj=ud~`iN|Y#ll~zf5PD|ClBcV&6Qot*>K7u z^J78$ev?7|;0nfpx3pHHjQWI%n98?jlK~25d-2WL9>!z=As-aS~@CyOT4xtKom%=~p3?nF( zKCa-6)qOSje;-e+n`2ndB$e6QgDaw4#o7MM_ z?%o)#QqLrk*^#%ajq@qmiZ_n5FeBvcdZQux6(JKk)ne{?X>_m0jxZ>we}23H)-=(YHR$+F1QbpBt%n&8 z$Lw-jz4v@N93A1uma4ET^VM3rZ+(0g*Q;X91PdAz82v)^1i4zM?`csoXhz$pR(>LL zKGJtOIOPT)-kkf%&!-CUKkeS4?o0-gIaUL0b%2K6j7*Y>TYRzt{#gT`X2R*1rx zly#pbf9s@(4jSGZVmF3;7j590EO@-2 z&p;M2nFu@*;O?Lf*6zXD=KvZ2f2UduGJ}$P9JLjF>sXOlNQF=*O6J4?xv#@k+?L;b z?BR~z-v|;b0DZ1J(yjXBr1^ki{9}Tm>Vuz)e>Da1z}G@-@Y1$2OR8)xS;TucJ$5SM z#h2D_bi1l^L4BoC6XO-m-`FlI_wCIXFPh^QPr14nu+Sg5>{ZF9&va%mV;V1#QU+A4 zy^a2`?~$_(W+MtbDx8cP!PvJ^BalNyad1$RH*W#}!5e?Fy&fwvBSo$sXp;*pKxsa? ze=sfrwJE5&mf$CoFfVHdxDd!mHq`(PBEjQG-Rk=N*KJ>YahPACbDqL4J~db&N+H2L zW%!X)zph9zrz#BQT%B<`;i5pP!R|bv3TQC)n&N|6VD;@{E{_}PXc$I7ls2IfM^y1B zX3Dnfz*uX><;=i7V0VGfd_WqQ8cQFFe^S3Te<2Egyp7P%%YzRH`Ak5*9kn0p=%{#M zYu@WxMm(rGzbbK5Ph&fvH`)sD(jt%Pv$NnW`?#0_s8Sck>6^3%`>NgUVBdTUgSz*q zUtY`gD>MbZeAhUK3y|ze`kEvz4}brTfDb&-)roZ4e&Rkvd9Ys{VWeBFaVk*Ye+nKY zFs05xe9FEJ(DA>B5pcqfzB=f=8$z#ew40Hsh5!*VCgn#_pkT@Xsx2i#B+`|rKw0N=!OFV@~Hllz6{SWOA?#|3U zW?%P^C_6z*BrXYCenU+IIE!`JzH=l<4pZQhVG9CL^HIW#ymUM~4KwLQYUHJW{?DAEo zTdO+=ca>XFu0$XE-n*;S>|Qmy*UdKN%@jiH^3U$Rkz+rTJ$*B~F99_mN&Exc!OMj3 zH;TUp#rHeRWdhVtIW4tke*^MM9<~dBe%NX6y}atdk1p-dVSDe@mAH9cAi$FxB=upt zX!VpAHPFvqg3(wP{hq=o?A)G06Me@)SO+jlo>+6|sNrS7LmbUi9 zp>NbS@kfjNS7wplf&tsA5wZmu4qM?nDUm-gB#V0L7OynJ43!EnJJf#W%SF~j_8(Qj zKvjjS47}-AM(0ACe|mR^i*_m-klcazEvjFG8z7?VWmJ!be=AX++4kKntI#ympG}Wu z2mE5nv?tV~Z9|8KKXm#?buE&kepuvG`-M`(;GbWEaEclzpy(=x%)n%Zk(5L!%C&QB z>v)~Ry?id-W|Dv`K1^HPNJ^QoVVD3xyJqmeIF1>q{6?xve>IPR6TEAkE2x)HckV{+ z8l>3+nKt}H=Ku#VW%P-H8+NQAL}{)tQv>StW-6HO2i#w|p1 zH1J?$<(#UsUOidpzS8pnuhrg;(b^>t7i9B;fqMm&cmP#*SpEc2q%M%L>=w$8Rh5M( zYzXu+fBR*fskk&&hHM&l!~?g2g29dU7x08Oc5Lm9Ah{c!i0O4a>Nth&M==C@1FUMFFAC>H2B!HAEZs{jU7MF0WT-Mc~n zD92iAN`4wsYr2!u{j&*Wbm;{g)H?afVqV#mi7(Dv`l2XLMt98$Y)x0*8=G<{l7#4S`{ zD&3_T!qKy;$TkGdS6Uc(To1^F)J8Nke+9%KF_4$KhOVeRVbt;E?aXUw-Ed%hcYCO3 zqfA-hA83u=hPCpxo_W4XU$52*;e|eNKMus*5bP>qL;u|4KP_b$p?@~YHu6~ehXG-v zY#BPk@`70kwzFp+VdgMUT9}^VREZ=--`xqf0MfUE>*;NQN{X~1JCQ{q-QYXXe@P`Y z{FCd6Ay{$&O5Dk?@ZN;s5=rI+naI0NP-!v`Nu)r9<7_(H3x!+o`jQLqoVD|AyX#EtHOyZ~hIc8rgz~g(kdul5SDpr=5KmJ;K*b(X zSrL#y6(8~@Fu$@$)hbyQD49rIf2!>jbeC{eJWeHOYs_idKDjH)vsr_5R`pdyOc|Mc zi`ZM0CJ%i&!s~%xiEzhK;W|fCSnbW6Wio}|ogvZ_sG+0}lf-f9$=DadBi7wg-ox_D zh8(m5eqmL$K&x^`b&RnAL{RPy(nxkcNQ=}Ip%ZBKo5M! zlw(fhjGQ_Z-A-h0`sn^I*Krf>iMI$Md}Cnza=idgb}!N+r7tVL@rg6#AAoDR$nI8H z31W`Pr6DkPnXf3K3zW7tf1Gx`(6#;1ys2!El`aRnJLR5=kzK*slzDU@(hm10`^SUW z-V&F~Y4MSgXlVFmFx8l6hj()OXne(}ldMo}EheyIT$mz$lv5rRVSa$QIx+l&i!%_> zyy%?WohImup{)tJ0D$#v0#ORk#%K0-|DQd57$Gqe<>e}UYr>nOfBo-AZ+k3hNr8M}?|>qewY91E7ek z^kFPo5+b~p#m6~a;ZXIXgL7tH`RoEaI)}h(>qUV5DtCaf?yD1X8%VzC73_tV8NT0% zFSB220EROHdBesMf0~IwSkJ!1rczR>c^9qU`h35*)keo8Tb$Ml$QK592fw~LCBSyO zkSBWo_-Jx89*K?~ot==QH@X}dAJrzMvn1As+$sCz@I{3l5cK1jX(KFCtqE6Y+a*cl zw=F55sQRhv48uQe;;#s(%~ye{XSMr(cu|NxdQKgV&qr^ne?y~Pw})=Gd3^~=iov(r zv?P8w-iO&4I+4}+=*Q9c=TWPyk-bsMxhLM--AVbj`%n-4f~%v0^U3M{afPljfLt|8 zzyrvvH9C}gturzkSvGm>s9>{gIxMtFjk}mVepv#tPyry{7XRK3U`@_pyD-PFU5HMM z4ibc(zmbDie?}EOjBKTXX&LtvLb=6`@~}p(m2Dg?g*#jsYyQu_KL3Bry_mlI-bE%; zY-$hcOUj)-)@g=<6hl4a3q$d$l{N0^LGU^J({0K%|!gwFUXv-L@hxG^UkG{QVp_6RKvZRm|HDE$^e`7qcWs+uN&Duu`!D}kNLgx<;M@#-pw9@cs1YhtTkcSVX@*YDV`cPx0`M89lw z{cgjUmk?-uv{h;Kxm+wdM2WN)gcD3e{?aK7N0JT1p-0HfTS+I#34otP!weokw{IGO5k`5qu5;}YuxN+y1GR%i3j@$^D;lM zpSEAJ=bXCqg)Q01cperGVNhgu*Qs;A)uB6`H*XFP{`X)1{Br&s9(GCE8-wq#0fi}%w92Xc)@rSu)~A%tJ@*-t>_esHzMJwe~gw)MuFG} zc#G0-*JYQ2r&)qKS_%Dv5}K2Qcp^B97XS~Q*Z>?kmjH3cn*E8_Vrw4prC0-`HsfI! zZxO!wIpaZsd!{kd%K{e2#X=;sCDWh}4?v^R6>N~QF!niV7~;N3WSrn0p|}&F>arV= zEQFsDm43Y5gyJ69B_a+Fe=6AF=S(KJF(S!BVp)kvbFt!g0F$7nR)>KJ>8gv;7)-n& zyBzjsx888t19klG|IwQ7*vC}1*~Cf8wlZBA98wYPioCQOjw21B{|GxP%rsJL-XwC( zlikr&_`oy#G!scG;X}*7)Jh z&~}0&uvl<{I}x-jf$L2oR-k_JPB7n+OOm<+r3!L&7fFI>3)C;i(Z1TP*CI`T^es;o zpf^<&G@6;S2yo$~Pf{4>4A&w7Rq%Jr4+W31jgf(trQ!<#f4UcmRFP$^rBbBosRpw{ z)97FP&>s)Lsr)n<583qfbT+&O3mI~MQUn^uY=eA8MHM7XmhLt}8T-2N zM%R7sHNow%Yc8XQjH9{8!Z23=Vxr;lbU2udFLE$b!y!NYJ4dT-#1Xr&F@;kE^VlXo-bDI%v3Q|A=7(vcGw zS6Jf+6A|(hb%$6eWI`-7g8k_uwoxMyl&~mDEr$f^&37!m6G_N7uq@pQ5f!{RE)Drk zAQSA5M&!zhFNB8k_uuCPlEn%g)58cTNk6C vR)4v=!aP# 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

gw{{*{6O16Ev zXk@d>J4`x;8H|Ky3UeJ8Cs#3qK>;%MnIU%(IANuMe+a$~nChAqcFT`8%4{EfJQ%OO zv*1m$1VsC&65`Cf0pfeD099!R=*&kabx(a@dX7w4qMVidyF7DvDH5Y#SgbZdT+gyR z`6;`TDO5}tzj>DirG4IDmx-i8FcrJ$Qyl)HNDHKoWw}T>M#7dX`Q7;LzAMC`>(gjxKF z*!cxFgYR_8-q-I65 zf01;~cBxc9`SY`53y$}{c1#{IbUNFPyG@a-wjW#`AsWx9|J+LolWRp1EvOA-t z=~{I_Hz|-32J*{cjfgoTsjA|z@f;UhIjlJ2zEUX z^bQhbJ3XBYdoiZnCm939Jn8qkohqHIu79}xpzU3Bj`OdQ;WhMY#k>9qwZn^;R9i|+ zPZlFb7lTeU0aC#UkRZP|CBmo>B10B3330Too>sI0G3IEAW?-#P5m>ghG}jfuM#5rO@~&+3WwT6C9wI;n3-^lu{k`)6Iq` z(;aq$q+TpaQ2WppDsm%!Vo6F!aBjvLM(K{O2n->u%VMr2jBu8C{wX=u_Z5+`d}Ce- z-JfR(-pG@r#^{WYeO55Mp#{{ie|a;cz}RMr=nHKz$$Aku&|AAFBOznAQp`)d8`?z4oGVo-2S_m{d-KuRi=zr-eP2R1fD&ZEY z?UJJHg;ywreaI7tg1xli#*MMOfF4i27m$w`F6`WExXVBNfIy@b!iMX~lh3mXNNWTn zzMEau^cg2u-9RG0T6cSM;aXJyNxLg8uFu;35?&*JF|)ZBxdG{7glYmUElNu?(W}So zp11Jpm}Y|WQY`;X{(?W$M#cLPq}OtdNFqB5BtAWxa0cI0aF3-y{txx25ShA5@^I7V z=)$#+R;o&ae*8i`=>oCVzalDEzPjjhNSlar^fmB7ny>pjUSts|H-@`_*Q#HCs710c z1b9}1vBvB=2}}l%KifJVwG{1q8=H~~UP*tv=hW_S<>Cg$Q&pj;TB#8iGj<=DzFP_P(lfT%?yS=lvC33^_RnWF-(q1=nvX1vEidHNopspO-)=x*JC7F4lCo;DHo* zH5`lop4ariTWweMyNp}AE`)}!@%a3^dbS>!TcG~QH0@4e)xUrDbzX%bir!|Q6>8uSc%>*>4Jps4Za78V(jjC*2-T8xEGy|0cgz`-AWaVU<4G{){ocRi2g{C7(*m?Qd6FUF9;b`sB=@-^C)$!! zal`Sj!A>>~MM@lTE#V^^O+3wz1oZ8IOEEAitpiLqw8N>MuzM@w0}K&sOfsZ(4XG>g z#|bs+^RsVsT>|*eKTcn9#rtOVS{J1sOcpU4f4{{#YkqD5Qd2CcmnX%1argEe#feBE zEe}j!VFBgjR(Q6i7xbLHQ>(9=5q-fwUQe3rmQ{Xb8K$Jm5^yoCDS`>(Sw`BSduNe{;e>G)EJjj0(=o3Zf~jGFt? z%PWS)<|v*4tvv=VYQuu4K3Xy)BJ&Lt64C2%;fk|=6-T;ag=or%ldBNje29vl``=~3 z900L&84eQD9`S)4@F&f-S112{6X7U7>SvZxj`3p_1qA;TfIn^R1yLzv?a7}xvJcTI zZ8LJ2CCEQ(hAep1Q6pO*vWi9XK&2ysTFMhy*a&o!Eu(b2+3+*e?5BQcOXV+vekoS; zRHVa%+AJr6pkgjK3h^ztk$5Z9t|h@o+*BhT$p3)lK%Bu@upCASPP0RE-OZ zn*hmLBD4to{zFE7P`g&0xO!dMU6e--*N(<*LXW!(R9@TA!ocnR)hq0ZbD$itB81da z)5V3N07(g3?W)XGX2rA-%kjUo@g4(z9=4RB7V7*|+fv!+Gxu>s0~qSd38Qx+H9#T_ zchCY>G>@9QiZ+mXOl7h?UITSMo`7Tl9>{xx#Xa@&g-tXywg`q%?WIGBW57Ru)8f^< zdv+T^DCg7H+y8Zsx;=A_;4w@iV5Lk*HLXc5g;le|#5c8ki-n%hE9=iz81fG-ectG` zEBdZ%>yI2m8gLM*j$k8$`De-&Ho)G`HUZWu%?OY*t?@bCS%=I!(!4N!7(7tr%5kim z_LGRfGbcJKcToKjX4&%7*vL5=65>Au#CnAmmKe=@#J+*)@0LJR=`LUAF(YvUUeW-qePa^fiKyUq`~y;pA}7wI`v5E;j$F_p_acLS*V400l&yHm!PB8KGGwZ;UgAb z7$F>J_90Vh#uQR*4m&+!r6m;w(}rPlCh_z<@qaPGrxQ**?fLlnJ>XRkXD_;n6}?c- z;~XlyEAbPbk@<8*`6Y(%uK_sEOe7-*swj;jV<#Kw!dO|t*@*bjUrqM(JlW$t=Q=}0 zgtGB*CWmom5u94|amGwTvHXOC1QBTk<6gA2pG1CleH*AH@QWaIv z%U-RLFl9)ejx`L97g59Wfc~dHopaf%X~_#befDfJ=7eUXxJD{y8~}IpgO8e%HC94Z zxl+gi1Fq-4!}SI+b~G&YPX2(0uXib|-;Oa>L)UYUmjXYhq*@{@$u*>=%W~Iw<~&e7 zJIH;fxuN6fTEXn9-fYU9vh}? zaOCRjCeC#xMu((+5dw^Mzy>Lla71jp*YXF)Kz3LIRRkKSWsAW&`%YMky=S<^#u{C9 zY%sgkwgMyruc#bfo=OWCZA4|qM3Sw;6$VFQ`H1s8o|6yS$BTAAUS zrUr`01PYe~@h0klZlW)-cq}o}09#2p2Rij^G_NqE%#IsIcah{D@lxXl&Ous#-JT_S z6?=yi+35&%4FPbSO!@qm*Nb!aSa=T!qE966+N`-|34v2I!@7wZZ;Hkq<7r4L7nqv& zb{N1sLmc8c=s>JNyytMi$PWbPsHED*D`b-l@?mmsC^+^@i$}RcL_$r2h@-k zWFwpyGWn07GJYY&g7+GBA}tzC0^sUlW>VLL1Hxv}Rsa!k0tlyOV0fAv-fcfu6=cnf z@4x=T(zU!mT##W-!!n660vn+7Vn!C?AzpTlUS>}skhY*GH1Vx-P;H9i)S8^>$oVlqH`{_w2I6htB2lNWfbgrwRV~`HIPe!^Eagg2qNJaJKK~ z-YQYY%FGJL;$0vSEBiS!xuX!m;NnRlMW2_==ksz@5SfJy}n~qxO1!lluB~ zQO_@{Z;#oi8XADsA zvh|{e%i;JM{tQ7c91K?J_PHlw1|k`NsTFGKgI+5K*hbr4;pkR69R&S~3jS1QG8jP% zs}}a3rHr3W9Nk{Y&>kMTp6Ce1|JWT%Aiu3D zxi@5cxbZQ6s3(9hyWZ$)qkLR|8ygr>X3JrEe-f`yDrPr&Co7=z@Md|Ep7RmTbt4QmQ5U4QxcW-`4vwLCVfE0UeZ=aurxCcCAK!BJ`> z;lu9Ay$NxBa@m1;qukF9+`S=oeTdT-+P3+=X6?F#(PyReQs1`Cv4tz_^4gbXVX<>c z^@2sFW{6;Ern9;9dM#nYtxCj>BccYXVTq&_FXYaS1E%a2K&&17;%9|oG#}VPda~qA zs5QL~0e(}hefVR1`OM6JVEHi6G;IsZJZYOMu0wybn~q7wUaab2+&C-i2Z*)+nYiE7NtU}6{|^0;TzThi{#mThVP~$eAtAAXQO_P_jyPnF8?Y+RO2ytcZG03!bk4e+3C=$6>&1h?UmIw;K(PhJ2bkyN($ir(UAgmaw;Si=LI6eeO?#i9-cR3Ldg=W*xASFosTdvF ze`=IZVMOb^JxesE&pV!>rD#O*E6x+I4sQ_M(-zewlB)qUaUE%*^JHIhAJ10#rqaxy zSobc}0D@C6O;mD^CrE7MKKvU|z4Z=GuhhStI5ubk zD>lWg=_BY)I`M9LudC?(Hq;RdS%15(y(_o>rE@vvU0ce6yUHJ#x6phi`q?k^@(|Bc zV!BSV!e&hpjG24>eh1Nd;SBrS9wFik{Wi(31SoUpC2ZG>n)db&-u~R)}rbg5x)sn(W<~(9;I&VW2l-c5)Dks=sfTzSE zNhY|&+^IX^OR6HpdTh%bcz8S%&?6tfS0et&R58dj;#bXgQo=rg9Y2;KYQl(udNvI| z0fe^V9<-9`TAg|_D0s`eqIe=wf2KYXPpsQ?dr*52d1(j4$6nV=w(Em9SP!83MH>|g ziwW!>l)}DK{Z4fZ17ofGD0+Iz{r(=g1G(-UFHqaCI^W{%aj7gVPDnZO9S0Z$OYe2+ zzNus>k(8cT2JJ(7?SVfF3TGd6ozHJ?40tGwBr@FK0aXJtKp$OOq^{}W`-wf3#5bLF z!}%8pTLFs6d;V|X{L-ch6sO~fnYLH;yBKy%4@Kc5*OVd!)vKvysXB}lSAB-9{=SBS z%9X8lRpH~jny19*wM&kBbn#(Qpjkqz)T$ki!o#6y7vG333%39s(yU=_)#P4G1pxjL zj}%K&)UEP-hWaj;mTQ^>mx6J9sbayxo`=eJA*ch2oayV`%1Lp>U`nidJcsG4z7xX|7Yuw#7Y>y8 zAJJvQZEK!#tGo5Cg4?YU$teB=FCg9=G7BU=y4kSgBd>$I#DQ!W_z(KoMUo%ijq&- zZ!PMjmmr|w)%ioV8l+jV{4mfSgXuJgkn_BaSfguP+i_Z(>v0bm-f}{724JLOa_U^S z#oIz;pjQ23OexTibKU#zw5lav3!nU7$xo$K` zGRr|x66_z{5bo4%s0BLwM*y~;Uh{_=pV+a2Wwc^;X>RoQXK#0L~W>yE~ z6H-Bl(dzd9pAQt1lPk7uCw$i z)5smV2uwRfUy!0VanMpi`s*Iea4}1bBzDSB7p5&a8eFhS(Breh?f_h5JM>f$d@kmG z>9)zt``4E~ByAm*>E+zzs!upMnVg*o4IV~g^#OJJTG}P$a}7FTVwIM`pHGK8ZJ5@$ z6#cdYulBdk2j`th8mwb>sr~?2sb5;tN#qI?|XF!%@NQZK)AGW+qOPG25@TX3lf`4X4R0cxp z6=eHT26DrrflC^^vS$dqUZ;sUu~?g8FmE#Klcy;>U5dt9$uaQ1+2$R$pwMgV*qIw> z_3@^|eGp`X5$4QuOO@g97dd%Ll~ElXM4FA>x{#Ou$bfCX@c=M8Jn82+eXKkz$qzXi zxscGEEGpLG3YroWF`4yqwj@R5Y}F3paks>*Qa`XrP;HAQB5f;Lv_xuBUU%3xBXgK8 zU6ZwV2%!(ggLzt5(3(q!@8qvijfz}RM_I9gxTO`LgQElem|`{OI*NWez0Rk)G1VXd z6pKy}sCT-z%mH3HUdpZloGPX0q7>0SLJ93YU|6S*`a<}#Q@?_ZIl8ADI-fnwHT`5*a^|-P`823N(Kvwc7!q?lX zHN}R?b`vQo9?r>3eg-Qlf5W&ej=`8Cm4YLw%C6!gFn}Lc?U&luCnC@4*Y)nZP|>hu zlTlL645N%{Pz2jWD0Ju2Tyf4grWu%~t>v>WWkUe|BhABf&`@L*ueXnUz`l25ZCOgoEqIlUF=4iy) zD}7jG#$d2qnXC0v=`~M4p%CO>t=f%|?6Tp>v$vn@`Z+w440o6>;Pcm|WOyTUK;JXO z5xrYBD0ezDL^_D;bJNvpgsX`NvKws-Si^Ah2tO!e&t3)Hfg>@rZeQTY)Tb`whNSi5# zv4v66DNzTwf(eECiB6i><)v8Jg@(y5HcSt3Cxc*~4+4heJ4;%^-_U#Qid!CR z_ROK#w@3=Jr!0Cn6lRFt9H<|?j_LXv!*Z^?0i2al`P9*@T>Qg7_yQeJe(f!@r>H?r zonqG*1$P!e>gp0U6S>0;k=$e=$@VR(sQ_qRt0iiUyMvXEWV!gcgM@;3rL>Qr4Tt!; zfx|uh@A*jX9-JK6_}L)#qV>KM9BNJIuqI+r(*m`uaAAGw=g&cGmtT(HDCPCY3rb;B z=aqfkE2v4^J?#x2R{D=(sTnfzUH2oG7DQh!G})9sB(YA!%2`Jb*`o$9?@j5tRsd6o z<@ff(bCP#Z>35HNgjFdeR^q=G_+cP0Fwr$l@CZq!dDBtmAY=~F+6y&yuA|2;KHC9r zt%Hpnk)*@MqBrohMu%pVtPkULUpw0ijBU;o&-og@;XK9aU^67*>kY}wg_TxGNjkr! ziDcF=H?+Db>mi;YjGemzthRyvk$`_=`TPcX6bk}7Yyw@;$m5Oj6i4WRnR$8N_h%kf z*@R9SMgr18lC$aUEbG-ta^OER?g>Zk_{2S_{XS{HZTC7m&7=VZ9{NSdIsb71Mtd|4 zalp2&h#;r#x`}^QWr*le z!skY&;ZXx~eXtSgT2A3Q>rAVZYAUaytZwjSj%tn~ zN}b7Njm#0})vOD1Yw_<+TY6wrD*sNbscX*2E(=cYb@nD1rQ_(vcZmF}1I3?WfxEl+ z{pwObI25O1Rt73}1F2iYj!-Eb1O^*LKvR$`tgg`1c)EJAuVnIQ}|< z6eR~Cwk3o4?7-=Bkwu)}gVT+F7GjnkruxHg7NufM$Oy(%xbhT~u5s9?JKZMt=3${U z(Po>g4W?=Uh?6bw@>ml(-BLksn=x*i6Xf-+zzyUENWCG5?zk3PjJN=}sKY zR9m30VcPpa6j?SyS~qq_UY;;!-$o9r6)iE%x_>@S&W7Ie;z}tpjzM2!F?m}H&zI-8 zfya{dTl4tRpNSWujnRR!HUvO8y^}?!a@MOB>vUiN*0Q0WX|?c8N}M4_nUjF2*lZvt zQP5Xib}(LHZ}99afo*uKLXJdJDyV1Ep?38hd-ox%@lFs|9SWV7rDAaO-gYxIO@e;$ z)KTyRu92AMUw$$zofX!QqGHP}(CTOSfxnU&Lm|n0XR#sJx%Hbs7N+dFq0dX978Ssi zIj}PTtxahz@6$7XZSx&1sYHC-P@;skyZzVx&K1>3XpAq}zi(cA$86Ucdo8Abp-q#- z!$ieq7-R5SSg=966I@4#_k0Uz!uW z2O1vuwaEXg5}rmwU&{keN|C%$Y`+%CUoe+%mAoRgvhU&s;1e}X$42mG$Gm(Y5Q)PA zASXAb)HMUPl^#R?H2EDA>1+xmlj2skUCsUUnGS@y4RAuzcN8d^ro+14#)wvjnkH$U zM*P|{hqI2^wfY?+!s9L4*L!z8w@cR6i8%0gRcbc&!x?Mr?SQQn-ThB^_3!G5l_&gW ziUt*4`8Q;Q&AXySiL5aSOH&65mVSx~1Y#S@izzotXFh#*+jN!@Fm!+|fE~^nd7iuz z=RPzrbq>?kSK60LHnc!#Sa;Zz_T^eIrOC~X&&NQhsea605%)|Je%Of-i=(kkLvClz zuEM8tsUoH>s^&w4^?Y`Y+b4a;wZZa!cRmc(yP?%Q2f1;Bo^X{)%}eQTmqW2qOd~G@ zmnxHM_CJ z-P{p;Qto9>!7K|kEesSL^zmwdRES(^<6@ArWn$IkIcA|6KlK%55;ej`_?;@KK`Kct z;zI3U0LPqC&bZo~5pD#nfsN}>F_T{TI=b2pkV0l-mFR$bn+%fE6(1ZrpSN$-Lk4Q8 zVw7i$&R3D-<5sz}ZQNHCAAJkqDT}7-u0sg8%V4%9QzG)>(r1ieQl%-Bi?M z&FhtksXJ{ue|D}HA#kXNb%6oIKu-B%7I`$ zFug~g=2c*wu3X-dy_q-inb|h%urp2niu?lG|F8?A(E>DT%CT1I0U`K=@A2xxJ#Al% zO-{>9VK(>6w$70jils*^1dLydSD#o!#FSAw^>)4l725xKLN1+wj=$0a)(g@zKJ$Mf z@4p>C>jW$660ZGi;r~)oIs7>JS^P_o>BiV&{by099kolw1EzABYP5U_)4BGA#_x!6 zQCRo^ybb_O_NFd2&}Lj8B9la?y`p^>3Dr>jWP+k|wQ!V(Cb8Ym-=SGklqlnvawe^k znse8;gm_yn4S|f7SO>lKxEKXY&0wZnMkH9`2|^I7^gMqnD$OlV*h3?~J(8vX42F{n zkAOF2d9hIWT`QY{`>#0%e6aU3U6IvUMBO}d?gN0>ZQiy^-a&1~nd+>%sZzLUdr_fu znOU5-&w%%DqkY+vp}yaT+n)xGfaeUpiIm*xDFw7*l4#Fyl-6?^a+OHe>p!dsl}kb> z(T?9XC>wBsm0T0s)Do!`S@`OJUDY-!6z5XnQvHQM#WWiRElEnG$l4%-C)t{I(3 zBRxQV!vGt|;DO2e>HBh-jIfWn!b}eC<2uBU9GhLDUxp;Q5Avbz_pp!0jV;8%K9Op( zwt5*DbEieLs|}-AHF;@_|0s`Tjyt!!I64_BVYddz~VU>0q zjnlWXG!U7)U&;SV7mH`h76hD4WBI2u?;5}rPV1C-*i>(cQGY4~6yxhppM=Z4ktP9}^b3#&;TqmcQC1NDJa;6KFU-zC$a;}-BOKGjtG0tq~L~DO;rQ@pOFPf#ho*dEBxbc|t6A}Spbu=B}D7$)AQD&zZKQ98-kIIfCz?qq}_GYc* zopMQ!eTrHCT9&BH0*5~7oGiO2?t?i3$X`3o>a!}1L`&rnc_#VdgY1k3uWyV=8`j0i zG`9No8+b*|8{R^G^Fkr6*PKUJ%%R&H&d*0FB~{E(1wy-Oizk(7{j6)Y;lTxfh#D0U ze3|TqtS_F+!gvxG&_+m=G1o+!qU%=5O0D6BlV!AeS%?I* z))72rdz_K9F!rN@abNHJTfYS~M8h;wnsYz=y>m;+BJjQdYdp%Sed!Rn1$(-OTXXa4 znU&CSQ)%($L!{hcm_npqQxNQ8!%)?n?AK)ZQ}rct{+>CU@Cv+2IAG4vq9-vvP3(y>!iANiKXs$%n-l2Vw(uCU~RwL1BN9?iSu$ zol)q!d$j-_kCS$gPb{&u^tE?vd7{!At&zn$B$aT``UW59{2h#gDH!Q^W+j+t1%v{e zomv4dzQ26kD}P!me2b>~Is=r4gnHJ5y1Ds!1iAVAy#CShuU-ITY?qb)_^Vv83A?Y4 zJ)4k-jalIstQHyFJAH{dB2nAay4ZN5gRXU0c#l*nERGe9dL?fEOpsi8>-+l1*X-WX zcw0cwO;k9x=w_Gfw~tKHMkK;TX!y9%tWF(#oC%H4Teqw}qUE|nZBNosxwp57r zUs}R*eY*c-D}S;YFE&fJ#$(_@m8pDSkEN0B*iNV)}qvh?`~)Va#9FxhE4umj{~Y$n*B{7I8XV7z=9! zcyB;TCD!>ve5dC0xZh$6(XW~?CJlDKmcdVc8{PP<<)YHZa%%w_U1G(BN;F?hl@{9F zB#hf~Tfc`+&C{2=zSs8V8s$f@$DyU3f4JHc*zAU?@2qj%;Hd;0c=aN1rh0e%n7#gn z2+V+I58dJ$5}%(raju_})BN~|#|wXksQYtbp~AtW)7Ix$SL(R=W;@i?N;QLpLoc{o zXLVwM_3vUFGuWLyqg#;u_V{Ed$!KV*m=a8zG z)IetB)_M)K#-)VEY?{T8XLb#_&{XQ-h^xvFf8jOPkm7kRM$|YmaW<4woVT#eP3`II zu1+r#SfCwcsGWvH4AUX5G?8gT;LU1bcnp^cXF$kULpU0P+rRlKvVH&# z{4nM@q2QO?ZiSvLNooK77a^aYNOQgBbcggotuc78_Hf&_Tk;_S2;QmxGE z7)E-X2aR4r`7^inl?aBGXvoyD3u^|ojYst${6dN2Rw+B_tZR!8hf=9by8(cKt6v|F zYrwrZNPaOmy`oR^v+5l)85;TGgJRFBN#lW7RXB$Epx0*e=kX!-SrX#-=j`L6aMJuc zzG{(?>|jx3tUge3l&2+KeMn|t&!Jqumg{oZxc4)3+J+YhCqk;e$sh3%b~6j@<&oxL zdE@ig8E3?1T@7<-^FG~Y92Nkl5OagFeN(gzq7x#J3WRob&WNc+anUdQ<9YSjvP^2_ z#FF~!^&Ei?eQ@2FswCJv9raW;<;&1Q7m5<~j;K9C2uNNYu;1WAE4^^pn;BhbRjA7% zY&-aYntGEYis8oH567G#E>lgr9|$jd&_NkgaPYEptAd@rpdUv$gACv)^ztS~6))yd zSXo5pfC^UjPZrOjYGBY!C%XN35O#}q#uT|&@o+9zwiX0kk5V%|36;AYK?LG)exc~i+z;)KGL=-{BM zId!1-s#qvkp_)^Fs{&A>AzEbsdxYjmOi@q{3LtpN?nY5!F67)t7?>{{i@rxT8Q144 zCKrFvhUe_Xb<{YX6W7ogZP2wMXsEsxXV`Q2bB+?)D9*juFJo+xbu?krvS`_~J%UR= zBLhd`uHtTXmF2`-bLBz6HEc?1*pks7_d|Vm2mXKzZWPj1)(c=#argS=QErgbg;OQm zYNa^GJ0J2kN%i#7{}MDF499u9#VY}qqJZkZ@6}{C9Tin+)qj|!Hm^JQWs`2yr)H$L7um$L>RUf{{1m12OpouxuX8;~LY-s-LSY>+1HcXGN> zv{2>vq=c*##|T*UOO)r4GUuYvLgvfH_b!fhtC9}aiA3`=jDZ?1knjhmDIBn(q)YN~ z_jIC}&1K|Jq3~lqa7tCF1sA=h3?4uQ!KP%4;#3^@9FatzZqS$^lB5y9ynE7OO>oS; z8GK4Z7%-{*R^=7txKb$1*7oc(Ev6qjFs|4Go#!!U^Q&{1vQnH^<72| zcl25^3d!}9!%%+={Z#ffnE%S~mQBpN`p0lREskN3eN@P7mi>ijv6QD5@6{V2(LT(X z*aVr7Kbr^@5NykPMHrpme=5CS3tG@Y4h9OS0tF1RZR~Dz zRtG+pZp4v)15~GHYZM-{Hr}t;sf8~}u7)o-)WBd*>f1rxQthhK99ed^k2n|w8odki zS^&NLQXS2snZVyM> zwlA!xYO0ACksuk|qAs2b)%!@;o}^Lsn}D}TqRo?DwB`=fii0Z*9JJ87{aA>vCJ6`)*&E}$wc*tHo+_v?+8Pt6cR27MRyD@( z%~Vft&PWpa_x{N+iIKerOJmK@dEw)*F?Fm4cCM71@XRSuQR<9>?xtWjXE9x$z8z{~i2RIwi#VH^t%f{s z^?D?m=O9pa*;x3`vnH{#>kwLyI9kHtb2ffTnWL-NPn7P>wV%G-JSpb*)bqDDH?1y2 zqC?UA;o19-b7$EXE2HV@ql4)S4uHM$zRbY4q{#4JTwL|3ij@an-XB?Pn0CY?o+2p% z;t959%M!XSg@>1hN#a#iEHFmUC^)qD!!`qO2|?ro!?xu;f}P!)fL|1b)2fSaukM!5 zv~?BzR1gaiR0xN6w$nHxh9(n@U0G~p#koX7kw4OV5P(?KE;6hnCLG!cU;zA}CH1A* zJCeIPKu+^Ax*g5npDx-Z=Xi?}0tE=8WKwYh$@v6$udNa{Ecq+9WYRQ6UdM!Qc}$jx z=uwvpEyTwCLC?5;#N~3oO5n&Og`{{PGmcr<)1gpAI_QMKQ?dCM(5=Gq$^7Qg)+4I3lTpF#YWmU}9dH-Q_MQR1DF|bxH;W4uAxCI>`m(3k0MuA}ms>FW;&Cu{ z_U9k#TMH&IT*tdkWHw%*E)@~Q4qfQT` z)#CXBN!=qvSL9BJOh&eaI3C!N`T!X+(1NeC`6&YnJVU&XGooS|26#m2clxJVbz4wJ zAvY`LU)dUwHk}1gpp5?gap&NCf9Z>J+((oJl0b(JdE2fONoy?F2c{EJeF#4;Yu@oC z_gj>HTtc4&9(9rwFLOSS1RHcrS=QZFU^+0m?4_oyE>UyOhd^v_qQ;CiFyj2o5*2TM zf^U;am<_DneNx*<7*OS2Cbs5RBg9M0Fr+>DG=!+y^Pcm2*|B%&)cFSc1n%3e+W5J1 zY|Udfdfvcfc9eiW)vJj$2wV%j#uF8oz7diTo2z3zXu*%_O<4hg>arlG(!5a+Gb)0k z3d7&UMmHmG$W-|u9HtLE5+ zbyLc-v*d{@0B_GSOQghK*yI=4Z3`OxB7C?pP2){_Kl((pdapR$kwoY&y9)Nonc#=p zm2!BLc2eN+k#c9YeYi?2Sns!6Xz(-QVWh<$@-kE3($%b}yKAv?IWl+z1Fbg}2^Zm1 znftGq3`Y=+XfIm6f4PsBv`-)YQi!5u^*NiyFt4zz1Sn>z*WGdjAX7(EbNT zmC$?Lb%b2Tw?%c!-hJ>gxj3~;sa49j>YC`p%M{C1Cc~-$P$d4w2ci{=+=e+29<4qu z*G~OR087_TCi@Yac%C7M8{I_2aBCGA8y1q(W-4rWpK>I7Q!EBMlOvivq0{)(Sn$m&L&X|n>f$1zWPUdy6&xS@@c$eo>LlT?V&Li z=^;?aYzQ&^n=guxv>6!N0}_YMCVf`w46fVj0FdK4>A^25^Z^-wrOAGl5M6m@gTBR3 zwCKVT#Pno!QPoB?VU1t0d^yQkEQvl26QfKW3#RF@u7o6ArJQ$!JvZ%Fzd!p)q-bpt zqbF_|vCQF|uMD#TK-Snbd4B0gn`_!iBeO=$nI~o@sHZPUV?omJ+oR6Cb#xZLc%$A` zpkpW{H&o<19sm*wcE3J}mF2)8(i2yZ@WNBJ!M*|i2I3pYZ=k+`{s!h7*l*yzf&T`f zEoB?g=Nak$CY?ZLaSva1_%fU49NepH-dbpu>H?P@?DljfTfe3fMKL-tsoH~sV@4D4Si3(Vt^$#ERr6$ rg2WE)y7s>#rdE(-!9n-`)51w-Sw*4+E1pd^SVh8xT9`%pp6kB=Q)@T1