Files
Endfield-Data/LuaScripts/Common/Utils/FactoryUtils.lua
2026-01-31 21:42:01 +07:00

2394 lines
79 KiB
Lua

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