local SocialBuildingSource = CS.Beyond.Gameplay.Factory.SocialBuildingSource local GeneralAbilityType = GEnums.GeneralAbilityType local AbilityState = CS.Beyond.Gameplay.GeneralAbilitySystem.AbilityState local FactoryUtils = {} function FactoryUtils.curPowerIsEnough() return not FactoryUtils.getCurRegionPowerInfo().isStopByPower end function FactoryUtils.getBuildingStateType(nodeId) local spBuilding = GameInstance.player.facSpMachineSystem:GetNode(nodeId) if spBuilding and spBuilding:IsIdle() then return GEnums.FacBuildingState.Idle end return GameInstance.remoteFactoryManager:QueryBuildingState(Utils.getCurrentChapterId(), nodeId, false) end function FactoryUtils.getCraftNeedTime(craftData) local formulaGroupId = craftData.formulaGroupId local machineCraftGroupData = Tables.factoryMachineCraftGroupTable:GetValue(formulaGroupId) return craftData.progressRound * machineCraftGroupData.msPerRound * 0.001 end function FactoryUtils.getCurHubNodeId() local id = GameInstance.player.facSpMachineSystem:GetCurHubNodId() return id > 0 and id or nil end function FactoryUtils.getPowerText(power, isEnergy) local unit = isEnergy and Language.LUA_FAC_POWER_UNIT or Language.LUA_FAC_MACHINE_CONSUME_POWER_UNIT return UIUtils.getNumString(power) .. unit end function FactoryUtils.getBuildingStateIconName(nodeId, state) state = state or FactoryUtils.getBuildingStateType(nodeId) return UIConst.UI_SPRITE_FAC_BUILDING_COMMON, FacConst.FAC_BUILDING_STATE_TO_SPRITE[state] end function FactoryUtils.getItemProductivityPerMinus(itemId) return 0 end function FactoryUtils.isBuilding(itemId) if string.isEmpty(itemId) then return false end local valid, data = Tables.factoryBuildingItemTable:TryGetValue(itemId) return valid, valid and data.buildingId or nil end function FactoryUtils.isLogistic(itemId) if string.isEmpty(itemId) then return false end local valid, data = Tables.factoryItem2LogisticIdTable:TryGetValue(itemId) return valid, valid and data.logisticId or nil end function FactoryUtils.isInBuildMode() local opened, ctrl = UIManager:IsOpen(PanelId.FacBuildMode) if opened then return ctrl.m_mode ~= FacConst.FAC_BUILD_MODE.Normal, ctrl.m_mode else return false end end function FactoryUtils.isMovingBuilding() local opened, ctrl = UIManager:IsOpen(PanelId.FacBuildMode) if opened then return ctrl.m_buildingNodeId ~= nil else return false end end function FactoryUtils.getBuildingNodeHandler(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() return CSFactoryUtil.GetNodeHandlerByNodeId(nodeId, chapterId) end function FactoryUtils.isPendingBuildingNode(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() local slotId = CSFactoryUtil.GetBlueprintSlotId(chapterId, nodeId) return slotId > 0 end function FactoryUtils.getPendingBuildingNodeSlotId(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() local slotId = CSFactoryUtil.GetBlueprintSlotId(chapterId, nodeId) if slotId > 0 then return slotId else return end end function FactoryUtils.getPendingSlotName(slotId) return Language["LUA_FAC_BLUEPRINT_PENDING_NAME_" .. slotId] end function FactoryUtils.getBuildingComponentHandler(componentId) return CSFactoryUtil.GetComponentHandlerByComponentId(componentId) end function FactoryUtils.getBuildingComponentHandlerAtPos(syncNode, cptPos) local cpt = syncNode:GetComponentInPosition(cptPos:GetHashCode()) if cpt then return FactoryUtils.getBuildingComponentHandler(cpt.componentId) end return nil end function FactoryUtils.getBuildingComponentPayload_Social(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() return CSFactoryUtil.GetBuildingComponentPayload_Social(nodeId, chapterId) end function FactoryUtils.isSocialBuilding(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() return CSFactoryUtil.IsSocialBuilding(nodeId, chapterId) end function FactoryUtils.isOthersSocialBuilding(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() return CSFactoryUtil.IsOthersSocialBuilding(nodeId, chapterId) end function FactoryUtils.getSocialBuildingSource(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() return CSFactoryUtil.GetSocialBuildingSource(nodeId, chapterId) end function FactoryUtils.getSocialBuildingDetails(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() local social, source = CSFactoryUtil.GetSocialBuildingDetails(nodeId, chapterId) return social, source end function FactoryUtils.getBuildingSocialSourceInfo(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() local social, source = CSFactoryUtil.GetSocialBuildingDetails(nodeId, chapterId) local isOthers = source == SocialBuildingSource.Others local iePreset = false if social then iePreset = social.preset end return isOthers, iePreset end function FactoryUtils.getSocialBuildingStability(nodeId, chapterId) chapterId = chapterId or Utils.getCurrentChapterId() return CSFactoryUtil.GetSocialBuildingStability(nodeId, chapterId) end function FactoryUtils.isSocialBuildingTechLayerUnlocked(buildingId) local buildingData = Tables.factoryBuildingTable:GetValue(buildingId) if buildingData.type ~= GEnums.FacBuildingType.Battle then return nil, true end local success, techId = CSFactoryUtil.TryGetTechId(buildingId) if not success then logger.error("[Factory] Get building tech id failed, buildingId: " .. tostring(buildingId)) return nil, false end local techData = Tables.facSTTNodeTable:GetValue(techId) local techLayerId = techData.layer local isLocked = GameInstance.player.facTechTreeSystem:LayerIsLocked(techLayerId) return techLayerId, not isLocked end function FactoryUtils.canMoveBuilding(nodeId, needToast) local node = FactoryUtils.getBuildingNodeHandler(nodeId) if not node then return false end local isMoveLocked = CSFactoryUtil.CheckIsBuildingMoveAndDelLocked(node.templateId, node.instKey, needToast == true) if isMoveLocked then return false end local isOthersSocialBuilding = FactoryUtils.isOthersSocialBuilding(nodeId) if isOthersSocialBuilding then return false end local pdp = node.predefinedParam if not pdp then return true end if not pdp.common then return true end if pdp.common.forbidMove then if needToast then Notify(MessageConst.SHOW_TOAST, Language.LUA_FACTORY_BUILDING_MOVE_NOT_ALLOWED) end return false end return true end function FactoryUtils.canDelBuilding(nodeId, needToast) if FactoryUtils.isPendingBuildingNode(nodeId) then return end local node = FactoryUtils.getBuildingNodeHandler(nodeId) if not node then return false end local _, bData = Tables.factoryBuildingTable:TryGetValue(node.templateId) if bData and not bData.canDelete then return false end local isDelLocked = CSFactoryUtil.CheckIsBuildingMoveAndDelLocked(node.templateId, node.instKey, needToast == true) if isDelLocked then return false end local pdp = node.predefinedParam if not pdp then return true end if not pdp.common then return true end if pdp.common.forbidDelete then if needToast then Notify(MessageConst.SHOW_TOAST, Language.LUA_FACTORY_BUILDING_DELETE_NOT_ALLOWED) end return false end return true end function FactoryUtils.delBuilding(nodeId, onComplete, noConfirm, hintText) local clearAct local canDelete = FactoryUtils.canDelBuilding(nodeId, true) if not canDelete then return end local delBuildingAct = function() if clearAct then clearAct() end GameInstance.player.remoteFactory.core:Message_OpDismantle(Utils.getCurrentChapterId(), nodeId, function() if onComplete then onComplete() end end) end local isOthersSocialBuilding = FactoryUtils.isOthersSocialBuilding(nodeId) if isOthersSocialBuilding then noConfirm = false hintText = Language.LUA_FAC_DEL_SOCIAL_BUILDING_CONFIRM end if noConfirm then delBuildingAct() else if hintText == nil or hintText == "" then hintText = Language.LUA_FAC_ASK_DELETE_BUILDING end Notify(MessageConst.SHOW_POP_UP, { content = hintText, hideBlur = true, onCancel = clearAct, onConfirm = delBuildingAct, }) end end function FactoryUtils.canShareBuilding(nodeId) if Utils.isInBlackbox() then return false end if FactoryUtils.isPendingBuildingNode(nodeId) then return false end local node = FactoryUtils.getBuildingNodeHandler(nodeId) if not node then return false end local isSocialBuilding = FactoryUtils.isSocialBuilding(nodeId) if not isSocialBuilding then return false end return true end function FactoryUtils.canReportSocialBuilding(nodeId) local social, source = FactoryUtils.getSocialBuildingDetails(nodeId) local isValidSource = source == SocialBuildingSource.Others and not social.preset if not isValidSource then return false end local node = FactoryUtils.getBuildingNodeHandler(nodeId) local buildingId = node.templateId local socialBuildingData = FactoryUtils.getSocialBuildingData(buildingId) if not socialBuildingData then return false end return socialBuildingData.canReport end function FactoryUtils.reportSocialBuilding(nodeId, fnValidateOnCallback) if not FactoryUtils.canReportSocialBuilding(nodeId) then return end local chapterId = Utils.getCurrentChapterId() local social, source = FactoryUtils.getSocialBuildingDetails(nodeId, chapterId) local ownerId = social.ownerId GameInstance.player.friendSystem:SyncSocialFriendInfo({ ownerId }, function() if fnValidateOnCallback and not fnValidateOnCallback() then return end local success, ownerInfo = GameInstance.player.friendSystem:TryGetFriendInfo(ownerId) if not success then logger.info(ELogChannel.Factory, "ReportSocialBuilding: Owner info not found, roleId: " .. tostring(ownerId)) end UIManager:Open(PanelId.ReportPlayer, { reportType = FriendUtils.ReportGroupType.SocialBuilding, roleId = ownerId, socialBuildingParam = { chapterId = Utils.getCurrentChapterId(), nodeId = nodeId, }, }) end) end function FactoryUtils.canLikeSocialBuilding(nodeId, needToast) local isOthersSocialBuilding = FactoryUtils.isOthersSocialBuilding(nodeId) if not isOthersSocialBuilding then return false end return not FactoryUtils.isLikedSocialBuilding(nodeId, needToast) end function FactoryUtils.isLikedSocialBuilding(nodeId, needToast) local social = FactoryUtils.getBuildingComponentPayload_Social(nodeId) local lastSetLikeTs = social.lastSetLikeTs local currentRefreshTs = Utils.getCurrentCommonServerRefreshTime() local isLiked = lastSetLikeTs >= currentRefreshTs if needToast and isLiked then Notify(MessageConst.SHOW_TOAST, Language.LUA_FAC_LIKE_SOCIAL_BUILDING_ALREADY_DONE) end return isLiked end function FactoryUtils.likeSocialBuilding(nodeId, callback) if not FactoryUtils.canLikeSocialBuilding(nodeId, true) then return end local chapterId = Utils.getCurrentChapterId() GameInstance.player.remoteFactory.core:Message_SetSocialLike(chapterId, nodeId, callback) end function FactoryUtils.updateBuildingLikeAbilityState(nodeId) local abilityState = FactoryUtils.canLikeSocialBuilding(nodeId) and AbilityState.Idle or AbilityState.ForbiddenUse GameInstance.player.generalAbilitySystem:SwitchAbilityStateByType(GeneralAbilityType.BuildingLike, abilityState) end function FactoryUtils.getItemBuildingData(itemId) local succ, buildingItemData = Tables.factoryBuildingItemTable:TryGetValue(itemId) if not succ then return end local buildingData = Tables.factoryBuildingTable:GetValue(buildingItemData.buildingId) return buildingData end function FactoryUtils.getItemBuildingId(itemId) local succ, buildingItemData = Tables.factoryBuildingItemTable:TryGetValue(itemId) if not succ then return end return buildingItemData.buildingId end function FactoryUtils.getBuildingItemData(buildingId, noError) local succ, buildingItemData = Tables.factoryBuildingItemReverseTable:TryGetValue(buildingId) if not succ then if not noError then logger.error("策划配错了,建筑没有对应道具", buildingId) end return end local itemData = Tables.itemTable:GetValue(buildingItemData.itemId) return itemData end function FactoryUtils.getBuildingItemId(buildingId) if not buildingId then return nil end local succ, buildingItemData = Tables.factoryBuildingItemReverseTable:TryGetValue(buildingId) if succ then return buildingItemData.itemId end end function FactoryUtils.getSocialBuildingData(buildingId) local success, buildingData = Tables.factoryBuildingTable:TryGetValue(buildingId) if not success then return end local nodeType success, nodeType = CSFactoryUtil.GetFCNodeType(buildingData.type) if not success then return end local socialBuildingData success, socialBuildingData = Tables.factorySocialBuildingTable:TryGetValue(nodeType) return socialBuildingData end function FactoryUtils.isItemSocialBuilding(itemId) local result = false local buildingId = FactoryUtils.getItemBuildingId(itemId) if buildingId then local socialBuildingData = FactoryUtils.getSocialBuildingData(buildingId) if socialBuildingData then result = socialBuildingData.isSocialBuilding end end return result end function FactoryUtils.getCurBuildingConsumePower(nodeId) local node = FactoryUtils.getBuildingNodeHandler(nodeId) local powerCost = FactoryUtils.getBuildingConsumePower(node.templateId) local powerObj = node.power if powerObj then if node.power.powerCost then powerCost = node.power.powerCost end end return powerCost end function FactoryUtils.getBuildingConsumePower(buildingId) local data = Tables.factoryBuildingTable:GetValue(buildingId) return data.powerConsume end function FactoryUtils.getItemOutputItemIds(itemId, ignoreUnlock) local outcomeIds = {} local facCore = GameInstance.player.remoteFactory.core do local hasCraft, craftIds = Tables.factoryItemAsMachineCrafterIncomeTable:TryGetValue(itemId) if hasCraft then for _, craftId in pairs(craftIds.list) do if ignoreUnlock or facCore:IsFormulaVisible(craftId) then local craftData = Tables.factoryMachineCraftTable:GetValue(craftId) local itemBundleGroupList = craftData.outcomes for _, group in pairs(itemBundleGroupList) do for _, bundle in pairs(group.group) do outcomeIds[bundle.id] = true end end end end end end do local hasCraft, craftIds = Tables.FactoryItemAsHubCraftIncomeTable:TryGetValue(itemId) if hasCraft then local sys = GameInstance.player.facSpMachineSystem for _, craftId in pairs(craftIds.list) do if ignoreUnlock or sys:IsCraftUnlocked(craftId) then local craftData = Tables.factoryHubCraftTable:GetValue(craftId) for _, bundle in pairs(craftData.outcomes) do outcomeIds[bundle.id] = true end end end end end if not next(outcomeIds) then return end local outcomeIdList = {} for id, _ in pairs(outcomeIds) do table.insert(outcomeIdList, id) end return outcomeIdList end function FactoryUtils.getItemAsInputRecipeIds(itemId, ignoreUnlock) local recipeIds = {} local canCraft = false do local _, fuelData = Tables.factoryFuelItemTable:TryGetValue(itemId) if fuelData then local buildingId, powerStationData for id, data in pairs(Tables.factoryPowerStationTable) do local buildingItemId = FactoryUtils.getBuildingItemId(id) if WikiUtils.canShowWikiEntry(buildingItemId) then buildingId = id powerStationData = data break end end if buildingId and powerStationData then local info = { incomes = { { id = itemId, count = 1 } }, time = powerStationData.msPerRound * fuelData.progressRound * 0.001, outcomeText = string.format(Language.FUEL_OUTCOME_TEXT_FORMAT, fuelData.powerProvide), buildingId = buildingId, craftId = fuelData.id, isUnlock = true, } table.insert(recipeIds, info) canCraft = true end end end do local hasCraft, craftIds = Tables.factoryItemAsMachineCrafterIncomeTable:TryGetValue(itemId) if hasCraft then canCraft = true for _, craftId in pairs(craftIds.list) do table.insert(recipeIds, FactoryUtils.parseMachineCraftData(craftId)) end end end do local hasCraft, craftIds = Tables.FactoryItemAsHubCraftIncomeTable:TryGetValue(itemId) if hasCraft then canCraft = true for _, craftId in pairs(craftIds.list) do if ignoreUnlock or FactoryUtils.isSpMachineFormulaUnlocked(craftId) then table.insert(recipeIds, FactoryUtils.parseHubCraftData(craftId, true)) end end end end do local manualCraftIdList = {} for craftId, v in pairs(Tables.factoryManualCraftTable) do for i = 0, v.ingredients.Count - 1 do local ingredientItemId = v.ingredients[i].id if v.ingredients[i].id == itemId then table.insert(manualCraftIdList, craftId) break end end end if #manualCraftIdList > 0 then local manualCraft = GameInstance.player.facManualCraft canCraft = true for _, craftId in pairs(manualCraftIdList) do if ignoreUnlock or manualCraft:IsCraftUnlocked(craftId) then table.insert(recipeIds, FactoryUtils.parseManualCraftData(craftId, true)) end end end end return recipeIds, canCraft end function FactoryUtils.getBuildingCrafts(buildingId, ignoreUnlock, justId, producerMode) local bData = Tables.factoryBuildingTable:GetValue(buildingId) local bType = bData.type local crafts = {} local facCore = GameInstance.player.remoteFactory.core local inventory = GameInstance.player.inventory if bType == GEnums.FacBuildingType.PowerStation then local powerStationData = Tables.factoryPowerStationTable:GetValue(buildingId) for fuelId, fuelData in pairs(Tables.factoryFuelItemTable) do if ignoreUnlock or inventory:IsItemFound(fuelId) then if justId then table.insert(crafts, fuelId) else local info = { incomes = { { id = fuelId, count = 1 } }, time = powerStationData.msPerRound * fuelData.progressRound * 0.001, outcomeText = string.format(Language.FUEL_OUTCOME_TEXT_FORMAT, fuelData.powerProvide), buildingId = buildingId, craftId = fuelId, sort = fuelData.powerProvide, } table.insert(crafts, info) end end end table.sort(crafts, Utils.genSortFunction({"sort"}, true)) elseif bType == GEnums.FacBuildingType.Hub or bType == GEnums.FacBuildingType.SubHub then local sys = GameInstance.player.facSpMachineSystem for craftId, data in pairs(Tables.factoryHubCraftTable) do if ignoreUnlock or sys:IsCraftUnlocked(craftId) then if justId then table.insert(crafts, craftId) else local info = FactoryUtils.parseHubCraftData(craftId) info.buildingId = buildingId table.insert(crafts, info) end end end elseif bType == GEnums.FacBuildingType.Miner then if ignoreUnlock or inventory:IsItemFound(FactoryUtils.getBuildingItemId(buildingId)) then local minerData = Tables.factoryMinerTable:GetValue(buildingId) for _, mineable in pairs(minerData.mineable) do local mineId = mineable.miningItemId if justId then table.insert(crafts, mineId) else table.insert(crafts, FactoryUtils.parseMinerCraftData(buildingId, mineable)) end end end elseif bType == GEnums.FacBuildingType.MachineCrafter or bType == GEnums.FacBuildingType.FluidReaction then local machineCrafterData = Tables.factoryMachineCrafterTable:GetValue(buildingId) for i = 0, machineCrafterData.modeMap.Count - 1 do local curModeItem = machineCrafterData.modeMap[i] if not producerMode or curModeItem.modeName == producerMode then local machineCrafterGroupData = Tables.factoryMachineCraftGroupTable:GetValue(curModeItem.groupName) for _, craftId in pairs(machineCrafterGroupData.craftList) do if ignoreUnlock or facCore:IsFormulaVisible(craftId) then if justId then table.insert(crafts, craftId) else table.insert(crafts, FactoryUtils.parseMachineCraftData(craftId)) end end end if producerMode then break end end end elseif bType == GEnums.FacBuildingType.FluidPumpIn then local fluidPumpInDataSuccess, fluidPumpInData = Tables.factoryFluidPumpInTable:TryGetValue(buildingId) if fluidPumpInDataSuccess then local time = fluidPumpInData.msPerRound * 0.001 for liquidItemId, _ in pairs(Tables.liquidTable) do local liquidPreFix = "liquid" local liquidItemSubString = string.sub(liquidItemId, string.find(liquidItemId, liquidPreFix) + #liquidPreFix) local liquidPointItemId = string.format("item_liquidpoint%s", liquidItemSubString) local liquidPointSuccess, liquidPointItemData = Tables.itemTable:TryGetValue(liquidPointItemId) if liquidPointSuccess then if justId then table.insert(crafts, liquidItemId) else local incomesId = liquidPointItemId local info = { time = time, incomes = { { id = incomesId, count = 1 } }, outcomes = { { id = liquidItemId, count = 1 } }, buildingId = buildingId, craftId = liquidItemId, } table.insert(crafts, info) end end end end elseif bType == GEnums.FacBuildingType.FluidConsume then local consumeSuccess, consumeData = Tables.factoryFluidConsumeTable:TryGetValue(buildingId) if consumeSuccess then local time = consumeData.msPerRound * 0.001 for index = 0, consumeData.liquidable.Count - 1 do local liquidItemId = consumeData.liquidable[index] if justId then table.insert(crafts, liquidItemId) else local incomesId = liquidItemId local info = { time = time, incomes = { { id = incomesId, count = 1 } }, buildingId = buildingId, craftId = liquidItemId, useFinish = true, } table.insert(crafts, info) end end end end return crafts, bType end function FactoryUtils.getBuildingCraftsWithNodeId(nodeId, ignoreUnlock, justId) local node = FactoryUtils.getBuildingNodeHandler(nodeId) local buildingId = node.templateId local formulaManComponentPosition = GEnums.FCComponentPos.FormulaMan:GetHashCode() local formulaManComponent = node:GetComponentInPosition(formulaManComponentPosition) local currentMode = formulaManComponent ~= nil and formulaManComponent.formulaMan.currentMode or nil local result local pdp = node.predefinedParam if pdp then local limitedResult = {} local unlockIdList if pdp.producer and pdp.producer.limitedFormulaIds.Count > 0 then unlockIdList = pdp.producer.limitedFormulaIds elseif pdp.fluidReaction and pdp.fluidReaction.visibleFormulas.Count > 0 then unlockIdList = pdp.fluidReaction.visibleFormulas end if unlockIdList then result = FactoryUtils.getBuildingCrafts(buildingId, true, justId, currentMode) for _, v in ipairs(result) do local curId = justId and v or v.craftId local found = false for i = 0, unlockIdList.Count - 1 do if unlockIdList[i] == curId then found = true break end end if found then table.insert(limitedResult, v) end end result = limitedResult end end if not result then result = FactoryUtils.getBuildingCrafts(buildingId, ignoreUnlock, justId, currentMode) end return result end function FactoryUtils.checkBuildingHasModeSwitch(buildingId, mode) if not FactoryUtils.isDomainSupportPipe() then return false end local buildingModeUnlocked = GameInstance.player.remoteFactory.core:IsBuildingModeUnlocked( FacConst.FAC_FORMULA_MODE_MAP.LIQUID, buildingId ) if not buildingModeUnlocked then return false end local crafterData = Tables.factoryMachineCrafterTable:GetValue(buildingId) if crafterData.modeMap.Count <= 1 then return false end for index = 0, crafterData.modeMap.Count - 1 do local mapData = crafterData.modeMap[index] if mapData ~= nil and mapData.modeName == FacConst.FAC_FORMULA_MODE_MAP.LIQUID then return true end end return false end function FactoryUtils.getMachineCraftGroupData(buildingId, modeName) local crafterData = Tables.factoryMachineCrafterTable:GetValue(buildingId) for i = 0, crafterData.modeMap.Count - 1 do local modeMapItem = crafterData.modeMap[i] if modeMapItem.modeName == modeName then return Tables.factoryMachineCraftGroupTable:GetValue(modeMapItem.groupName) end end end function FactoryUtils.getMachineCraftGroupDataFromNodeHandler(nodeHandler) local buildingId = nodeHandler.templateId local formulaManComponentPosition = GEnums.FCComponentPos.FormulaMan:GetHashCode() local formulaManComponent = nodeHandler:GetComponentInPosition(formulaManComponentPosition) local currentMode = formulaManComponent.formulaMan.currentMode return FactoryUtils.getMachineCraftGroupData(buildingId, currentMode) end function FactoryUtils.getItemCrafts(itemId, ignoreUnlock, includeMiner, includeFluidPumpIn) local crafts = {} local canCraft = false local facCore = GameInstance.player.remoteFactory.core local inventory = GameInstance.player.inventory do local hasCraft, craftIds = Tables.factoryItemAsMachineCrafterOutcomeTable:TryGetValue(itemId) if hasCraft then canCraft = true for _, craftId in pairs(craftIds.list) do if ignoreUnlock or facCore:IsFormulaVisible(craftId) then table.insert(crafts, FactoryUtils.parseMachineCraftData(craftId)) end end end end do local hasCraft, craftIds = Tables.factoryItemAsHubCraftOutcomeTable:TryGetValue(itemId) if hasCraft then canCraft = true local sys = GameInstance.player.facSpMachineSystem for _, craftId in pairs(craftIds.list) do if ignoreUnlock or sys:IsCraftUnlocked(craftId) then table.insert(crafts, FactoryUtils.parseHubCraftData(craftId, true)) end end end end do local hasCraft, craftIds = Tables.factoryItemAsManualCraftOutcomeTable:TryGetValue(itemId) if hasCraft then canCraft = true local sys = GameInstance.player.facManualCraft for _, craftId in pairs(craftIds.list) do if ignoreUnlock or sys:IsCraftUnlocked(craftId) then table.insert(crafts, FactoryUtils.parseManualCraftData(craftId, true)) end end end end do if includeMiner then for buildingId, minerData in pairs(Tables.factoryMinerTable) do local buildingItemId = FactoryUtils.getBuildingItemId(buildingId) local isUnlock = inventory:IsItemFound(buildingItemId) for idx = 0, minerData.mineable.Count - 1 do local mineable = minerData.mineable[idx] if mineable.miningItemId == itemId then if ignoreUnlock or isUnlock then canCraft = true table.insert(crafts, FactoryUtils.parseMinerCraftData(buildingId, mineable)) end end end end end end do if includeFluidPumpIn then local _, liquidData = Tables.liquidTable:TryGetValue(itemId) if liquidData then for buildingId, _ in pairs(Tables.factoryFluidPumpInTable) do local info = FactoryUtils.parseLiquidCraftData(buildingId, itemId) if info then canCraft = true table.insert(crafts, info) end end end end end return crafts, canCraft end function FactoryUtils.getItemProductItemList(itemId, skipItemTable) local itemMap = {} local itemList = {} local itemData = Tables.itemTable[itemId] local itemType = itemData.type local inv = GameInstance.player.inventory local ignoreUnlock = itemType == GEnums.ItemType.Blueprint if not skipItemTable then for _, id in pairs(itemData.outcomeItemIds) do if ignoreUnlock or inv:IsItemFound(id) then itemMap[id] = true table.insert(itemList, id) end end end local skipFindFormula = not skipItemTable and itemData.outcomeItemIds.Count > 0 if not skipFindFormula then local extraItemIds, buildingId if itemType == GEnums.ItemType.Material then extraItemIds = FactoryUtils.getItemOutputItemIds(itemId, false) elseif itemType == GEnums.ItemType.NormalBuilding or itemType == GEnums.ItemType.FuncBuilding then buildingId = FactoryUtils.getItemBuildingId(itemId) elseif itemType == GEnums.ItemType.Blueprint then local succ, d = Tables.machineBlueprint2MachineItemTable:TryGetValue(itemId) if succ then buildingId = FactoryUtils.getItemBuildingId(d.itemId) end end if buildingId then local crafts, bType = FactoryUtils.getBuildingCrafts(buildingId, ignoreUnlock, true, nil) if bType == GEnums.FacBuildingType.Miner then extraItemIds = crafts elseif bType == GEnums.FacBuildingType.MachineCrafter then extraItemIds = {} for _, craftId in ipairs(crafts) do local craftData = Tables.factoryMachineCraftTable:GetValue(craftId) for _, itemBundleGroup in pairs(craftData.outcomes) do for _, itemBundle in pairs(itemBundleGroup.group) do table.insert(extraItemIds, itemBundle.id) end end end end end if extraItemIds then for _, id in ipairs(extraItemIds) do if not itemMap[id] then itemMap[id] = true table.insert(itemList, id) end end end end if next(itemList) then return itemList else return nil end end function FactoryUtils.parseMachineCraftData(craftId) local craftData = Tables.factoryMachineCraftTable:GetValue(craftId) local formulaGroupId = craftData.formulaGroupId local machineCraftGroupData = Tables.factoryMachineCraftGroupTable:GetValue(formulaGroupId) local machineCraftData = Tables.factoryMachineCrafterTable:GetValue(craftData.machineId) local formulaMode = FacConst.FAC_FORMULA_MODE_MAP.NORMAL for index = 0, machineCraftData.modeMap.Count - 1 do local mapData = machineCraftData.modeMap[index] if mapData ~= nil and mapData.groupName == formulaGroupId then formulaMode = mapData.modeName break end end local info = { incomes = {}, time = craftData.progressRound * machineCraftGroupData.msPerRound * 0.001, formulaMode = formulaMode, outcomes = {}, buildingId = craftData.machineId, craftId = craftId, isUnlock = GameInstance.player.remoteFactory.core:IsFormulaVisible(craftId), } for _, itemBundleGroup in pairs(craftData.ingredients) do for _, itemBundle in pairs(itemBundleGroup.group) do table.insert(info.incomes, { id = itemBundle.id, count = itemBundle.count, buffer = craftData.buffers:GetValue(itemBundle.id) }) end end for _, itemBundleGroup in pairs(craftData.outcomes) do for _, itemBundle in pairs(itemBundleGroup.group) do table.insert(info.outcomes, { id = itemBundle.id, count = itemBundle.count, buffer = craftData.buffers:GetValue(itemBundle.id) }) end end return info end function FactoryUtils.parseHubCraftData(craftId, findBuilding) local craftData = Tables.factoryHubCraftTable:GetValue(craftId) local info = { incomes = {}, outcomes = {}, craftId = craftId, isUnlock = GameInstance.player.facSpMachineSystem:IsCraftUnlocked(craftId), } for _, itemBundle in pairs(craftData.ingredients) do table.insert(info.incomes, { id = itemBundle.id, count = itemBundle.count }) end for _, itemBundle in pairs(craftData.outcomes) do table.insert(info.outcomes, { id = itemBundle.id, count = itemBundle.count }) end if findBuilding then info.buildingId = FacConst.HUB_DATA_ID end return info end function FactoryUtils.parseManualCraftData(craftId, findBuilding) local craftData = Tables.factoryManualCraftTable:GetValue(craftId) local info = { incomes = {}, outcomes = {}, craftId = craftId, isUnlock = GameInstance.player.facManualCraft:IsCraftUnlocked(craftId) } for _, itemBundle in pairs(craftData.ingredients) do table.insert(info.incomes, { id = itemBundle.id, count = itemBundle.count }) end for _, itemBundle in pairs(craftData.outcomes) do table.insert(info.outcomes, { id = itemBundle.id, count = itemBundle.count }) end return info end function FactoryUtils.parseMinerCraftData(buildingId, mineable) local minerData = Tables.factoryMinerTable:GetValue(buildingId) local mineId = mineable.miningItemId local incomesId = "item_minepoint"..string.sub(mineId, string.find(mineId, "_"), -1) local minerTime = minerData.msPerRound / mineable.produceRate * 0.001 local newIncomes = {} local consumeItemId = mineable.consumeItem.id local consumeItemCount = mineable.consumeItem.count if not consumeItemId:isEmpty() and consumeItemCount > 0 then table.insert(newIncomes, { id = consumeItemId, count = consumeItemCount }) end table.insert(newIncomes, { id = incomesId, count = 1 }) local info = { time = minerTime, incomes = newIncomes, outcomes = { { id = mineId, count = 1 } }, buildingId = buildingId, craftId = string.format("%s_%s", mineId, buildingId), } return info end function FactoryUtils.parseLiquidCraftData(buildingId, liquidItemId) local _, fluidPumpInData = Tables.factoryFluidPumpInTable:TryGetValue(buildingId) local _, liquidData = Tables.liquidTable:TryGetValue(liquidItemId) if not fluidPumpInData or not liquidData then return nil end local liquidPreFix = "liquid" local liquidItemSubString = string.sub(liquidItemId, string.find(liquidItemId, liquidPreFix) + #liquidPreFix) local liquidPointItemId = string.format("item_liquidpoint%s", liquidItemSubString) local _, liquidPointItemData = Tables.itemTable:TryGetValue(liquidPointItemId) if not liquidPointItemData then return nil end local info = { time = fluidPumpInData.msPerRound * 0.001, incomes = { { id = liquidPointItemId, count = 1 } }, outcomes = { { id = liquidItemId, count = 1 } }, buildingId = buildingId, craftId = liquidItemId, } return info end function FactoryUtils.isSpecialBuilding(buildingId) local buildingData = Tables.factoryBuildingTable:GetValue(buildingId) local isSpBuilding = lume.find(FacConst.SP_BUILDING_TYPES, buildingData.type) ~= nil return isSpBuilding end function FactoryUtils.isInTopView() return LuaSystemManager.factory.inTopView end function FactoryUtils.isMachineTargetShown() local ctrl = UIManager.cfgs.FacMainLeft.ctrl return ctrl and ctrl.showMachineTarget or false end function FactoryUtils.canPlaceBuildingOnCurRegion(buildingId) if not Utils.isCurrentMapHasFactoryGrid() then return false end local isInMainRegion = GameInstance.remoteFactoryManager:IsPlayerPositionInMainRegion() if isInMainRegion then return true end local buildingData = Tables.factoryBuildingTable:GetValue(buildingId) if buildingData.type == GEnums.FacBuildingType.SubHub then return true end return not buildingData.onlyShowOnMain end function FactoryUtils.getCurRegionInfo() return GameInstance.remoteFactoryManager.system.core.currentScope end function FactoryUtils.getCurChapterInfo() return GameInstance.player.remoteFactory.core:GetChapterInfoById(Utils.getCurrentChapterId()) end function FactoryUtils.getCurRegionPowerInfo() local chapterInfo = FactoryUtils.getCurChapterInfo() if chapterInfo then return chapterInfo.blackboard.power end end function FactoryUtils.getRegionPowerInfoByChapterId(chapterId) local chapterInfo = GameInstance.remoteFactoryManager.system.core:GetChapterInfoById(chapterId) if chapterInfo == nil then return nil end return chapterInfo.blackboard.power end function FactoryUtils.getMedicProgress(nodeId) return GameInstance.remoteFactoryManager.medicalTowerManager:GetCurrentProgress(nodeId) end function FactoryUtils.findNearestBuilding(buildingId, ignoreCull) local playerPos = GameInstance.playerController.mainCharacter.position if Utils.isInBlackbox() then local minDist, targetNodeId local list = GameInstance.remoteFactoryManager.nearBuildingQuery:Query(playerPos, 400) for _, octreeData in pairs(list) do local nodeId = octreeData.data.nodeId local nodeHandler = FactoryUtils.getBuildingNodeHandler(nodeId) if nodeHandler.templateId == buildingId then local dist = Vector3.Distance(nodeHandler.transform.worldPosition, playerPos) if not targetNodeId or dist < minDist then minDist = dist targetNodeId = nodeId end end end return targetNodeId ~= nil, targetNodeId else return CS.Beyond.Gameplay.RemoteFactory.RemoteFactoryManager.FindNearestBuilding(buildingId, playerPos, ignoreCull == true) end end function FactoryUtils.queryVoxelRangeHeightAdjust(posX, posY, posZ) return CS.Beyond.Gameplay.RemoteFactory.RemoteFactoryUtil.VoxelRangeHeightAdjust( CS.UnityEngine.RectInt(posX, posZ, 1, 1), posY) end function FactoryUtils.getCurSceneHandler() return CSFactoryUtil.GetSceneHandler() end function FactoryUtils.isPlayerOutOfRangeManual() local level = PhaseManager.m_openedPhaseSet[PhaseId.Level] if not level then return true end return level.isPlayerOutOfRangeManual end function FactoryUtils.canPlayerEnterFacMode() local level = PhaseManager.m_openedPhaseSet[PhaseId.Level] if not level then return false end return not (level.isPlayerOutOfRangeManual or GameWorld.battle.isSquadInFight) end function FactoryUtils.clampTopViewCamTargetPosition(worldPos, curWorldPos) local level = PhaseManager.m_openedPhaseSet[PhaseId.Level] if not level then return curWorldPos, false end if level.m_lastLevelIdNum ~= GameWorld.worldInfo.curLevelIdNum then logger.critical("FactoryUtils.clampTopViewCamTargetPosition m_lastLevelIdNum ~= curLevelIdNum", level.m_lastLevelIdNum, GameWorld.worldInfo.curLevelIdNum) return curWorldPos, false end local rect if level.customFacTopViewRangeInWorld then local mainCamera = CameraManager.mainCamera local dist = (mainCamera.transform.position - LuaSystemManager.factory.topViewCamTarget.position).y rect = level.customFacTopViewRangeInWorld local yPadding = math.min(dist * math.tan(mainCamera.fieldOfView / 2 * math.pi / 180), rect.height / 2) local xPadding = math.min(yPadding / Screen.height * Screen.width, rect.width / 2) rect = Unity.Rect(rect.x + xPadding, rect.y + yPadding, math.max(0, rect.width - xPadding * 2), math.max(0, rect.height - yPadding * 2)) else rect = level.mainRegionLocalRectWithMovePadding if rect and (rect.width == 0 or rect.width == 0) then logger.critical("FactoryUtils.clampTopViewCamTargetPosition rect IS ZERO", GameWorld.worldInfo.curMapIdStr, GameWorld.worldInfo.curLevelId) local inMainRegion, panelIndex = Utils.isInFacMainRegionAndGetIndex() level:_UpdateCurMainRegionInfo(panelIndex) return curWorldPos, false end end if not rect then return curWorldPos, false end local regionTransform, localPos, curLocalPos if not level.customFacTopViewRangeInWorld then regionTransform = GameInstance.remoteFactoryManager.gameWorldAgent:GetRegionRootTransform() localPos = regionTransform:InverseTransformPoint(worldPos) curLocalPos = regionTransform:InverseTransformPoint(curWorldPos) else localPos = worldPos curLocalPos = curWorldPos end if rect:Contains(localPos:XZ()) then return worldPos, false else local xMin, xMax, yMin, yMax if curWorldPos then xMin = math.min(rect.xMin, curLocalPos.x) xMax = math.max(rect.xMax, curLocalPos.x) yMin = math.min(rect.yMin, curLocalPos.z) yMax = math.max(rect.yMax, curLocalPos.z) else xMin = rect.xMin xMax = rect.xMax yMin = rect.yMin yMax = rect.yMax end localPos.x = lume.clamp(localPos.x, xMin, xMax) localPos.z = lume.clamp(localPos.z, yMin, yMax) if regionTransform then return regionTransform:TransformPoint(localPos), true else return localPos, false end end end function FactoryUtils.gameEventFactoryItemPush(nodeId, itemId, count, curItems) local buildingNode = FactoryUtils.getBuildingNodeHandler(nodeId) local worldPos = GameInstance.remoteFactoryManager.visual:BuildingGridToWorld( Vector2(buildingNode.transform.position.x, buildingNode.transform.position.z)) EventLogManagerInst:GameEvent_FactoryItemPush(buildingNode.nodeId, buildingNode.templateId, GameInstance.remoteFactoryManager.currentSceneName, worldPos, itemId, count, curItems) end function FactoryUtils.getBuildingPortState(nodeId, isPipePort) if nodeId <= 0 then return end local facManager = GameInstance.remoteFactoryManager local success, complexPortFragment = facManager:TrySamplePortInfo(Utils.getCurrentChapterId(), nodeId) if not success then return end local inPortInfoList, outPortInfoList = {}, {} for index = 0, complexPortFragment.ports.length - 1 do local portData = complexPortFragment.ports:GetValue(index) if portData.valid and portData.isPipe == isPipePort then local infoList = portData.isInput and inPortInfoList or outPortInfoList table.insert(infoList, { index = portData.idx, touchCompId = portData.touchComId, touchNodeId = portData.touchNodeId, isBinding = portData.touchNodeId > 0, isBlock = portData.isBlock, }) end end local sortFunc = Utils.genSortFunction({"index"}, true) table.sort(inPortInfoList, sortFunc) table.sort(outPortInfoList, sortFunc) return inPortInfoList, outPortInfoList end function FactoryUtils.getBuildingTypeByBuildingId(buildingId) local success, buildingData = Tables.factoryBuildingTable:TryGetValue(buildingId) if not success then return GEnums.FacBuildingType.Empty end return buildingData.type end function FactoryUtils.getBuildingProcessingCraft(buildingInfo) if buildingInfo == nil then return nil end local buildingType = FactoryUtils.getBuildingTypeByBuildingId(buildingInfo.buildingId) local crafts = FactoryUtils.getBuildingCraftsWithNodeId(buildingInfo.nodeId, true) if crafts == nil then return nil end if buildingType == GEnums.FacBuildingType.PowerStation then for _, craftInfo in pairs(crafts) do if craftInfo.incomes ~= nil and craftInfo.incomes[1].id == buildingInfo.burningItemId then return craftInfo end end elseif buildingType == GEnums.FacBuildingType.Miner then local collectItemId = buildingInfo.collectingItemId if string.isEmpty(collectItemId) and buildingInfo.mineData ~= nil then collectItemId = buildingInfo.mineData.itemId end for _, craftInfo in pairs(crafts) do if craftInfo.outcomes ~= nil and craftInfo.outcomes[1].id == collectItemId then return craftInfo end end elseif buildingType == GEnums.FacBuildingType.FluidPumpIn then for _, craftInfo in pairs(crafts) do if craftInfo.outcomes ~= nil and craftInfo.outcomes[1].id == buildingInfo.collectingItemId then return craftInfo end end elseif buildingType == GEnums.FacBuildingType.FluidConsume then local consumeId = buildingInfo.consumeItemId for _, craftInfo in pairs(crafts) do if craftInfo.incomes ~= nil and craftInfo.incomes[1].id == consumeId then return craftInfo end end else for _, craftInfo in pairs(crafts) do if craftInfo.craftId == buildingInfo.formulaId or craftInfo.craftId == buildingInfo.lastFormulaId then return craftInfo end end end return nil end function FactoryUtils.getMachineCraftLockFormulaId(nodeId) local node = FactoryUtils.getBuildingNodeHandler(nodeId) if node == nil then return "" end local pdp = node.predefinedParam if pdp == nil then return "" end local producer = pdp.producer if producer == nil then return "" end return producer.lockFormulaId end function FactoryUtils.isEquipFormulaUnlocked(formulaId) return GameInstance.player.equipTechSystem:IsFormulaUnlock(formulaId) end function FactoryUtils.isSpMachineFormulaUnlocked(formulaId) return GameInstance.player.facSpMachineSystem:IsCraftUnlocked(formulaId) end function FactoryUtils.isItemInfiniteInFactoryDepot(itemId) local factoryDepot = GameInstance.player.inventory.factoryDepot if factoryDepot == nil then return false end local depotInChapter = factoryDepot:GetOrFallback(Utils.getCurrentScope()) if depotInChapter == nil then return false end local actualDepot = depotInChapter[Utils.getCurrentChapterId()] if actualDepot == nil then return false end local success, isInfinite = actualDepot.infiniteItemIds:TryGetValue(itemId) if success == false then return false end return isInfinite end function FactoryUtils.isBuildingInventoryLocked(nodeId) local node = FactoryUtils.getBuildingNodeHandler(nodeId) if node == nil then return false end local pdp = node.predefinedParam if pdp == nil then return false end local cache, gridBox = pdp.cache, pdp.gridBox if cache ~= nil then return cache.lockManualInOut end if gridBox ~= nil then return gridBox.lockManualInOut end return false end function FactoryUtils.getLogisticData(templateId) local _, data do _, data = Tables.factoryGridConnecterTable:TryGetValue(templateId) if not data then _, data = Tables.factoryGridRouterTable:TryGetValue(templateId) end if not data then _, data = Tables.factoryBoxValveTable:TryGetValue(templateId) end if data then return data.gridUnitData, false end end do _, data = Tables.factoryLiquidRouterTable:TryGetValue(templateId) if not data then _, data = Tables.factoryLiquidConnectorTable:TryGetValue(templateId) end if not data then _, data = Tables.factoryFluidValveTable:TryGetValue(templateId) end if data then return data.liquidUnitData, true end end logger.error("No LogisticData", templateId) end function FactoryUtils.isFactoryItemFluid(itemId) local success, factoryItemData = Tables.factoryItemTable:TryGetValue(itemId) if success == false then return false end return factoryItemData.itemState end function FactoryUtils.getMachineCraftCacheLayoutData(nodeId) local nodeHandler = FactoryUtils.getBuildingNodeHandler(nodeId) if nodeHandler == nil then return nil end local groupData = FactoryUtils.getMachineCraftGroupDataFromNodeHandler(nodeHandler) local crafts = FactoryUtils.getBuildingCraftsWithNodeId(nodeId, true, false) if groupData == nil or crafts == nil or #crafts == 0 then return nil end local layoutData = {} layoutData.normalIncomeCaches = {} layoutData.fluidIncomeCaches = {} layoutData.normalOutcomeCaches = {} layoutData.fluidOutcomeCaches = {} local firstCraft = crafts[1] for _, income in ipairs(firstCraft.incomes) do local itemId = income.id local cacheData = FactoryUtils.isFactoryItemFluid(itemId) and layoutData.fluidIncomeCaches or layoutData.normalIncomeCaches local bufferId = LuaIndex(income.buffer) if cacheData[bufferId] == nil then local data = { slotCount = 1, } cacheData[bufferId] = data else local slotCount = cacheData[bufferId].slotCount cacheData[bufferId].slotCount = slotCount + 1 end end for _, outcome in ipairs(firstCraft.outcomes) do local itemId = outcome.id local cacheData = FactoryUtils.isFactoryItemFluid(itemId) and layoutData.fluidOutcomeCaches or layoutData.normalOutcomeCaches local bufferId = LuaIndex(outcome.buffer) if cacheData[bufferId] == nil then local data = { slotCount = 1, } cacheData[bufferId] = data else local slotCount = cacheData[bufferId].slotCount cacheData[bufferId].slotCount = slotCount + 1 end end local bindingCollector = function(bindingDataList, caches) if caches == nil or #caches == 0 then return end for index = 0, bindingDataList.Count - 1 do local cacheData = caches[LuaIndex(index)] if cacheData == nil then logger.error("配方道具数据与建筑数据不匹配") return end local bindingData = bindingDataList[index] cacheData.portCount = bindingData.bindingPortIndices.Count cacheData.ports = bindingData.bindingPortIndices end end bindingCollector(groupData.ingredientBufferBinding, layoutData.normalIncomeCaches) bindingCollector(groupData.outcomeBufferBinding, layoutData.normalOutcomeCaches) bindingCollector(groupData.pipeIngredientBufferBinding, layoutData.fluidIncomeCaches) bindingCollector(groupData.pipeOutcomeBufferBinding, layoutData.fluidOutcomeCaches) return layoutData end function FactoryUtils.getNodeWorldPos(nodeId) local buildingNode = FactoryUtils.getBuildingNodeHandler(nodeId) local worldPos = CSFactoryUtil.GetBuildingModelPosition(buildingNode) return worldPos end local EvtRendererClass = CS.Beyond.Gameplay.Factory.EvtLogisticFigureRenderer function FactoryUtils.stopLogisticFigureRenderer() FactoryUtils.changeLogisticFigureRenderer(EvtRendererClass.S_NONE) end function FactoryUtils.startBeltFigureRenderer() FactoryUtils.changeLogisticFigureRenderer(EvtRendererClass.S_CONVEYOR) end function FactoryUtils.startPipeFigureRenderer() FactoryUtils.changeLogisticFigureRenderer(EvtRendererClass.S_PIPE) end function FactoryUtils.changeLogisticFigureRenderer(figureBit) GameInstance.remoteFactoryManager:ToggleLogisticFigure(figureBit) end function FactoryUtils.isBeltInSimpleFigure() return GameInstance.remoteFactoryManager:IsConveyorInSimpleFigure() end function FactoryUtils.isPipeInSimpleFigure() return GameInstance.remoteFactoryManager:IsPipeInSimpleFigure() end function FactoryUtils.updateFacTechTreeTechPointNode(view, facTechPackageId) local packageData = Tables.facSTTGroupTable[facTechPackageId] local costPointCfg = Tables.itemTable[packageData.costPointType] view.textResourceName.text = costPointCfg.name view.textResourceNumber.text = Utils.getItemCount(packageData.costPointType) local showTips = function() Notify(MessageConst.SHOW_ITEM_TIPS, { itemId = packageData.costPointType, transform = view.imgIcon.transform, posType = UIConst.UI_TIPS_POS_TYPE.LeftTop, isSideTips = DeviceInfo.usingController, }) end view.imgIcon:LoadSprite(UIConst.UI_SPRITE_ITEM, packageData.costPointType) view.imgIconButton.onClick:AddListener(function() showTips() end) if view.imgBg then view.imgBg.onClick:AddListener(function() showTips() end) end end function FactoryUtils.updateFacTechTreeTechPointCount(view, facTechPackageId) local packageData = Tables.facSTTGroupTable[facTechPackageId] view.textResourceNumber.text = Utils.getItemCount(packageData.costPointType) end function FactoryUtils.updateBlackboxCell(view, blackboxId, onClickFunc) local BlackboxCellState = { Complete = "complete", Lock = "lock", Normal = "normal", Active = "active", Inactive = "inactive", } local blackboxCfg = Tables.dungeonTable[blackboxId] local blackboxName = blackboxCfg.dungeonName local isComplete = DungeonUtils.isDungeonPassed(blackboxId) local isUnlock = DungeonUtils.isDungeonUnlock(blackboxId) local isActive = DungeonUtils.isDungeonActive(blackboxId) view.nameTxtS.text = blackboxName view.nameTxtN.text = blackboxName local state1 if isActive then state1 = BlackboxCellState.Active else state1 = BlackboxCellState.Inactive end view.stateController:SetState(state1) local state2 if isComplete then state2 = BlackboxCellState.Complete elseif isActive and not isUnlock then state2 = BlackboxCellState.Lock else state2 = BlackboxCellState.Normal end view.stateController:SetState(state2) if onClickFunc then view.button.onClick:RemoveAllListeners() view.button.onClick:AddListener(function() onClickFunc() end) end end function FactoryUtils.getBlackboxInfoTbl(blackboxIds, ignoreInactiveAndLocked) local relativeBlackboxes = {} for _, blackboxId in pairs(blackboxIds) do local isComplete = DungeonUtils.isDungeonPassed(blackboxId) local isUnlock = DungeonUtils.isDungeonUnlock(blackboxId) local isActive = DungeonUtils.isDungeonActive(blackboxId) if not ignoreInactiveAndLocked or ignoreInactiveAndLocked and isActive and isUnlock then local blackboxCfg = Tables.dungeonTable[blackboxId] local blackboxInfo = {} blackboxInfo.blackboxId = blackboxId blackboxInfo.completeSortId = isComplete and 1 or 0 blackboxInfo.activeSortId = isActive and 0 or 1 blackboxInfo.unlockSortId = isUnlock and 0 or 1 blackboxInfo.sortId = blackboxCfg.sortId table.insert(relativeBlackboxes, blackboxInfo) end end table.sort(relativeBlackboxes, Utils.genSortFunction({ "completeSortId", "activeSortId", "unlockSortId", "sortId" }, true)) return relativeBlackboxes end function FactoryUtils.genFilterBlackboxArgs(packageName, onFilterConfirmFunc) local filter = {} filter.tagGroups = {} local layerFilter = {} layerFilter.title = Language.LUA_FAC_TECH_TREE_BLACKBOX_LIST_FILTER_LAYER_DESC layerFilter.tags = {} local packageCfg = Tables.facSTTGroupTable[packageName] for _, layerId in pairs(packageCfg.layerIds) do local layerCfg = Tables.facSTTLayerTable[layerId] if not layerCfg.isTBD then table.insert(layerFilter.tags, { layerId = layerId, name = layerCfg.name, order = layerCfg.order, }) end end table.sort(layerFilter.tags, Utils.genSortFunction({ "order" }, true)) table.insert(filter.tagGroups, layerFilter) local categoryFilter = {} categoryFilter.title = Language.LUA_FAC_TECH_TREE_BLACKBOX_LIST_FILTER_CATEGORY_DESC categoryFilter.tags = {} for _, categoryId in pairs(packageCfg.categoryIds) do local categoryCfg = Tables.facSTTCategoryTable[categoryId] if not GameInstance.player.facTechTreeSystem:CategoryIsHidden(categoryId) then table.insert(categoryFilter.tags, { categoryId = categoryId, name = categoryCfg.name, order = categoryCfg.order, }) end end table.sort(categoryFilter.tags, Utils.genSortFunction({ "order" }, true)) table.insert(filter.tagGroups, categoryFilter) local completeFilter = {} completeFilter.title = Language.LUA_FAC_TECH_TREE_BLACKBOX_LIST_FILTER_IS_COMPLETE_DESC completeFilter.tags = { { name = Language.LUA_FAC_TECH_TREE_BLACKBOX_LIST_FILTER_STATE_UN_DESC, completeState = false }, { name = Language.LUA_FAC_TECH_TREE_BLACKBOX_LIST_FILTER_STATE_COMPLETE_DESC, completeState = true } } table.insert(filter.tagGroups, completeFilter) filter.onConfirm = function(selectedTags) onFilterConfirmFunc(selectedTags) end filter.getResultCount = function(selectedTags) local ids = FactoryUtils.getFilterBlackboxIds(packageName, selectedTags) return #ids end return filter end function FactoryUtils.getFilterBlackboxIds(packageName, selectedTags) local blackboxIds = {} local packageCfg = Tables.facSTTGroupTable[packageName] for _, techId in pairs(packageCfg.techIds) do local nodeData = Tables.facSTTNodeTable[techId] for _, blackboxId in pairs(nodeData.blackboxIds) do local isUnlock = DungeonUtils.isDungeonUnlock(blackboxId) local isActive = DungeonUtils.isDungeonActive(blackboxId) if not isUnlock or not isActive then goto continue end local layerMatch = false local hasLayerTag = false local categoryMatch = false local hasCategoryTag = false local completeMatch = false local hasCompleteTag = false for _, tag in ipairs(selectedTags) do if tag.layerId ~= nil then hasLayerTag = true end if hasLayerTag then layerMatch = layerMatch or nodeData.layer == tag.layerId end if tag.categoryId ~= nil then hasCategoryTag = true end if hasCategoryTag then categoryMatch = categoryMatch or nodeData.category == tag.categoryId end if tag.completeState ~= nil then hasCompleteTag = true end if hasCompleteTag then completeMatch = completeMatch or GameInstance.dungeonManager:IsDungeonPassed(blackboxId) == tag.completeState end end if not hasLayerTag then layerMatch = true end if not hasCategoryTag then categoryMatch = true end if not hasCompleteTag then completeMatch = true end if layerMatch and categoryMatch and completeMatch and not lume.find(blackboxIds, blackboxId) then table.insert(blackboxIds, blackboxId) end ::continue:: end end return blackboxIds end function FactoryUtils.checkCanOpenPhaseFacTechTree(arg) local facTechTreeSystem = GameInstance.player.facTechTreeSystem if arg == nil then return true end local techId = arg.techId if not string.isEmpty(techId) then local packageId = Tables.facSTTNodeTable[techId].groupId local techIsHidden = facTechTreeSystem:NodeIsHidden(techId) local packageIsLocked = facTechTreeSystem:PackageIsLocked(packageId) if not techIsHidden and not packageIsLocked then return true else return false, Language.LUA_FAC_TECH_TREE_JUMP_FAIL_DESC end end local layerId = arg.layerId if not string.isEmpty(layerId) then return true end local packageId = arg.packageId if not string.isEmpty(packageId) then local hidden = facTechTreeSystem:PackageIsHidden(packageId) local locked = facTechTreeSystem:PackageIsLocked(packageId) if not hidden and not locked then return true else return false, Language.LUA_FAC_TECH_TREE_JUMP_FAIL_DESC end end logger.error("invalid params, plz check") return false end function FactoryUtils.getPackageInvestigateProgress(packageName) local curProgress, totalProgress = 0, 0 local packageCfg = Tables.facSTTGroupTable[packageName] local fac = GameInstance.player.facTechTreeSystem for _, techId in pairs(packageCfg.techIds) do if not fac:NodeIsHidden(techId) then totalProgress = totalProgress + 1 if not fac:NodeIsLocked(techId) then curProgress = curProgress + 1 end end end return curProgress, totalProgress end function FactoryUtils.enterFacCamera(stateName) return GameAction.AddCameraControlState(stateName) end function FactoryUtils.exitFacCamera(state) GameAction.RemoveCameraControlState(state) end function FactoryUtils.getCurOpenedBuildingId() local machine = PhaseManager.m_openedPhaseSet[PhaseId.FacMachine] if not machine then return true end return machine.m_panelBuildingDataId end function FactoryUtils.canShowPipe() return GameInstance.remoteFactoryManager.unlockSystem.systemUnlockedPipe and FactoryUtils.isDomainSupportPipe() end function FactoryUtils.isDomainSupportPipe() return CSFactoryUtil.IsCurDomainSupportPipe() end function FactoryUtils.getCraftTimeStr(time, forceFloor) if time == nil then return "" end local floorTime = math.floor(time) if floorTime == time or forceFloor then return tostring(floorTime) else return string.format("%.1f", time) end end function FactoryUtils.getMatchedFormulaIdByItemList(buildingId, mode, itemList) if itemList == nil or #itemList == 0 then return "" end local success, groupData = Tables.factoryMachineCrafterTable:TryGetValue(buildingId) if not success then return "" end local groupId = "" for index = 0, groupData.modeMap.Count - 1 do local modeData = groupData.modeMap[index] if modeData.modeName == mode then groupId = modeData.groupName break end end if string.isEmpty(groupId) then return "" end for formulaId, formulaData in pairs(Tables.factoryMachineCraftTable) do if formulaData.formulaGroupId == groupId then local searchMap = {} local totalSearchCount = 0 for _, itemId in ipairs(itemList) do if searchMap[itemId] == nil then searchMap[itemId] = 0 end searchMap[itemId] = searchMap[itemId] + 1 totalSearchCount = totalSearchCount + 1 end local totalDataCount = 0 for bundleGroupIndex = 0, formulaData.ingredients.Count - 1 do local bundleGroup = formulaData.ingredients[bundleGroupIndex].group for itemIndex = 0, bundleGroup.Count - 1 do local itemBundle = bundleGroup[itemIndex] local itemId = itemBundle.id if searchMap[itemId] then searchMap[itemId] = searchMap[itemId] - 1 if searchMap[itemId] == 0 then searchMap[itemId] = nil end end totalDataCount = totalDataCount + 1 end end if totalDataCount == totalSearchCount and not next(searchMap) then return formulaId end end end return "" end function FactoryUtils.getActiveChapterIdList() local csList = GameInstance.player.remoteFactory.curActiveChapterIds local idList = {} for chapterId, _ in cs_pairs(csList) do table.insert(idList, chapterId) end return idList end function FactoryUtils.getPlayerAllMarkerBuildingNodeInfo() local csList = GameInstance.player.remoteFactory.curSignBuildingMsgList local infoList = {} local count = 0 for index, data in cs_pairs(csList) do count = count + 1 local chapterId = ScopeUtil.ChapterIdStr2Int(data.ChapterId) local slotId = CSFactoryUtil.GetBlueprintSlotId(chapterId, data.NodeId) if slotId <= 0 then local info = { nodeId = data.NodeId, chapter = data.ChapterId, timestamp = index, iconKey = {} } for i = 0, data.SignId.Count - 1 do table.insert(info.iconKey, data.SignId[i]) end table.insert(infoList, info) end end return count, infoList end function FactoryUtils.getLiquidCanBeDischarge(itemId) local _, itemData = Tables.factoryItemTable:TryGetValue(itemId) if itemData == nil or itemData.dischargeType == nil then return false end return itemData.dischargeType end function FactoryUtils.getBlueprintTagGroupInfos() local tagGroupDic = {} for k, v in pairs(Tables.factoryBlueprintTagTable) do local tagGroup = tagGroupDic[v.type] if not tagGroup then local typeData = Tables.factoryBlueprintTagTypeTable[v.type] tagGroup = { title = typeData.name, sortId = typeData.sortId, tags = {} } tagGroupDic[v.type] = tagGroup end if string.isEmpty(v.formulaId) or GameInstance.player.remoteFactory.core:IsFormulaVisible(v.formulaId) then table.insert(tagGroup.tags, { id = k, type = v.type, name = v.name, sortId = v.sortId, }) end end local tagGroupList = {} for _, v in pairs(tagGroupDic) do table.insert(tagGroupList, v) table.sort(v.tags, Utils.genSortFunction({ "sortId", "id" })) end table.sort(tagGroupList, Utils.genSortFunction({ "sortId", "id" })) return tagGroupList end function FactoryUtils.createBPAbnormalIconHelper() local helper = { cachedResults = {}, } helper.IsAbnormal = function(machineId, itemId) if not GameInstance.player.inventory:IsItemFound(itemId) then return true end if not Tables.factoryBuildingTable:ContainsKey(machineId) then return false end local canProduceItems = helper.cachedResults[machineId] if not canProduceItems then local craftInfos = FactoryUtils.getBuildingCrafts(machineId) if not craftInfos or not next(craftInfos) then canProduceItems = true else canProduceItems = {} for _, cInfo in ipairs(craftInfos) do if cInfo.outcomes then for _, v in ipairs(cInfo.outcomes) do canProduceItems[v.id] = true end end end end helper.cachedResults[machineId] = canProduceItems end if canProduceItems == true then return false else return not canProduceItems[itemId] end end return helper end function FactoryUtils.clearQuickBarSlot(csIndex) local remoteFactoryCore = GameInstance.player.remoteFactory.core if remoteFactoryCore.isTempQuickBarActive then remoteFactoryCore:MoveItemToTempQuickBar("", csIndex) else GameInstance.player.remoteFactory:SendSetQuickBar(GEnums.FCQuickBarType.Inner, 0, csIndex, "") end end function FactoryUtils.getFreeBusLimitsInfo(regionId, index) local bus, source = GameInstance.remoteFactoryManager:GetFreeBusLimitsInfo(regionId, index) return { ["log_hongs_bus"] = bus, ["log_hongs_bus_source"] = source, } end local domainAllowModes = {} function FactoryUtils.addBuildingDomainSortFilterInfo(info, data, domainId) info.recommendDomains = {} info.domainSortGroup = FacConst.DOMAIN_SORT_GROUP.Normal local allowModes = domainAllowModes[domainId] if not allowModes then allowModes = {} local _, domainData = Tables.domainDataTable:TryGetValue(domainId) if domainData then for _, v in pairs(domainData.machineModeTypeGroup) do allowModes[v] = true end end domainAllowModes[domainId] = allowModes end for _, filterDomainId in pairs(data.recommendDomains) do table.insert(info.recommendDomains, filterDomainId) end if #data.placeDomains > 0 and lume.find(data.placeDomains, domainId) == nil then info.domainSortGroup = FacConst.DOMAIN_SORT_GROUP.Unsupported end if info.domainSortGroup > FacConst.DOMAIN_SORT_GROUP.ModeUnsupported and next(allowModes) then local succ, crafterData = Tables.factoryMachineCrafterTable:TryGetValue(data.id) if succ then local allow = false for index = 0, crafterData.modeMap.Count - 1 do local mapData = crafterData.modeMap[index] if mapData ~= nil and allowModes[mapData.modeName]then allow = true break end end if not allow then info.domainSortGroup = FacConst.DOMAIN_SORT_GROUP.ModeUnsupported end end end if info.domainSortGroup > FacConst.DOMAIN_SORT_GROUP.Unsuitable then if #data.recommendDomains > 0 and lume.find(data.recommendDomains, domainId) == nil then info.domainSortGroup = FacConst.DOMAIN_SORT_GROUP.Unsuitable end end info.domainReverseSort = -info.domainSortGroup end local allowPipeDoamins function FactoryUtils.GetAllowPipeDoaminList() if allowPipeDoamins == nil then allowPipeDoamins = {} for _, cfg in pairs(Tables.domainDataTable) do for _, v in pairs(cfg.machineModeTypeGroup) do if v == FacConst.FAC_FORMULA_MODE_MAP.LIQUID then table.insert(allowPipeDoamins, cfg.domainId) break end end end end return allowPipeDoamins end function FactoryUtils.SetCreatorName(blueprintContent, isCreating, bpInst) local ShowPSCreator = UNITY_PS5 local creatorName local roleId if isCreating then roleId = GameInstance.player.playerInfoSystem.roleId creatorName = string.format(Language.LUA_FAC_BLUEPRINT_CREATOR_FORMAT_USER, roleId) elseif FactoryUtils.isPlayerBP(bpInst) then roleId = bpInst.creatorUserId creatorName = string.format(Language.LUA_FAC_BLUEPRINT_CREATOR_FORMAT_USER, roleId) else creatorName = string.format(Language.LUA_FAC_BLUEPRINT_CREATOR_FORMAT_SYS, bpInst.info.creatorName) ShowPSCreator = false end if not ShowPSCreator then blueprintContent.view.creatorNameTxt.text = creatorName blueprintContent.view.creatorNode.gameObject:SetActive(creatorName ~= nil) blueprintContent.view.psNameIcon.gameObject:SetActive(false) return end if roleId == GameInstance.player.playerInfoSystem.roleId then creatorName = string.format(Language.LUA_FAC_BLUEPRINT_CREATOR_FORMAT_USER, GameInstance.player.friendSystem.SelfInfo.psName) blueprintContent.view.creatorNameTxt.text = creatorName blueprintContent.view.psNameIcon.gameObject:SetActive(true) blueprintContent.view.creatorNode.gameObject:SetActive(true) return end blueprintContent.view.creatorNode.gameObject:SetActive(false) GameInstance.player.friendSystem:SyncFriendInfoById(bpInst.creatorRoleId, function() local success, friendInfo = GameInstance.player.friendSystem:TryGetFriendInfo(bpInst.creatorRoleId) if success and friendInfo.psnData then creatorName = string.format(Language.LUA_FAC_BLUEPRINT_CREATOR_FORMAT_USER, friendInfo.psName) blueprintContent.view.psNameIcon.gameObject:SetActive(true) else creatorName = string.format(Language.LUA_FAC_BLUEPRINT_CREATOR_FORMAT_USER, roleId) blueprintContent.view.psNameIcon.gameObject:SetActive(false) end blueprintContent.view.creatorNameTxt.text = creatorName blueprintContent.view.creatorNode.gameObject:SetActive(true) end) end function FactoryUtils.isPlayerBP(bpInst) if bpInst == nil then return false end local isMine = bpInst.sourceType == CS.Beyond.Gameplay.RemoteFactory.RemoteFactoryBlueprintSourceType.Mine local isOther = (bpInst.sourceType == CS.Beyond.Gameplay.RemoteFactory.RemoteFactoryBlueprintSourceType.Gift and bpInst.param.shareIdx ~= 0) return isMine or isOther end function FactoryUtils.isOtherPeopleGiftBlueprint(bpInst) if bpInst == nil then return false end local isGift = bpInst.sourceType == CS.Beyond.Gameplay.RemoteFactory.RemoteFactoryBlueprintSourceType.Gift return isGift and bpInst.param.shareIdx ~= 0 and bpInst.creatorUserId ~= GameInstance.player.playerInfoSystem.roleId end function FactoryUtils.getItemCraft(itemId) local craftInfos, hasCraft = FactoryUtils.getItemCrafts(itemId, false) local defaultCraftId = WikiUtils.getItemDefaultCraftId(itemId) if craftInfos ~= nil and #craftInfos > 0 then for i, craftInfo in ipairs(craftInfos) do if craftInfo.craftId == defaultCraftId then return craftInfo end end return craftInfos[1] end return nil end function FactoryUtils.isSystemBlueprintUnlocked(id) if Utils.isInBlackbox() or not Utils.isSystemUnlocked(GEnums.UnlockSystemType.FacBlueprint) then return false end return GameInstance.player.remoteFactory.blueprint.builtinBlueprints:TryGetValue(id) end function FactoryUtils.getMatchingBlueprintShareCode(text) local patterns = Tables.facBlueprintConst.BlueprintShareCodePrefix local start_pos = 10000 local end_pos = -1 for index = 1, #patterns do local pattern = patterns[CSIndex(index)] local i, j = string.find(text, pattern, 1, true) if i and i <= start_pos and j >= end_pos then start_pos = math.min(start_pos, i) end_pos = math.max(end_pos, j) end end if end_pos <= 0 then return text end local charset = Tables.facBlueprintConst.BlueprintCharSet while end_pos < #text and charset:find(text:sub(end_pos+1, end_pos+1), 1, true) do end_pos = end_pos + 1 end return text:sub(start_pos, end_pos) end function FactoryUtils.exitFactoryRelatedMode() Notify(MessageConst.FAC_BUILD_EXIT_CUR_MODE, true) Notify(MessageConst.FAC_EXIT_DESTROY_MODE, true) LuaSystemManager.factory:ToggleTopView(false, true) end _G.FactoryUtils = FactoryUtils return FactoryUtils