public static bool Copy(BlueprintData data, List <int> entityIds, int referenceId = 0) { PlanetFactory factory = GameMain.data.localPlanet.factory; var buildings = new Dictionary <int, EntityData>(); var belts = new Dictionary <int, EntityData>(); foreach (int id in entityIds) { EntityData entity = factory.entityPool[id]; ItemProto entityProto = LDB.items.Select(entity.protoId); // ignore standalone inserters and miners if (entityProto.prefabDesc.isInserter || entityProto.prefabDesc.minerType != EMinerType.None) { continue; } if (entityProto.prefabDesc.isBelt) { belts.Add(entity.id, entity); } else if ((entity.pos.magnitude - GameMain.localPlanet.realRadius - 0.2f) < 0.5f) // ignore multilevel buildings (for now) { buildings.Add(entity.id, entity); } } if (buildings.Count == 0 && belts.Count == 0) { return(false); } if (buildings.TryGetValue(referenceId, out EntityData globalReference)) { CopyBuilding(data, globalReference, globalReference); buildings.Remove(referenceId); } else { globalReference = buildings.Count > 0 ? buildings.First().Value : belts.First().Value; } foreach (EntityData building in buildings.Values) { CopyBuilding(data, building, globalReference); } foreach (EntityData belt in belts.Values) { CopyBelt(data, belt, globalReference); } return(true); }
public static void PrepareNew() { if (!hasData) { return; } hasData = false; previousData = data; data = new BlueprintData(); pastedEntities.Clear(); GC.Collect(); UpdateUIText(); }
public static void Restore(BlueprintData newData = null) { if (hasData) { BlueprintData temp = data; data = newData ?? previousData; previousData = temp; } else { hasData = true; data = newData ?? previousData; } pastedEntities.Clear(); GC.Collect(); UpdateUIText(); EnterBuildModeAfterBp(); }
public static void PasteInsertersOnly(BlueprintData data, Vector3 targetPos, float yaw, int pasteIndex = 0, bool connectToPasted = false) { PlayerAction_Build actionBuild = GameMain.data.mainPlayer.controller.actionBuild; var backupMechaBuildArea = actionBuild.player.mecha.buildArea; actionBuild.player.mecha.buildArea = 10000f; Vector2 targetSpr = targetPos.ToSpherical(); ConcurrentQueue <InserterCopy> inserterQueue = new ConcurrentQueue <InserterCopy>(data.copiedInserters); Util.Parallelize((threadIndex) => { while (inserterQueue.TryDequeue(out InserterCopy inserter)) { var pastedEntity = ConcurrentPasteInserter(threadIndex, inserter, yaw, pasteIndex, connectToPasted); BuildLogic.CheckBuildConditionsWorker(_abs[threadIndex], pastedEntity.buildPreview); } }); actionBuild.player.mecha.buildArea = backupMechaBuildArea; }
public static BlueprintData Import(string input, out HashSet <int> incompatibleIds) { string unzipped; var name = ""; incompatibleIds = new HashSet <int>(); try { List <string> segments = input.Split(':').ToList(); var base64Data = segments.Last(); if (segments.Count > 1) { segments.RemoveAt(segments.Count - 1); name = String.Join(":", segments.ToArray()); } unzipped = Unzip(Convert.FromBase64String(base64Data)); } catch (Exception e) { Console.WriteLine("Error while unzipping string: " + e.ToString()); return(null); } BlueprintData deserialized = null; try { fsSerializer serializer = new fsSerializer(); fsData data = fsJsonParser.Parse(unzipped); if (data.AsDictionary.ContainsKey("version")) { int intVer = (int)data.AsDictionary["version"].AsInt64; data.AsDictionary["$version"] = new fsData(intVer.ToString()); } string version = data.AsDictionary["$version"].AsString; if (version.Equals("1")) { serializer.AddConverter(new Vector3Converter_V1()); } else { serializer.AddConverter(new Vector3Converter()); serializer.AddConverter(new Vector2Converter()); serializer.AddConverter(new QuaternionConverter()); } serializer.TryDeserialize <BlueprintData>(data, ref deserialized).AssertSuccessWithoutWarnings(); } catch (Exception e) { Console.WriteLine("Error while trying to deserialise: " + e.ToString()); return(null); } foreach (var building in deserialized.copiedBuildings) { building.itemProto = LDB.items.Select((int)building.protoId); if (building.itemProto == null) { incompatibleIds.Add(building.protoId); } } foreach (var belt in deserialized.copiedBelts) { belt.itemProto = LDB.items.Select((int)belt.protoId); if (belt.itemProto == null) { incompatibleIds.Add(belt.protoId); } } foreach (var inserter in deserialized.copiedInserters) { inserter.itemProto = LDB.items.Select((int)inserter.protoId); if (inserter.itemProto == null) { incompatibleIds.Add(inserter.protoId); } } if (incompatibleIds.Count > 0) { return(null); } deserialized.name = name; return(deserialized); }
public static BeltCopy CopyBelt(BlueprintData data, EntityData sourceEntity, EntityData referenceEntity) { PlanetFactory factory = GameMain.data.localPlanet.factory; ItemProto sourceEntityProto = LDB.items.Select(sourceEntity.protoId); if (!sourceEntityProto.prefabDesc.isBelt) { return(null); } BeltComponent belt = factory.cargoTraffic.beltPool[sourceEntity.beltId]; Vector2 sourceSprPos = sourceEntity.pos.ToSpherical(); BeltCopy copiedBelt = new BeltCopy() { originalId = sourceEntity.id, protoId = sourceEntityProto.ID, itemProto = sourceEntityProto, altitude = Mathf.RoundToInt(2 * (sourceEntity.pos.magnitude - GameMain.localPlanet.realRadius - 0.2f) / 1.3333333f), backInputId = factory.cargoTraffic.beltPool[belt.backInputId].entityId, leftInputId = factory.cargoTraffic.beltPool[belt.leftInputId].entityId, rightInputId = factory.cargoTraffic.beltPool[belt.rightInputId].entityId, outputId = factory.cargoTraffic.beltPool[belt.outputId].entityId, }; factory.ReadObjectConn(sourceEntity.id, 0, out bool isOutput, out int otherId, out int otherSlot); if (otherId > 0 && factory.entityPool[otherId].beltId == 0) { copiedBelt.connectedBuildingId = otherId; copiedBelt.connectedBuildingIsOutput = isOutput; copiedBelt.connectedBuildingSlot = otherSlot; } factory.ReadObjectConn(sourceEntity.id, 1, out isOutput, out otherId, out otherSlot); if (otherId > 0 && factory.entityPool[otherId].beltId == 0) { copiedBelt.connectedBuildingId = otherId; copiedBelt.connectedBuildingIsOutput = isOutput; copiedBelt.connectedBuildingSlot = otherSlot; } if (sourceEntity.id == referenceEntity.id) { data.referencePos = sourceSprPos; } else { copiedBelt.originalSegmentCount = sourceSprPos.GetSegmentsCount(); copiedBelt.cursorRelativePos = (sourceSprPos - data.referencePos).Clamp(); } data.copiedBelts.Add(copiedBelt); factory.ReadObjectConn(sourceEntity.id, 4, out _, out otherId, out _); if (otherId != 0) { // only copy belt to belt inserter if both belts are part fo the blueprint factory.ReadObjectConn(otherId, 0, out _, out int endId, out _); factory.ReadObjectConn(otherId, 1, out _, out int startId, out _); int idToFind = sourceEntity.id == endId ? startId : endId; if (data.copiedBelts.FindIndex(x => x.originalId == idToFind) != -1) { EntityData inserterEntity = factory.entityPool[otherId]; CopyInserter(data, inserterEntity, sourceEntity); } } return(copiedBelt); }
public static InserterCopy CopyInserter(BlueprintData data, EntityData sourceEntity, EntityData referenceEntity) { PlanetFactory factory = GameMain.data.localPlanet.factory; PlayerAction_Build actionBuild = GameMain.data.mainPlayer.controller.actionBuild; if (sourceEntity.inserterId == 0) { return(null); } InserterComponent inserter = factory.factorySystem.inserterPool[sourceEntity.inserterId]; if (data.copiedInserters.FindIndex(x => x.originalId == inserter.entityId) != -1) { return(null); } int pickTarget = inserter.pickTarget; int insertTarget = inserter.insertTarget; ItemProto itemProto = LDB.items.Select(sourceEntity.protoId); bool incoming = insertTarget == referenceEntity.id; int otherId = incoming ? pickTarget : insertTarget; Vector2 referenceSprPos = referenceEntity.pos.ToSpherical(); Vector2 sourceSprPos = sourceEntity.pos.ToSpherical(); Vector2 sourceSprPos2 = inserter.pos2.ToSpherical(); // The belt or other building this inserter is attached to Vector2 otherSprPos; ItemProto otherProto; if (otherId > 0) { otherProto = LDB.items.Select(factory.entityPool[otherId].protoId); otherSprPos = factory.entityPool[otherId].pos.ToSpherical(); } else if (otherId < 0) { otherProto = LDB.items.Select(factory.prebuildPool[-otherId].protoId); otherSprPos = factory.prebuildPool[-otherId].pos.ToSpherical(); } else { otherSprPos = sourceSprPos2; otherProto = null; } bool otherIsBelt = otherProto == null || otherProto.prefabDesc.isBelt; // Cache info for this inserter InserterCopy copiedInserter = new InserterCopy { itemProto = itemProto, protoId = itemProto.ID, originalId = inserter.entityId, pickTarget = pickTarget, insertTarget = insertTarget, referenceBuildingId = referenceEntity.id, incoming = incoming, // rotations + deltas relative to the source building's rotation rot = Quaternion.Inverse(referenceEntity.rot) * sourceEntity.rot, rot2 = Quaternion.Inverse(referenceEntity.rot) * inserter.rot2, posDelta = sourceSprPos - referenceSprPos, // Delta from copied building to inserter pos pos2Delta = sourceSprPos2 - referenceSprPos, // Delta from copied building to inserter pos2 posDeltaCount = sourceSprPos.GetSegmentsCount(), pos2DeltaCount = sourceSprPos2.GetSegmentsCount(), otherPosDelta = otherSprPos - referenceSprPos, otherPosDeltaCount = otherSprPos.GetSegmentsCount(), // not important? pickOffset = inserter.pickOffset, insertOffset = inserter.insertOffset, filterId = inserter.filter, startSlot = -1, endSlot = -1, otherIsBelt = otherIsBelt }; InserterPoses.CalculatePose(actionBuild, pickTarget, insertTarget); if (actionBuild.posePairs.Count > 0) { float minDistance = 1000f; for (int j = 0; j < actionBuild.posePairs.Count; ++j) { var posePair = actionBuild.posePairs[j]; float startDistance = Vector3.Distance(posePair.startPose.position, sourceEntity.pos); float endDistance = Vector3.Distance(posePair.endPose.position, inserter.pos2); float poseDistance = startDistance + endDistance; if (poseDistance < minDistance) { minDistance = poseDistance; copiedInserter.startSlot = posePair.startSlot; copiedInserter.endSlot = posePair.endSlot; copiedInserter.pickOffset = (short)posePair.startOffset; copiedInserter.insertOffset = (short)posePair.endOffset; } } } /* factory.ReadObjectConn(sourceEntity.id, 1, out bool isOutput, out int connectedId, out int connectedSlot); * * if (connectedId != 0) * { * copiedInserter.startSlot = connectedSlot; * } * * * factory.ReadObjectConn(sourceEntity.id, 0, out _, out connectedId, out connectedSlot); * if (connectedId != 0) * { * copiedInserter.endSlot = connectedSlot; * } */ data.copiedInserters.Add(copiedInserter); return(copiedInserter); }
public static BuildingCopy CopyBuilding(BlueprintData data, EntityData sourceEntity, EntityData referenceEntity) { PlanetFactory factory = GameMain.data.localPlanet.factory; ItemProto sourceEntityProto = LDB.items.Select(sourceEntity.protoId); Vector3 sourcePos = sourceEntity.pos; Quaternion sourceRot = sourceEntity.rot; Quaternion zeroRot = Maths.SphericalRotation(sourcePos, 0f); float yaw = Vector3.SignedAngle(zeroRot.Forward(), sourceRot.Forward(), zeroRot.Up()); BuildingCopy copiedBuilding = new BuildingCopy() { originalId = sourceEntity.id, protoId = sourceEntityProto.ID, itemProto = sourceEntityProto, modelIndex = sourceEntity.modelIndex, }; var prefabDesc = BlueprintManager.GetPrefabDesc(copiedBuilding); if (sourceEntity.assemblerId > 0) { copiedBuilding.recipeId = factory.factorySystem.assemblerPool[sourceEntity.assemblerId].recipeId; } else if (sourceEntity.labId > 0) { LabComponent labComponent = factory.factorySystem.labPool[sourceEntity.labId]; copiedBuilding.recipeId = ((!labComponent.researchMode) ? labComponent.recipeId : -1); } else if (sourceEntity.powerGenId > 0) { PowerGeneratorComponent powerGeneratorComponent = factory.powerSystem.genPool[sourceEntity.powerGenId]; if (powerGeneratorComponent.gamma) { copiedBuilding.recipeId = ((powerGeneratorComponent.productId <= 0) ? 0 : 1); } } else if (sourceEntity.powerExcId > 0) { copiedBuilding.recipeId = Mathf.RoundToInt(factory.powerSystem.excPool[sourceEntity.powerExcId].targetState); } else if (sourceEntity.ejectorId > 0) { copiedBuilding.recipeId = factory.factorySystem.ejectorPool[sourceEntity.ejectorId].orbitId; } else if (sourceEntity.stationId > 0) { StationComponent stationComponent = factory.transport.stationPool[sourceEntity.stationId]; copiedBuilding.stationConfig = new StationConfig() { workEnergyPerTick = factory.powerSystem.consumerPool[stationComponent.pcId].workEnergyPerTick, tripRangeDrones = stationComponent.tripRangeDrones, tripRangeShips = stationComponent.tripRangeShips, warpEnableDist = stationComponent.warpEnableDist, warperNecessary = stationComponent.warperNecessary, includeOrbitCollector = stationComponent.includeOrbitCollector, deliveryDrones = stationComponent.deliveryDrones, deliveryShips = stationComponent.deliveryShips }; for (int i = 0; i < stationComponent.slots.Length; i++) { if (stationComponent.slots[i].storageIdx != 0) { copiedBuilding.slotFilters.Add(new SlotFilter() { slotIndex = i, storageIdx = stationComponent.slots[i].storageIdx }); } } for (int i = 0; i < stationComponent.storage.Length; i++) { if (stationComponent.storage[i].itemId != 0) { copiedBuilding.stationSettings.Add(new StationSetting() { index = i, itemId = stationComponent.storage[i].itemId, max = stationComponent.storage[i].max, localLogic = stationComponent.storage[i].localLogic, remoteLogic = stationComponent.storage[i].remoteLogic }); } } } else if (sourceEntity.splitterId > 0) { var splitterComponent = factory.cargoTraffic.splitterPool[sourceEntity.splitterId]; copiedBuilding.splitterSettings = new SplitterSettings() { inPriority = splitterComponent.inPriority, outPriority = splitterComponent.outPriority, outFilter = splitterComponent.outFilter }; var slots = new List <int>(4) { splitterComponent.beltA, splitterComponent.beltB, splitterComponent.beltC, splitterComponent.beltD }; if (copiedBuilding.splitterSettings.inPriority) { copiedBuilding.splitterSettings.inPrioritySlot = slots.IndexOf(splitterComponent.input0); } if (copiedBuilding.splitterSettings.outPriority) { copiedBuilding.splitterSettings.outPrioritySlot = slots.IndexOf(splitterComponent.output0); } // TODO: find a way to restore splitter settings // SplitterComponent splitterComponent = factory.cargoTraffic.splitterPool[sourceEntity.splitterId]; } else if (sourceEntity.storageId > 0) { copiedBuilding.recipeId = factory.factoryStorage.storagePool[sourceEntity.storageId].bans; } Vector2 sourceSprPos = sourcePos.ToSpherical(); if (sourceEntity.id == referenceEntity.id) { data.referencePos = sourceSprPos; copiedBuilding.cursorRelativeYaw = yaw; } else { copiedBuilding.originalSegmentCount = sourceSprPos.GetSegmentsCount(); copiedBuilding.cursorRelativePos = (sourceSprPos - data.referencePos).Clamp(); copiedBuilding.cursorRelativeYaw = yaw; } data.copiedBuildings.Add(copiedBuilding); for (int i = 0; i < sourceEntityProto.prefabDesc.insertPoses.Length; i++) { factory.ReadObjectConn(sourceEntity.id, i, out bool _, out int otherObjId, out int _); if (otherObjId > 0) { EntityData inserterEntity = factory.entityPool[otherObjId]; CopyInserter(data, inserterEntity, sourceEntity); } } if (sourceEntityProto.prefabDesc.multiLevel) { copiedBuilding.altitude = Mathf.RoundToInt((sourceEntity.pos.magnitude - GameMain.localPlanet.realRadius - 0.2f) / prefabDesc.lapJoint.magnitude); if (copiedBuilding.altitude > 0) { copiedBuilding.connectedBuildingId = referenceEntity.id; } factory.ReadObjectConn(sourceEntity.id, 15, out bool _, out int otherObjId, out int _); if (otherObjId > 0) { EntityData stackedEntity = factory.entityPool[otherObjId]; CopyBuilding(data, stackedEntity, sourceEntity); } } return(copiedBuilding); }
internal void Awake() { harmony = new Harmony("com.brokenmass.plugin.DSP.MultiBuild" + CHANNEL); itemSpecificSpacing = Config.Bind <bool>("General", "itemSpecificSpacing", true, "If this option is set to true, the mod will remember the last spacing used for a specific building. Otherwise the spacing will be the same for all entities."); spacingStore[0] = 0; //StartCoroutine(CheckForUpdates()); try { foreach (var pluginInfo in BepInEx.Bootstrap.Chainloader.PluginInfos) { if (BLACKLISTED_MODS.Contains(pluginInfo.Value.Metadata.GUID)) { incompatiblePlugins.Add(" - " + pluginInfo.Value.Metadata.Name); } } if (incompatiblePlugins.Count > 0) { isValidInstallation = false; harmony.PatchAll(typeof(IncompatibilityNotice)); } if (isValidInstallation) { harmony.PatchAll(typeof(MultiBuild)); harmony.PatchAll(typeof(BlueprintManager)); harmony.PatchAll(typeof(BuildLogic)); harmony.PatchAll(typeof(BlueprintCreator)); harmony.PatchAll(typeof(InserterPoses)); UIBlueprintGroup.onCreate = () => BlueprintCreator.StartBpMode(); UIBlueprintGroup.onRestore = () => { if (BlueprintCreator.bpMode) { BlueprintCreator.EndBpMode(true); } BlueprintManager.Restore(); }; UIBlueprintGroup.onImport = () => { if (BlueprintCreator.bpMode) { BlueprintCreator.EndBpMode(true); } HashSet <int> incompatibleIds; var data = BlueprintData.Import(GUIUtility.systemCopyBuffer, out incompatibleIds); if (data != null) { BlueprintManager.Restore(data); UIRealtimeTip.Popup("Blueprint successfully imported from your clipboard", false); } else { string message = "Error while importing data from your clipboard"; if (incompatibleIds.Count > 0) { message += $" - Found {incompatibleIds.Count} incompatible entities.\nIds: [{incompatibleIds.Join(null, ", ")}]"; } UIRealtimeTip.Popup(message, true); } }; UIBlueprintGroup.onExport = () => { if (BlueprintCreator.bpMode) { BlueprintCreator.EndBpMode(); } if (BlueprintManager.hasData) { GUIUtility.systemCopyBuffer = BlueprintManager.data.Export(); UIRealtimeTip.Popup("Blueprint successfully exported to your clipboard", false); } else { UIRealtimeTip.Popup("No blueprint data to export", true); } }; } } catch (Exception e) { isValidInstallation = false; Console.WriteLine(e.ToString()); } }
public static void Paste(BlueprintData data, Vector3 targetPos, float yaw, bool pasteInserters = true, int pasteIndex = 0) { PlayerAction_Build actionBuild = GameMain.data.mainPlayer.controller.actionBuild; var backupMechaBuildArea = actionBuild.player.mecha.buildArea; actionBuild.player.mecha.buildArea = 10000f; Vector2 targetSpr = targetPos.ToSpherical(); ConcurrentQueue <BuildingCopy> buildingsQueue = new ConcurrentQueue <BuildingCopy>(data.copiedBuildings); Util.Parallelize((int threadIndex) => { while (buildingsQueue.TryDequeue(out BuildingCopy building)) { var pastedEntity = ConcurrentPasteBuilding(threadIndex, building, targetSpr, yaw, pasteIndex); lock (actionBuild.nearcdLogic) { actionBuild.nearcdLogic.ActiveEntityBuildCollidersInArea(pastedEntity.pose.position, 5f); } } }); buildingsQueue = new ConcurrentQueue <BuildingCopy>(data.copiedBuildings); Util.Parallelize((int threadIndex) => { while (buildingsQueue.TryDequeue(out BuildingCopy building)) { int pasteId = PASTE_INDEX_MULTIPLIER * pasteIndex + building.originalId; var pastedEntity = BlueprintManager.pastedEntities[pasteId]; ConcurrentConnectBuildings(threadIndex, pastedEntity); BuildLogic.CheckBuildConditionsWorker(_abs[threadIndex], pastedEntity.buildPreview); } }); ConcurrentQueue <BeltCopy> beltsQueue = new ConcurrentQueue <BeltCopy>(data.copiedBelts); Util.Parallelize((int threadIndex) => { while (beltsQueue.TryDequeue(out BeltCopy belt)) { var pastedEntity = ConcurrentPasteBelt(threadIndex, belt, targetSpr, yaw, pasteIndex); } }); // after creating the belt previews this restore the correct connection to other belts and buildings beltsQueue = new ConcurrentQueue <BeltCopy>(data.copiedBelts); Util.Parallelize((int threadIndex) => { while (beltsQueue.TryDequeue(out BeltCopy belt)) { int pasteId = PASTE_INDEX_MULTIPLIER * pasteIndex + belt.originalId; var pastedEntity = BlueprintManager.pastedEntities[pasteId]; ConcurrentConnectBelt(threadIndex, pastedEntity); BuildLogic.CheckBuildConditionsWorker(_abs[threadIndex], pastedEntity.buildPreview); } }); if (pasteInserters) { ConcurrentQueue <InserterCopy> inserterQueue = new ConcurrentQueue <InserterCopy>(data.copiedInserters); Util.Parallelize((threadIndex) => { while (inserterQueue.TryDequeue(out InserterCopy inserter)) { var pastedEntity = ConcurrentPasteInserter(threadIndex, inserter, yaw, pasteIndex); BuildLogic.CheckBuildConditionsWorker(_abs[threadIndex], pastedEntity.buildPreview); } }); } actionBuild.player.mecha.buildArea = backupMechaBuildArea; }