Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit_UltimateTrainer", function(localizationManager)
    local locale = BLT.Localization:get_language().language
    locale = HT.Utils:inTable(locale, HT.supportedLocales) and locale or "en"
    localizationManager:load_localization_file(HT.modPath .. "/locales/en.json")
    if locale ~= "en" then
        localizationManager:load_localization_file(HT.modPath .. "/locales/" .. locale .. ".json")
    end
end)

Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_UltimateTrainer", function(menuManager)

    MenuCallbackHandler.ut_player_set_level = function(self, item)
        local level = item:value()
        item:set_value("")
        if HT.Utils:isEmptyString(level) then
            return
        end
        level = HT.Utils:toNumber(level)
        if not HT.Utils:isInteger(level) then
            return
        end
        if level < 0 or level > 100 then
            return
        end
        HT.Player:setLevel(level)
    end

    MenuCallbackHandler.ut_player_set_infamy_rank = function(self, item)
        local infamyRank = item:value()
        item:set_value("")
        if HT.Utils:isEmptyString(infamyRank) then
            return
        end
        infamyRank = HT.Utils:toNumber(infamyRank)
        if not HT.Utils:isInteger(infamyRank) then
            return
        end
        if infamyRank < 0 or infamyRank > 500 then
            return
        end
        HT.Player:setInfamyRank(infamyRank)
    end

    MenuCallbackHandler.ut_player_add_spending_money = function(self, item)
        local amount = item:value()
        item:set_value("")
        if HT.Utils:isEmptyString(amount) then
            return
        end
        amount = HT.Utils:toNumber(amount)
        if not HT.Utils:isInteger(amount) then
            return
        end
        if amount < 0 then
            return
        end
        HT.Player:addSpendingMoney(amount)
    end

    MenuCallbackHandler.ut_player_add_offshore_money = function(self, item)
        local amount = item:value()
        item:set_value("")
        if HT.Utils:isEmptyString(amount) then
            return
        end
        amount = HT.Utils:toNumber(amount)
        if not HT.Utils:isInteger(amount) then
            return
        end
        if amount < 0 then
            return
        end
        HT.Player:addOffshoreMoney(amount)
    end

    MenuCallbackHandler.ut_player_reset_money = function(self, item)
        HT.Player:resetMoney()
    end

    MenuCallbackHandler.ut_player_add_continental_coins = function(self, item)
        local amount = item:value()
        item:set_value("")
        if HT.Utils:isEmptyString(amount) then
            return
        end
        amount = HT.Utils:toNumber(amount)
        if not HT.Utils:isInteger(amount) then
            return
        end
        if amount < 0 then
            return
        end
        HT.Player:addContinentalCoins(amount)
    end

    MenuCallbackHandler.ut_player_reset_continental_coins = function(self, item)
        HT.Player:resetContinentalCoins()
    end

    MenuCallbackHandler.ut_player_toggle_skill_points_hack = function(self, item)
        local value = HT.Utils:getToggleValue(item:value())
        HT.Player:setSkillPointsHack(value)
    end

    MenuCallbackHandler.ut_player_set_skill_points_total_amount = function(self, item)
        local amount = item:value()
        if HT.Utils:isEmptyString(amount) then
            return
        end
        amount = HT.Utils:toNumber(amount)
        if not HT.Utils:isInteger(amount) then
            return
        end
        if amount < 0 or amount > 690 then
            HT:addAlert("ut_alert_skill_points_total_amount_out_of_range", HT.colors.warning)
            return
        end
        HT.Player:setSkillPointsTotalAmount(amount)
    end

    MenuCallbackHandler.ut_player_add_perk_experience = function(self, item)
        local amount = item:value()
        item:set_value("")
        if HT.Utils:isEmptyString(amount) then
            return
        end
        amount = HT.Utils:toNumber(amount)
        if not HT.Utils:isInteger(amount) then
            return
        end
        if amount < 0 then
            return
        end
        HT.Player:addPerkPoints(amount)
    end

    MenuCallbackHandler.ut_player_reset_perk_points = function(self, item)
        HT.Player:resetPerkPoints()
    end

    MenuCallbackHandler.ut_player_add_one_of_all_weapon_mods = function(self, item)
        HT.Player:addItemsToInventory("weapon_mods")
    end

    MenuCallbackHandler.ut_player_add_one_of_all_masks = function(self, item)
        HT.Player:addItemsToInventory("masks")
    end

    MenuCallbackHandler.ut_player_add_one_of_all_materials = function(self, item)
        HT.Player:addItemsToInventory("materials")
    end

    MenuCallbackHandler.ut_player_add_one_of_all_patterns = function(self, item)
        HT.Player:addItemsToInventory("textures")
    end

    MenuCallbackHandler.ut_player_add_one_of_all_colors = function(self, item)
        HT.Player:addItemsToInventory("colors")
    end

    MenuCallbackHandler.ut_player_clear_weapon_mods = function(self, item)
        HT.Player:clearInventoryItems("weapon_mods")
    end

    MenuCallbackHandler.ut_player_clear_masks = function(self, item)
        HT.Player:clearInventoryItems("masks")
    end

    MenuCallbackHandler.ut_player_clear_materials = function(self, item)
        HT.Player:clearInventoryItems("materials")
    end

    MenuCallbackHandler.ut_player_clear_patterns = function(self, item)
        HT.Player:clearInventoryItems("textures")
    end

    MenuCallbackHandler.ut_player_clear_colors = function(self, item)
        HT.Player:clearInventoryItems("colors")
    end

    MenuCallbackHandler.ut_player_unlock_all_weapons = function(self, item)
        HT.Player:unlockInventoryCategory("weapon")
    end

    MenuCallbackHandler.ut_player_unlock_all_melee_weapons = function(self, item)
        HT.Player:unlockInventoryCategory("melee_weapon")
    end

    MenuCallbackHandler.ut_player_unlock_all_throwables = function(self, item)
        HT.Player:unlockInventoryCategory("grenade")
    end

    MenuCallbackHandler.ut_player_unlock_all_armors = function(self, item)
        HT.Player:unlockInventoryCategory("armor")
    end

    MenuCallbackHandler.ut_player_unlock_all_slots = function(self, item)
        HT.Player:setAllSlots(true)
    end

    MenuCallbackHandler.ut_player_lock_all_slots = function(self, item)
        HT.Player:setAllSlots(false)
    end

    MenuCallbackHandler.ut_player_remove_exclamation_marks = function(self, item)
        HT.Player:removeExclamationMarks()
    end

    MenuCallbackHandler.ut_player_unlock_all_trophies = function(self, item)
        HT.Player:unlockAllTrophies()
    end

    MenuCallbackHandler.ut_player_lock_all_trophies = function(self, item)
        HT.Player:lockAllTrophies()
    end

    MenuCallbackHandler.ut_player_unlock_all_steam_achievements = function(self, item)
        HT.Player:unlockAllSteamAchievements()
    end

    MenuCallbackHandler.ut_player_lock_all_steam_achievements = function(self, item)
        HT.Player:lockAllSteamAchievements()
    end

    MenuCallbackHandler.ut_unlocker_toggle_dlc_unlocker = function(self, item)
        local value = HT.Utils:getToggleValue(item:value())
        HT.Unlocker:setDlcUnlocker(value)
    end

    MenuCallbackHandler.ut_unlocker_toggle_skin_unlocker = function(self, item)
        local value = HT.Utils:getToggleValue(item:value())
        HT.Unlocker:setSkinUnlocker(value)
    end

    MenuCallbackHandler.ut_mission_access_cameras = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        managers.menu:close_all_menus()
        HT.Mission:accessCameras()
    end

    MenuCallbackHandler.ut_mission_remove_invisible_walls = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Mission:removeInvisibleWalls()
    end

    MenuCallbackHandler.ut_mission_convert_all_enemies = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Mission:convertAllEnemies()
    end

    MenuCallbackHandler.ut_mission_trigger_alarm = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Mission:triggerAlarm()
    end

    MenuCallbackHandler.ut_mission_toggle_disable_ai = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Mission:setDisableAi(value)
    end

    MenuCallbackHandler.ut_mission_toggle_invisible_player = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Mission:setInvisiblePlayer(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_noclip = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setNoclip(value, false)
    end

    MenuCallbackHandler.ut_dexterity_set_noclip_speed_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.noclipSpeedMultiplier = tonumber(item:value()) or 2
        HT:setSetting("noclip_speed_multiplier", HT.Dexterity.noclipSpeedMultiplier)
    end

    MenuCallbackHandler.ut_mission_toggle_instant_drilling = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Mission:setInstantDrilling(value)
    end

    MenuCallbackHandler.ut_mission_toggle_prevent_alarm_triggering = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Mission:setPreventAlarmTriggering(value)
    end

    MenuCallbackHandler.ut_mission_toggle_unlimited_pagers = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Mission:setUnlimitedPagers(value)
    end

    MenuCallbackHandler.ut_spawn_remove_npcs = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Spawn:removeNpcs()
    end

    MenuCallbackHandler.ut_spawn_remove_loots = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Spawn:removeLoots()
    end

    MenuCallbackHandler.ut_spawn_remove_equipments = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Spawn:removeEquipments()
    end

    MenuCallbackHandler.ut_spawn_remove_packages = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Spawn:removePackages()
    end

    MenuCallbackHandler.ut_spawn_remove_bags = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Spawn:removeBags()
    end

    MenuCallbackHandler.ut_spawn_dispose_corpses = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Spawn:disposeCorpses()
    end

    MenuCallbackHandler.ut_time_set_environment_early_morning = function(self, item)
        HT.Time:setEnvironmentSetting("environments/pd2_env_hox_02/pd2_env_hox_02")
    end

    MenuCallbackHandler.ut_time_set_environment_morning = function(self, item)
        HT.Time:setEnvironmentSetting("environments/pd2_env_morning_02/pd2_env_morning_02")
    end

    MenuCallbackHandler.ut_time_set_environment_mid_day = function(self, item)
        HT.Time:setEnvironmentSetting("environments/pd2_env_mid_day/pd2_env_mid_day")
    end

    MenuCallbackHandler.ut_time_set_environment_afternoon = function(self, item)
        HT.Time:setEnvironmentSetting("environments/pd2_env_afternoon/pd2_env_afternoon")
    end

    MenuCallbackHandler.ut_time_set_environment_night = function(self, item)
        HT.Time:setEnvironmentSetting("environments/pd2_env_n2/pd2_env_n2")
    end

    MenuCallbackHandler.ut_time_set_environment_misty_night = function(self, item)
        HT.Time:setEnvironmentSetting("environments/pd2_env_arm_hcm_02/pd2_env_arm_hcm_02")
    end

    MenuCallbackHandler.ut_time_set_environment_foggy_night = function(self, item)
        HT.Time:setEnvironmentSetting("environments/pd2_env_foggy_bright/pd2_env_foggy_bright")
    end

    MenuCallbackHandler.ut_time_reset_environment = function(self, item)
        HT.Time:resetEnvironment()
    end

    MenuCallbackHandler.ut_dexterity_toggle_god_mode = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setGodMode(value, true)
    end

    MenuCallbackHandler.ut_dexterity_toggle_infinite_stamina = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setInfiniteStamina(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_run_in_all_directions = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setRunInAllDirections(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_can_run_with_any_bag = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setCanRunWithAnyBag(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_fast_mask = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setFastMask(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_no_carry_cooldown = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setNoCarryCooldown(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_no_flashbangs = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setNoFlashbangs(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_instant_swap = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setInstantSwap(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_instant_reload = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setInstantReload(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_shoot_through_walls = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setShootThroughWalls(value, true)
    end

    MenuCallbackHandler.ut_dexterity_toggle_no_recoil = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setNoRecoil(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_no_spread = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setNoSpread(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_unlimited_ammo = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setUnlimitedAmmo(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_instant_interaction = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setInstantInteraction(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_instant_deployment = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setInstantDeployment(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_unlimited_equipment = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setUnlimitedEquipment(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_move_speed_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.enableMoveSpeedMultiplier = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setMoveSpeedMultiplier(
            HT.Dexterity.enableMoveSpeedMultiplier,
            HT.Dexterity.moveSpeedMultiplier
        )
    end

    MenuCallbackHandler.ut_dexterity_set_move_speed_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.moveSpeedMultiplier = tonumber(item:value()) or 1
        HT.Dexterity:setMoveSpeedMultiplier(
            HT.Dexterity.enableMoveSpeedMultiplier,
            HT.Dexterity.moveSpeedMultiplier
        )
    end

    MenuCallbackHandler.ut_dexterity_toggle_throw_distance_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.enableThrowDistanceMultiplier = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setThrowDistanceMultiplier(
            HT.Dexterity.enableThrowDistanceMultiplier,
            HT.Dexterity.throwDistanceMultiplier
        )
    end

    MenuCallbackHandler.ut_dexterity_set_throw_distance_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.throwDistanceMultiplier = tonumber(item:value()) or 1
        HT.Dexterity:setThrowDistanceMultiplier(
            HT.Dexterity.enableThrowDistanceMultiplier,
            HT.Dexterity.throwDistanceMultiplier
        )
    end

    MenuCallbackHandler.ut_dexterity_toggle_fire_rate_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.enableFireRateMultiplier = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setFireRateMultiplier(
            HT.Dexterity.enableFireRateMultiplier,
            HT.Dexterity.fireRateMultiplier
        )
    end

    MenuCallbackHandler.ut_dexterity_set_fire_rate_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.fireRateMultiplier = tonumber(item:value()) or 1
        HT.Dexterity:setFireRateMultiplier(
            HT.Dexterity.enableFireRateMultiplier,
            HT.Dexterity.fireRateMultiplier
        )
    end

    MenuCallbackHandler.ut_dexterity_toggle_damage_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.enableDamageMultiplier = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setDamageMultiplier(
            HT.Dexterity.enableDamageMultiplier,
            HT.Dexterity.damageMultiplier
        )
    end

    MenuCallbackHandler.ut_dexterity_set_damage_multiplier = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Dexterity.damageMultiplier = tonumber(item:value()) or 1
        HT.Dexterity:setDamageMultiplier(
            HT.Dexterity.enableDamageMultiplier,
            HT.Dexterity.damageMultiplier
        )
    end

    MenuCallbackHandler.ut_configuration_toggle_anti_cheat_checker = function(self, item)
        local value = HT.Utils:getToggleValue(item:value())
        HT.AntiCheatChecker:setEnabled(value)
    end

    MenuCallbackHandler.ut_configuration_anti_cheat_checker_show_list = function(self, item)
        HT.AntiCheatChecker:showList()
    end

    MenuCallbackHandler.ut_configuration_toggle_hide_mods_list = function(self, item)
        local value = HT.Utils:getToggleValue(item:value())
        HT:setHideModsList(value)
    end

    MenuCallbackHandler.ut_instant_start_heist = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Instant:startHeist()
    end

    MenuCallbackHandler.ut_menu_extras_item_1_callback = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Extras:collectGagePackages()
    end

    MenuCallbackHandler.ut_menu_extras_item_2_callback = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Extras:boardWindows()
    end

    MenuCallbackHandler.ut_menu_extras_item_3_callback = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Extras:openDepositBoxes()
    end

    MenuCallbackHandler.ut_menu_extras_item_4_callback = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Extras:tieCivilians()
    end

    MenuCallbackHandler.ut_menu_extras_item_5_callback = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end

        local draw_invisible_walls = HT.Utils:getToggleValue(item:value())
        HT.Extras:displayInvisibleWalls(draw_invisible_walls)
    end

    MenuCallbackHandler.ut_menu_extras_item_6_callback = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Extras:lockupAI()
    end

    MenuCallbackHandler.ut_menu_extras_item_7_callback = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Extras:releaseAI()
    end

    MenuCallbackHandler.ut_menu_extras_item_8_callback = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Extras:toggleCashPenalty(value)
    end

    MenuCallbackHandler.ut_instant_restart_heist = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Instant:restartHeist()
    end

    MenuCallbackHandler.ut_instant_finish_heist = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Instant:finishHeist()
    end

    MenuCallbackHandler.ut_instant_leave_heist = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Instant:leaveHeist()
    end

    MenuCallbackHandler.ut_mission_toggle_xray = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Mission:setXray(value)
    end

    MenuCallbackHandler.ut_mission_spawn_current_bag = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Mission:spawnBagOfCurrentType()
    end

    MenuCallbackHandler.ut_mission_give_heist_xp = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Mission:giveHeistXp()
    end

    MenuCallbackHandler.ut_dexterity_toggle_no_fall_damage = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setNoFallDamage(value)
    end

    MenuCallbackHandler.ut_mission_get_out_of_custody = function(self, item)
        HT.Mission:getOutOfCustody()
    end

    MenuCallbackHandler.ut_mission_auto_cook = function(self, item)
        HT.Mission:autoCook()
    end

    MenuCallbackHandler.ut_mission_toggle_auto_cook = function(self, item)
        HT.Mission:autoCook()
    end

    MenuCallbackHandler.ut_cook_toggle_announcements = function(self, item)
        HT.Mission:setCookOption("announcements", HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_cook_toggle_prevent_explosion = function(self, item)
        HT.Mission:setCookOption("prevent_explosion", HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_cook_toggle_auto_flare = function(self, item)
        HT.Mission:setCookOption("auto_flare_breaker", HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_cook_set_supply_location = function(self, item)
        HT.Mission:setCookOption("supply_location", tonumber(item:value()) or 1)
    end
    MenuCallbackHandler.ut_cook_set_lab_floor = function(self, item)
        HT.Mission:setCookOption("lab_floor", tonumber(item:value()) or 1)
    end
    MenuCallbackHandler.ut_cook_toggle_keep_twitch = function(self, item)
        HT.Mission:setCookOption("keep_twitch", HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_cook_toggle_spawn_twitch = function(self, item)
        HT.Mission:setCookOption("spawn_twitch_start", HT.Utils:getToggleValue(item:value()))
    end

    MenuCallbackHandler.ut_mission_kill_all_enemies = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        HT.Mission:killAllEnemies()
    end

    MenuCallbackHandler.ut_mission_toggle_freeze_civilians = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Mission:setFreezeCivilians(value)
    end

    MenuCallbackHandler.ut_mission_open_all_doors = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        HT.Mission:openAllDoors()
    end

    MenuCallbackHandler.ut_dexterity_toggle_one_shot_kill = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setOneShotKill(value)
    end

    MenuCallbackHandler.ut_configuration_toggle_disable_anti_cheat = function(self, item)
        local value = HT.Utils:getToggleValue(item:value())
        HT.AntiCheat:setEnabled(value)
    end

    -- No-op used by section-header rows (they're disabled, so this never fires,
    -- but BLT requires every button to have a valid callback).
    MenuCallbackHandler.ut_noop = function(self, item) end

    MenuCallbackHandler.ut_dexterity_toggle_melee_range = function(self, item)
        local value = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setMeleeRange(value)
    end

    MenuCallbackHandler.ut_dexterity_toggle_melee_damage_multiplier = function(self, item)
        HT.Dexterity.enableMeleeDamageMultiplier = HT.Utils:getToggleValue(item:value())
        HT.Dexterity:setMeleeDamageMultiplier(
            HT.Dexterity.enableMeleeDamageMultiplier,
            HT:getSetting("melee_damage_multiplier") or 10
        )
    end

    MenuCallbackHandler.ut_dexterity_set_melee_damage_multiplier = function(self, item)
        local multiplier = tonumber(item:value()) or 10
        -- Slider fires rapidly while dragging; suppress the per-tick alert (the
        -- false arg) so it doesn't spam the screen and the log.
        if HT:getSetting("enable_melee_damage_multiplier") then
            HT.Dexterity:setMeleeDamageMultiplier(true, multiplier, false)
        else
            HT:setSetting("melee_damage_multiplier", multiplier)
        end
    end

    MenuCallbackHandler.ut_mission_toggle_instant_convert = function(self, item)
        if not HT:isInHeist() then
            HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
            return
        end
        if not HT:isHost() then
            HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
            return
        end
        local value = HT.Utils:getToggleValue(item:value())
        HT.Mission:setInstantConvert(value)
    end

    MenuCallbackHandler.ut_mission_apply_all_perks = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Player:applyAllPerks()
    end

    -- ===== Extra features (pirate-perfection ports) =====
    local function ut_extra_heist(setter)
        return function(self, item)
            if not HT:isInHeist() then
                HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
                return
            end
            setter(HT.Utils:getToggleValue(item:value()))
        end
    end
    local function ut_extra_host(setter)
        return function(self, item)
            if not HT:isInHeist() then
                HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
                return
            end
            if not HT:isHost() then
                HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
                return
            end
            setter(HT.Utils:getToggleValue(item:value()))
        end
    end

    MenuCallbackHandler.ut_extra_toggle_no_delay_melee   = ut_extra_heist(function(v) HT.Extra:setNoDelayMelee(v) end)
    MenuCallbackHandler.ut_extra_toggle_high_jump        = ut_extra_heist(function(v) HT.Extra:setHighJump(v) end)
    MenuCallbackHandler.ut_extra_toggle_auto_revive      = ut_extra_heist(function(v) HT.Extra:setAutoRevive(v) end)
    MenuCallbackHandler.ut_extra_toggle_infinite_body_bags  = ut_extra_heist(function(v) HT.Extra:setInfiniteBodyBags(v) end)
    MenuCallbackHandler.ut_extra_toggle_infinite_cable_ties = ut_extra_heist(function(v) HT.Extra:setInfiniteCableTies(v) end)
    MenuCallbackHandler.ut_extra_toggle_cops_dont_shoot  = ut_extra_heist(function(v) HT.Extra:setCopsDontShoot(v) end)
    MenuCallbackHandler.ut_extra_toggle_disable_cameras  = ut_extra_heist(function(v) HT.Extra:setDisableCameras(v) end)
    MenuCallbackHandler.ut_extra_toggle_disable_pagers   = ut_extra_heist(function(v) HT.Extra:setDisablePagers(v) end)
    MenuCallbackHandler.ut_extra_toggle_prevent_panic_buttons = ut_extra_host(function(v) HT.Extra:setPreventPanicButtons(v) end)
    MenuCallbackHandler.ut_extra_toggle_non_consumable_equipment = ut_extra_host(function(v) HT.Extra:setNonConsumableEquipment(v) end)
    MenuCallbackHandler.ut_extra_secure_all_loot = function(self, item)
        HT.Extra:secureAllLoot()
    end
    MenuCallbackHandler.ut_extra_spawn_gage_package = function(self, item)
        HT.Extra:spawnGagePackage()
    end

    -- Half 2: QoL / preplanning (not heist-gated; some apply pre-heist)
    MenuCallbackHandler.ut_extra_toggle_disable_skills_check = function(self, item)
        HT.Extra:setDisableSkillsCheck(HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_extra_toggle_no_pause = function(self, item)
        HT.Extra:setNoPause(HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_extra_toggle_skip_intro = function(self, item)
        HT.Extra:setSkipIntro(HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_extra_toggle_free_preplanning = function(self, item)
        HT.Extra:setFreePreplanning(HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_extra_toggle_host_choose_plan = function(self, item)
        HT.Extra:setHostChoosePlan(HT.Utils:getToggleValue(item:value()))
    end
    MenuCallbackHandler.ut_extra_unlock_all_assets = function(self, item)
        HT.Extra:unlockAllAssets()
    end
    MenuCallbackHandler.ut_extra_buy_all_preplanning = function(self, item)
        HT.Extra:buyAllPreplanning()
    end
    MenuCallbackHandler.ut_extra_set_drill_time = function(self, item)
        HT:setSetting("drill_time", tonumber(item:value()) or 1)
    end
    MenuCallbackHandler.ut_extra_apply_drill_time = function(self, item)
        HT.Extra:applyDrillTime()
    end

    -- Team Menu actions are host-authoritative.
    local function ut_troll_button_host(action)
        return function(self, item)
            if not HT:isInHeist() then
                HT:addAlert("ut_alert_in_heist_only_feature", HT.colors.warning)
                return
            end
            if not HT:isHost() then
                HT:addAlert("ut_alert_host_only_feature", HT.colors.warning)
                return
            end
            action()
        end
    end

    MenuCallbackHandler.ut_troll_toggle_team_god_mode    = ut_extra_host(function(v) HT.TrollMenu:setTeamGodMode(v) end)
    MenuCallbackHandler.ut_troll_set_bag_type = function(self, item)
        HT:setSetting("troll_bag_type", item:value())
    end
    MenuCallbackHandler.ut_troll_set_equipment_type = function(self, item)
        HT:setSetting("troll_equipment_type", item:value())
    end
    MenuCallbackHandler.ut_troll_spawn_bags     = ut_troll_button_host(function() HT.TrollMenu:spawnBagsOnAll() end)
    MenuCallbackHandler.ut_troll_give_equipment = ut_troll_button_host(function() HT.TrollMenu:giveEquipmentToAll() end)
    MenuCallbackHandler.ut_team_set_target = function(self, item) HT.TrollMenu:setTeamTarget(item:value()) end
    MenuCallbackHandler.ut_team_toggle_fall_protection = ut_extra_host(function(v) HT.TrollMenu:setTeamFallProtection(v) end)
    MenuCallbackHandler.ut_team_revive = ut_troll_button_host(function() HT.TrollMenu:reviveSelected() end)
    MenuCallbackHandler.ut_team_release = ut_troll_button_host(function() HT.TrollMenu:releaseSelected() end)
    MenuCallbackHandler.ut_team_teleport = ut_troll_button_host(function() HT.TrollMenu:teleportSelected() end)
    MenuCallbackHandler.ut_team_give_ties = ut_troll_button_host(function() HT.TrollMenu:giveSelectedSupply("cable_tie") end)
    MenuCallbackHandler.ut_team_give_bodybags = ut_troll_button_host(function() HT.TrollMenu:giveSelectedSupply("body_bag") end)
    MenuCallbackHandler.ut_team_refill = ut_troll_button_host(function() HT.TrollMenu:giveSelectedSupply("refill") end)
    MenuCallbackHandler.ut_team_mark_enemies = ut_troll_button_host(function() HT.TrollMenu:markAllEnemies() end)
    MenuCallbackHandler.ut_team_disable_security = ut_troll_button_host(function() HT.TrollMenu:disableLobbySecurity() end)
    MenuCallbackHandler.ut_team_announcement = function(self, item)
        local message = item:value()
        item:set_value("")
        HT.TrollMenu:announce(message)
    end

    -- ===== Tools (Name Spoof / Interact For Team / Item Stealing) =====
    MenuCallbackHandler.ut_misc_set_name_spoof = function(self, item)
        HT.Misc:setNameSpoof(item:value())
    end
    MenuCallbackHandler.ut_misc_toggle_interact_for_team = function(self, item)
        HT.Misc:setInteractForTeam(HT.Utils:getToggleValue(item:value()))
    end
    -- Runs before the stock renderer displays Hex Trainer. This makes opening
    -- through BLT Options/pause menu use the same context filtering as F1.
    MenuCallbackHandler.ut_main_context_focus = function(self, node, in_focus)
        if not in_focus then return end
        local previous_name = ""
        pcall(function()
            local active = managers.menu and managers.menu:active_menu()
            local previous = active and active.logic and active.logic:selected_node()
            previous_name = previous and previous:parameters().name or ""
        end)
        HT:refreshMainMenuContext(previous_name, node)
    end
    MenuCallbackHandler.ut_misc_steal_loadouts = function(self, item)
        if not HT:isInGame() then
            HT:addAlert("ut_alert_in_game_only_feature", HT.colors.warning)
            return
        end
        HT.Misc:stealAllLoadouts()
    end

    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/main.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/player.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/preplanning.json", nil, HT.settings)
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/mission.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/dexterity.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/cookoff.json", nil, HT.settings)
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/spawn.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/instant.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/troll.json", nil, HT.settings)
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/extras.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/time.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/tools.json", nil, HT.settings)
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/unlocker.json", nil, HT.settings)
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/configuration.json", nil, HT.settings)

    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/level.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/infamy-rank.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/money.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/continental-coins.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/skill-points.json", nil, HT.settings)
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/perk-points.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/inventory.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/trophies.json")
    MenuHelper:LoadFromJsonFile(HT.modPath .. "/menus/steam-achievements.json")

    -- Reset all cheats when the heist is RESTARTED or ABORTED/TERMINATED (finish
    -- and leave are already handled by the mission-end / leave-heist hooks).
    pcall(function()
        local function ht_reset_on_leave()
            pcall(function()
                if HT.Reset and HT.Reset.all then HT.Reset:all(true) end
            end)
        end
        -- Abort / terminate heist (pause menu -> Abort -> yes).
        if MenuCallbackHandler then
            local name = "_dialog_end_game_yes"
            local orig = MenuCallbackHandler[name]
            if type(orig) == "function" and not MenuCallbackHandler["_ht_wrapped" .. name] then
                MenuCallbackHandler["_ht_wrapped" .. name] = true
                MenuCallbackHandler[name] = function(self, ...)
                    ht_reset_on_leave()
                    return orig(self, ...)
                end
            end
        end
        -- Restart heist actually goes through GamePlayCentralManager:restart_the_game.
        if _G.GamePlayCentralManager and not GamePlayCentralManager._ht_wrapped_restart then
            GamePlayCentralManager._ht_wrapped_restart = true
            _G.CloneClass(GamePlayCentralManager)
            function GamePlayCentralManager:restart_the_game(...)
                ht_reset_on_leave()
                return GamePlayCentralManager.orig.restart_the_game(self, ...)
            end
        end
    end)

    -- Stock SuperBLT theme. Keep the proven menu renderer, but give every Hex
    -- item a consistent palette and make section headers unmistakable.
    pcall(function()
        local color_text = Color("d7dde5")
        local color_cyan = Color("35bff4")
        local color_amber = Color("f0a51b")
        local color_danger = Color("e15b64")
        local color_danger_hot = Color("ff7b83")
        local dangerous = {
            "reset", "remove", "clear", "delete", "dispose", "lock_all",
            "send_custody", "finish_heist", "leave_heist", "restart_heist"
        }

        local function is_danger(params)
            local value = tostring(params.callback or "") .. " " ..
                tostring(params.name or "") .. " " .. tostring(params.id or "")
            for _, token in ipairs(dangerous) do
                if value:find(token, 1, true) then return true end
            end
            return false
        end

        local menus = MenuHelper.menus or {}
        for _, menu in pairs(menus) do
            if type(menu) == "table" and menu._items_list then
                for _, item in ipairs(menu._items_list) do
                    local ok, params = pcall(function() return item:parameters() end)
                    if ok and params and type(params.name) == "string" and params.name:find("^ut_header_") then
                        params.disabled_color = color_amber
                        params.row_item_color = color_amber
                        params.hightlight_color = color_amber
                        params.highlight_color = color_amber
                        pcall(function() item:set_enabled(false) end)
                    elseif ok and params and type(params.name) == "string" and params.name:find("^ut_") then
                        if is_danger(params) then
                            params.row_item_color = color_danger
                            params.hightlight_color = color_danger_hot
                            params.highlight_color = color_danger_hot
                            params.marker_color = color_danger_hot
                        else
                            params.row_item_color = color_text
                            params.hightlight_color = color_cyan
                            params.highlight_color = color_cyan
                            params.marker_color = color_cyan
                        end
                    end
                end
            end
        end
    end)

end)

local packageManagerMetaTable = getmetatable(PackageManager)
local _script_data = packageManagerMetaTable.script_data

local ids_menu = Idstring("menu")
local ids_menus = {
    [Idstring("gamedata/menus/start_menu")] = true,
    [Idstring("gamedata/menus/pause_menu")] = true,
}
packageManagerMetaTable.script_data = function(self, typeId, pathId, ...)
    local scriptData = _script_data(self, typeId, pathId, ...)
    if typeId ~= ids_menu and not ids_menus[pathId] then
        return scriptData
    end

    for key, value in ipairs(scriptData[1]) do
        for key2, value2 in ipairs(value) do
            if value2.name == "options" then
                table.insert(scriptData[1][key], key2 + 1, {
                    name = "ut_open_menu_main",
                    text_id = "ut_menu_main_title",
                    help_id = "ut_menu_main_description",
                    next_node = "ut_main_menu",
                    _meta = "item",
                })
                break
            end
        end
    end
    return scriptData
end

if HT:getSetting("enable_hide_mods_list") then
    function MenuCallbackHandler:is_modded_client()
        return false
    end

    function MenuCallbackHandler:is_not_modded_client()
        return true
    end

    function MenuCallbackHandler:build_mods_list()
        return {}
    end
end
