local re = re local sdk = sdk local d2d = d2d local imgui = imgui local log = log local json = json local draw = draw local require = require local tostring = tostring local pairs = pairs local ipairs = ipairs local math = math local string = string local table = table local type = type local Core = require("_CatLib") local CONST = require("_CatLib.const") local Imgui = require("_CatLib.imgui") local mod = Core.NewMod("Auto Meal") mod.EnableCJKFont(18) if mod.Config.CurrentSet == nil then mod.Config.CurrentSet = -1 end if mod.Config.FallbackAnotherWeapon == nil then mod.Config.FallbackAnotherWeapon = true end if mod.Config.WeaponTypeConfig == nil then mod.Config.WeaponTypeConfig = {} for _ = 0, 14-1, 1 do table.insert(mod.Config.WeaponTypeConfig, -1) end end if mod.Config.GUIDelaySeconds == nil then mod.Config.GUIDelaySeconds = 3.0 end if mod.Config.EquipMySetConfig == nil then mod.Config.EquipMySetConfig = {} end if mod.Config.ConsumeItems == nil then mod.Config.ConsumeItems = true end if mod.Config.NotifyNoEnoughItems == nil then mod.Config.NotifyNoEnoughItems = true end if mod.Config.NotifyNoEnoughItemDetails == nil then mod.Config.NotifyNoEnoughItemDetails = true end if mod.Config.SystemLog == nil then mod.Config.SystemLog = true mod.Config.MealUI = true end local RequestUTCTime = 0 local RequestMealSetIndex = -1 local RequestMessage = "" local NOTIFICATION_MSG = { [CONST.LanguageType.English] = { FromDefault = "Apply default meal set: %s", FromOption = "Apply meal set: %s [%s]", ManualEat = "Manual", UseDefaultSetting = "Use Default Setting", InvalidSetting = "Auto Meal: Invalid Setting", NotEnough = "Food not enough.", NotEnoughDetail = "Food not enough, %s.", }, [CONST.LanguageType.SimplifiedChinese] = { FromDefault = "已享用默认烹饪组合:%s", FromOption = "已享用烹饪组合:%s%s】", ManualEat = "手动", UseDefaultSetting = "使用默认设置", InvalidSetting = "Auto Meal: 无效设置", NotEnough = "食材数量不足。", NotEnoughDetail = "食材数量不足:%s。", }, [CONST.LanguageType.TraditionalChinese] = { FromDefault = "已享用默认烹飪組合:%s", FromOption = "已享用烹飪組合:%s%s】", ManualEat = "手动", UseDefaultSetting = "使用默认设置", InvalidSetting = "Auto Meal: 無效設定", NotEnough = "食材數量不足。", NotEnoughDetail = "食材數量不足:%s。", }, } -- app.FacilityUtil isEnoughItem(app.ItemDef.ID, System.Int16, app.ItemUtil.STOCK_TYPE) local function Localized() local lang = Core.GetLanguage() if not NOTIFICATION_MSG[lang] then lang = CONST.LanguageType.English end return NOTIFICATION_MSG[lang] end -- app.savedata.cEquipIndex ---@param equipParam app.savedata.cEquipParam ---@param myset app.savedata.cEquipMyset local function EquipSetEquals(equipParam, myset) local usingIndex = equipParam._EquipIndex -- TODO: 这里并没有 Weapon type,在哪区分的? local box = equipParam._EquipBox ---@type app.savedata.cMysetEquipWork[] local mysetWorks = myset._MysetEquipWork ---@type app.savedata.cEquipIndex local mysetIndex = myset._MysetParam._EquipIndex local usingIndexArray = usingIndex.Index local mysetIndexArray = mysetIndex.Index local len = usingIndexArray:get_Count() if len ~= mysetIndexArray:get_Count() then return false end for i = 0, len-1, 1 do if usingIndexArray:get_Item(i) ~= mysetIndexArray:get_Item(i) then return false end end if len ~= mysetWorks:get_Count() then return false end for i = 0, len-1, 1 do local idx = usingIndexArray:get_Item(i) if idx == -1 then goto continue end local usingWork = box:get_Item(idx) local mysetWork = mysetWorks:get_Item(i) if usingWork.Category ~= mysetWork.Category then return false end if usingWork.Category == -1 then goto continue end if usingWork.GenderType ~= mysetWork.GenderType then return false end if usingWork.FreeVal0 ~= mysetWork.FreeVal0 then return false end if usingWork.FreeVal1 ~= mysetWork.FreeVal1 then return false end -- TODO:FIXME using 有 FreeVal2/FreeVal3;Myset 有 InsectId/IsNone -- if usingWork.FreeVal2 ~= mysetWork.FreeVal2 then -- return false -- end if not Core.CSharpIntArrayEquals(usingWork.EquipmentAccessoryIdArray, mysetWork.EquipmentAccessoryIdArray) then return false end if not Core.CSharpIntArrayEquals(usingWork.BowgunCustomizeId, mysetWork.BowgunCustomizeId) then return false end ::continue:: end return true end -- cMealMySet local MealMySetUtil = Core.WrapTypedef("app.MealMySetUtil") local MealUtil = Core.WrapTypedef("app.MealUtil") local ChatLogUtil = Core.WrapTypedef("app.ChatLogUtil") local ItemUtil = Core.WrapTypedef("app.ItemUtil") local ItemIDFixedToID = Core.TypeField("app.ItemDef", "FixedToID"):get_data() local ItemUtil_StockTypePouch = 0 local ItemUtil_StockTypeBox = 1 local ItemUtil_StockTypeBoth = 2 local ItemUtil_StockTypeBoth_PouchBox = 3 local ItemUtil_StockTypeBoth_BoxPouch = 4 ---@return app.savedata.cMealMySet local function GetMealMySet(index, requireValid) local data = MealMySetUtil:StaticCall("getMySetData(System.Int32)", index) if requireValid == true then if data == nil then log.error(string.format("Meal at %d is nil", index)) Core.SendMessage(string.format("Meal at %d is nil", index)) return nil end if not data:isValidData() then local msgCfg = Localized() Core.SendMessage(msgCfg.InvalidSetting) return nil end end return data end local MealStatusGUI = nil local function InitGUI() if mod.Config.MealUI and MealStatusGUI == nil then local comps = Core.FindComponents("app.GUI090201") if comps:get_Count() <= 0 then return end MealStatusGUI = comps:get_Item(0) end end -- DO NOT CALL THIS DIRECTLY local function ShowMealStatus(mealEffect) if not mod.Config.MealUI then return end InitGUI() if not MealStatusGUI then return end if mealEffect then MealStatusGUI:setupParameters(mealEffect) -- DO NOT CALL THIS DIRECTLY OR GAME WILL CRASH MealStatusGUI:requestOpen() end end local function RequestMeal(idx, msg) RequestMealSetIndex = idx RequestMessage = msg RequestUTCTime = Core.GetTime() + mod.Config.GUIDelaySeconds InitGUI() if MealStatusGUI ~= nil then MealStatusGUI:set__Timer(3.0) -- indicates it needs to be shown end end -- 我不知道为什么,但如果预先生成全局变量,那么接取并出发会报错(第一次不会),但是手动、接取但不出发不会报错 ---@param idx number custom meal set index ---@return app.cMealEffect local function MakeMealEffect(idx) if idx < 0 then return end local meal = GetMealMySet(idx) local mysetUse = Core.Ctor("app.cMealMySetUse") mysetUse:fromSaveFormat(meal) -- 基础食材 PORTABLE_FOOD + 主食物 + 副食物 -- app.user_data.MealData.cData local mealData = Core.GetVariousDataManager()._Setting._FacilitySetting._FacilityDiningSetting._MealData local data = mealData:getDataByIndex(mysetUse:get_PortableFoodType()) local name = Core.GetItemName(mysetUse:get_PortableFoodType()) -- Core.SendMessage("主食材:" .. tostring(name)) local mealEffect = Core.Ctor("app.cMealEffect") -- nobody knows the bool arg, but it was false 3tims and true at last call mealEffect:copyFrom(data, true) local ids = {7} -- 7 携带食料 local itemIDs = mysetUse:get_AddFoods() local foodCount = itemIDs:get_Count() if foodCount > 0 then Core.ForEach(itemIDs, function (id) local food = MealUtil:StaticCall("getFoodDataFromItemId(app.ItemDef.ID)", id) if mod.Config.Debug then Core.SendMessage("SubFood " .. tostring(Core.GetItemName(id))) end mealEffect:addParam(food, true) -- 预览的话就是 false table.insert(ids, id) end) end return mealEffect, ids end mod.HookFunc("app.GUIManager", "update()", function () if RequestUTCTime <= 0 then return end local cur = Core.GetTime() if cur < RequestUTCTime then return end RequestUTCTime = 0 if Core.IsLoading() then return end local idx = RequestMealSetIndex local msg = RequestMessage RequestMealSetIndex = -1 -- make sure no constant call if exeption thrown RequestMessage = "" local meal, itemIDs = MakeMealEffect(idx) if not meal then return end local hunter = Core.GetPlayerCharacter() if not hunter then return end -- setMealEffect throws exception?? local enough = true local s = {} if mod.Config.ConsumeItems then for _, id in pairs(itemIDs) do local haveNum = ItemUtil:StaticCall("getItemNum(app.ItemDef.ID, app.ItemUtil.STOCK_TYPE)", id, ItemUtil_StockTypeBoth) if haveNum <= 0 then enough = false if mod.Config.NotifyNoEnoughItems and mod.Config.NotifyNoEnoughItemDetails then local name = Core.GetItemName(id) if mod.Config.Debug then Core.SendMessage("%s wants %d, has %d", name, 1, haveNum) end s[#s+1] = name else break end end end end if enough then hunter:get_HunterStatus()._MealEffect:setMealEffect(meal, true) if mod.Config.ConsumeItems then for _, id in pairs(itemIDs) do ItemUtil:StaticCall("changeItemNum(app.ItemDef.ID, System.Int16, app.ItemUtil.STOCK_TYPE)", id, -1, ItemUtil_StockTypeBoth_BoxPouch) end end ShowMealStatus(meal) if mod.Config.SystemLog then Core.SendMessage(msg) end else if mod.Config.NotifyNoEnoughItems then if mod.Config.NotifyNoEnoughItemDetails then Core.SendMessage(Localized().NotEnoughDetail, table.concat(s,", ")) else Core.SendMessage(Localized().NotEnough) end end end end) Core.OnQuestStartEnter(function() if MealStatusGUI ~= nil and MealStatusGUI:get__Timer() > 2 then RequestUTCTime = Core.GetTime() + mod.Config.GUIDelaySeconds end end) local function AutoApplyMeal(setIndex) local msg local msgCfg = Localized() local mgr = Core.GetSaveDataManager() local equipMysetName if setIndex ~= nil then equipMysetName = msgCfg.ManualEat elseif setIndex == nil and mgr ~= nil then local save = mgr:getCurrentUserSaveData() -- app.savedata.cUserSaveParam local equipParam = save:get_Equip() -- app.savedata.cEquipParam local equipMyset = equipParam:get_EquipMySet() -- app.savedata.cEquipMyset[] local equipIndex = equipParam._EquipIndex -- app.savedata.cEquipIndex for i, equipSetIndex in pairs(mod.Config.EquipMySetConfig) do local myset = equipMyset:get_Item(i-1) if myset._MysetParam.MysetName == "" then goto continue end if EquipSetEquals(equipParam, myset) then setIndex = equipSetIndex equipMysetName = myset._MysetParam.MysetName break end ::continue:: end end if setIndex ~= nil then local data = GetMealMySet(setIndex, true) if not data then return end msg = string.format(msgCfg.FromOption, data.Name, equipMysetName) else local weaponType = Core.GetPlayerWeaponType() local wpSetIndex = mod.Config.WeaponTypeConfig[weaponType+1] if wpSetIndex == -1 and mod.Config.FallbackAnotherWeapon then weaponType = Core.GetPlayerSubWeaponType() wpSetIndex = mod.Config.WeaponTypeConfig[weaponType+1] end if wpSetIndex == -1 then setIndex = mod.Config.CurrentSet local data = GetMealMySet(setIndex, true) if not data then return end msg = string.format(msgCfg.FromDefault, data.Name) else setIndex = wpSetIndex local data = GetMealMySet(setIndex, true) if not data then return end msg = string.format(msgCfg.FromOption, data.Name, Core.GetWeaponTypeName(weaponType)) end end RequestMeal(setIndex, msg) end Core.OnAcceptQuest(function () AutoApplyMeal() end) local MYSET_MAX = Core.TypeField("app.MealDef", "MYSET_MAX"):get_data() or 40 local MealMySetNames = {} local function InitMealMySetNames() local firstMealIndex = -1 for i = 0, MYSET_MAX - 1, 1 do local myset = GetMealMySet(i) -- cMealMySet if myset ~= nil and myset:isValidData() then if firstMealIndex == -1 then firstMealIndex = i end MealMySetNames[i] = myset:get_field("Name") -- if mod.Config.Debug then -- -- mealsData[i] = myset -- local msg = "Name " .. MealMySetNames[i] .. ": ".. tostring(myset:get_field("BaseFoodType")) -- local adds = myset:get_field("AddFood") -- local len = adds:call("get_Count") -- for j = 0, len - 1, 1 do -- local addFood = adds:call("get_Item", j) -- msg = msg .. ", " .. tostring(addFood) -- end -- imgui.text(msg) -- end end end MealMySetNames[-1] = Localized().UseDefaultSetting return firstMealIndex end mod.Menu(function () local configChanged = false local changed = false changed, mod.Config.ConsumeItems = imgui.checkbox("Consume Items", mod.Config.ConsumeItems) configChanged = configChanged or changed Imgui.Tree("Notification and Dispaly", function () changed, mod.Config.NotifyNoEnoughItems = imgui.checkbox("Notify No Enough Items", mod.Config.NotifyNoEnoughItems) configChanged = configChanged or changed changed, mod.Config.NotifyNoEnoughItemDetails = imgui.checkbox("Notify No Enough Item Names", mod.Config.NotifyNoEnoughItemDetails) configChanged = configChanged or changed changed, mod.Config.MealUI = imgui.checkbox("Display Meal UI", mod.Config.MealUI) configChanged = configChanged or changed if mod.Config.MealUI then changed, mod.Config.GUIDelaySeconds = imgui.slider_float("GUI Delay Seconds", mod.Config.GUIDelaySeconds, 0.0, 6.0) configChanged = configChanged or changed end changed, mod.Config.SystemLog = imgui.checkbox("Display System Notification", mod.Config.SystemLog) configChanged = configChanged or changed end) -- local mealsData = {} local firstMealIndex = InitMealMySetNames() if firstMealIndex == -1 then imgui.text("No meal set") return end Imgui.Rect(function () mod.Runtime.ManualSet = mod.Runtime.ManualSet or mod.Config.CurrentSet Imgui.Button("Apply this meal:", function () AutoApplyMeal(mod.Runtime.ManualSet) RequestUTCTime = 1 end) imgui.same_line() _, mod.Runtime.ManualSet = imgui.combo("Manual Eat This", mod.Runtime.ManualSet, MealMySetNames) end) imgui.text() if imgui.button("Apply Auto Meal Now") then AutoApplyMeal() RequestUTCTime = 1 end if mod.Config.CurrentSet == -1 then mod.Config.CurrentSet = firstMealIndex end changed, mod.Config.CurrentSet = imgui.combo("Default Set", mod.Config.CurrentSet, MealMySetNames) configChanged = configChanged or changed -- imgui.text("Current Set: " .. tostring(mod.Config.CurrentSet)) if mod.Config.Debug then if mod.Config.MealUI then InitGUI() imgui.text("Timer: " .. tostring(MealStatusGUI:get__Timer())) end imgui.text("UTCTime: " ..tostring(Core.GetTime())) imgui.text("Req UTCTime: " ..tostring(RequestUTCTime)) end if imgui.tree_node("Weapon Type") then changed, mod.Config.FallbackAnotherWeapon = imgui.checkbox("Use Another Weapon If Main Weapon is Default", mod.Config.FallbackAnotherWeapon) configChanged = configChanged or changed local wpType = Core.GetPlayerWeaponType() local wp2Type = Core.GetPlayerSubWeaponType() for i = 0, 14-1, 1 do local weaponName = Core.GetWeaponTypeName(i) local opened = imgui.tree_node(weaponName) if mod.Config.WeaponTypeConfig[i+1] ~= -1 then imgui.same_line() imgui.text(MealMySetNames[mod.Config.WeaponTypeConfig[i+1]]) end if i == wpType then imgui.same_line() imgui.text("#1 weapon type") end if i == wp2Type then imgui.same_line() imgui.text("#2 weapon type") end if opened then changed, mod.Config.WeaponTypeConfig[i+1] = imgui.combo(weaponName, mod.Config.WeaponTypeConfig[i+1], MealMySetNames) configChanged = configChanged or changed imgui.tree_pop() end end imgui.tree_pop() end if imgui.tree_node("Equipment Loadout") then local mgr = Core.GetSaveDataManager() local save = mgr:getCurrentUserSaveData() -- app.savedata.cUserSaveParam local equipParam = save:get_Equip() -- app.savedata.cEquipParam local equipMyset = equipParam:get_EquipMySet() -- app.savedata.cEquipMyset[] Core.ForEach(equipMyset, function (myset, i) local name = myset._MysetParam.MysetName if name == "" then mod.Config.EquipMySetConfig[i+1] = nil return end -- EquipMySetData[i] = myset -- EquipMySetNameMap[i] = name if mod.Config.EquipMySetConfig[i+1] == nil then mod.Config.EquipMySetConfig[i+1] = -1 end local equipWorks = myset._MysetEquipWork -- 0 and 7: EQUIP_INDEX WEAPON, RESERVE_WEAPON local wpMsg = string.format("%s|%s", Core.GetWeaponTypeName(equipWorks:get_Item(0).FreeVal0), Core.GetWeaponTypeName(equipWorks:get_Item(7).FreeVal0)) local opened = imgui.tree_node(name .. ": " .. wpMsg) if mod.Config.EquipMySetConfig[i+1] ~= -1 then imgui.same_line() imgui.text(MealMySetNames[mod.Config.EquipMySetConfig[i+1]]) end if EquipSetEquals(equipParam, myset) then imgui.same_line() imgui.text("using") end if opened then changed, mod.Config.EquipMySetConfig[i+1] = imgui.combo(name, mod.Config.EquipMySetConfig[i+1], MealMySetNames) configChanged = configChanged or changed imgui.tree_pop() end end) imgui.tree_pop() end return configChanged end)