public void DoImplementation(bool skipPO = false) { m_oldSelection = selection; Bounds bounds = GetTotalBounds(false); foreach (InstanceState state in m_states) { if (skipPO && state is ProcState) { continue; } if (state is BuildingState) { continue; } if (state.instance.isValid) { state.instance.Delete(); } } // Remove buildings last so attached nodes are cleaned up foreach (InstanceState state in m_states) { if (skipPO && state is ProcState) { continue; } if (!(state is BuildingState)) { continue; } if (state.instance.isValid) { state.instance.Delete(); } } UpdateArea(bounds); selection = new HashSet <Instance>(); MoveItTool.m_debugPanel.UpdatePanel(); MoveItTool.UpdatePillarMap(); }
public override void Undo() { if (m_clones == null) { return; } Bounds bounds = GetTotalBounds(false); foreach (Instance instance in m_clones) { instance.Delete(); } m_clones = null; // Restore selection selection = m_oldSelection; MoveItTool.m_debugPanel.UpdatePanel(); UpdateArea(bounds); MoveItTool.UpdatePillarMap(); }
public override void Do() { if (MoveItTool.POProcessing > 0) { return; } MoveItTool.instance.m_lastInstance = null; m_clones = new HashSet <Instance>(); m_origToCloneUpdate = new Dictionary <Instance, Instance>(); m_nodeOrigToClone = new Dictionary <ushort, ushort>(); m_stateToClone = new Dictionary <InstanceState, Instance>(); m_InstanceID_origToClone = new Dictionary <InstanceID, InstanceID>(); matrix4x.SetTRS(center + moveDelta, Quaternion.AngleAxis(angleDelta * Mathf.Rad2Deg, Vector3.down), Vector3.one); // Clone nodes first foreach (InstanceState state in m_states) { if (state is NodeState) { Instance clone = state.instance.Clone(state, ref matrix4x, moveDelta.y, angleDelta, center, followTerrain, m_nodeOrigToClone, this); if (clone != null) { m_clones.Add(clone); m_stateToClone.Add(state, clone); m_InstanceID_origToClone.Add(state.instance.id, clone.id); m_origToCloneUpdate.Add(state.instance.id, clone.id); m_nodeOrigToClone.Add(state.instance.id.NetNode, clone.id.NetNode); } } } // Clone everything else except PO foreach (InstanceState state in m_states) { if (!(state is NodeState || state is ProcState)) { Instance clone = state.instance.Clone(state, ref matrix4x, moveDelta.y, angleDelta, center, followTerrain, m_nodeOrigToClone, this); if (clone == null) { Log.Debug($"Failed to clone {state}"); continue; } m_clones.Add(clone); m_stateToClone.Add(state, clone); m_InstanceID_origToClone.Add(state.instance.id, clone.id); m_origToCloneUpdate.Add(state.instance.id, clone.id); ; if (state is SegmentState segmentState) { MoveItTool.NS.SetSegmentModifiers(clone.id.NetSegment, segmentState); if (segmentState.LaneIDs != null) { // old version does not store lane ids var clonedLaneIds = MoveableSegment.GetLaneIds(clone.id.NetSegment); DebugUtils.AssertEq(clonedLaneIds.Count, segmentState.LaneIDs.Count, "clonedLaneIds.Count, segmentState.LaneIDs.Count"); for (int i = 0; i < clonedLaneIds.Count; ++i) { var lane0 = new InstanceID { NetLane = segmentState.LaneIDs[i] }; var lane = new InstanceID { NetLane = clonedLaneIds[i] }; // Log.Debug($"Mapping lane:{lane0.NetLane} to {lane.NetLane}"); m_InstanceID_origToClone.Add(lane0, lane); } } } } } // Clone PO foreach (InstanceState state in m_states) { if (state is ProcState) { _ = state.instance.Clone(state, ref matrix4x, moveDelta.y, angleDelta, center, followTerrain, m_nodeOrigToClone, this); } } if (m_origToClone != null) { Dictionary <Instance, Instance> toReplace = new Dictionary <Instance, Instance>(); foreach (Instance key in m_origToClone.Keys) { toReplace.Add(m_origToClone[key], m_origToCloneUpdate[key]); DebugUtils.Log("To replace: " + m_origToClone[key].id.RawData + " -> " + m_origToCloneUpdate[key].id.RawData); } ActionQueue.instance.ReplaceInstancesForward(toReplace); } m_origToClone = m_origToCloneUpdate; // Select clones selection = m_clones; MoveItTool.m_debugPanel.UpdatePanel(); UpdateArea(GetTotalBounds(false)); try { MoveItTool.UpdatePillarMap(); } catch (Exception e) { DebugUtils.Log("CloneActionBase.Do failed"); DebugUtils.LogException(e); } }
protected HashSet <InstanceState> ProcessPillars(HashSet <InstanceState> states, bool makeClone) { if (!MoveItTool.advancedPillarControl) { return(states); } HashSet <ushort> nodesWithAttachments = new HashSet <ushort>(); var watch = new System.Diagnostics.Stopwatch(); watch.Start(); foreach (InstanceState instanceState in states) { if (instanceState is NodeState ns && ((NetNode)(ns.instance.data)).m_building > 0 && ((buildingBuffer[((NetNode)(ns.instance.data)).m_building].m_flags & Building.Flags.Hidden) != Building.Flags.Hidden)) { nodesWithAttachments.Add(ns.instance.id.NetNode); //Debug.Log($"Node {ns.instance.id.NetNode} found"); } } HashSet <InstanceState> newStates = new HashSet <InstanceState>(states); foreach (InstanceState instanceState in states) { ushort buildingId = instanceState.instance.id.Building; if (instanceState is BuildingState originalState && MoveItTool.m_pillarMap.ContainsKey(buildingId) && MoveItTool.m_pillarMap[buildingId] > 0) { ushort nodeId = MoveItTool.m_pillarMap[buildingId]; if (nodesWithAttachments.Contains(nodeId)) // The node is also selected { //Debug.Log($"Pillar {buildingId} for selected node {nodeId}"); continue; } MoveableBuilding original = (MoveableBuilding)instanceState.instance; buildingBuffer[buildingId].m_flags |= Building.Flags.Hidden; selection.Remove(original); newStates.Remove(originalState); BuildingState cloneState = null; if (makeClone) { MoveableBuilding clone = original.Duplicate(); selection.Add(clone); cloneState = (BuildingState)clone.SaveToState(); newStates.Add(cloneState); Debug.Log($"Pillar {buildingId} for node {nodeId} duplicated to {clone.id.Building}"); } else { Debug.Log($"Pillar {buildingId} for node {nodeId} hidden"); } pillarsOriginalToClone.Add(originalState, cloneState); original.isHidden = true; } } if (pillarsOriginalToClone.Count > 0) { MoveItTool.UpdatePillarMap(); } states = newStates; watch.Stop(); Debug.Log($"Pillars handled in {watch.ElapsedMilliseconds} ms\nSelected nodes:{nodesWithAttachments.Count}, total selection:{states.Count}, dups mapped:{pillarsOriginalToClone.Count}"); PillarsProcessed = true; return(states); }
public void UndoImplementation(bool reset = false) { if (m_states == null) { return; } Dictionary <Instance, Instance> toReplace = new Dictionary <Instance, Instance>(); Dictionary <ushort, ushort> clonedNodes = new Dictionary <ushort, ushort>(); var stateToClone = new Dictionary <InstanceState, Instance>(); var InstanceID_origToClone = new Dictionary <InstanceID, InstanceID>(); Building[] buildingBuffer = BuildingManager.instance.m_buildings.m_buffer; // Recreate nodes foreach (InstanceState state in m_states) { try { if (state.instance.id.Type == InstanceType.NetNode) { Instance clone = state.instance.Clone(state, null); toReplace.Add(state.instance, clone); stateToClone.Add(state, clone); InstanceID_origToClone.Add(state.instance.id, clone.id); clonedNodes.Add(state.instance.id.NetNode, clone.id.NetNode); ActionQueue.instance.UpdateNodeIdInStateHistory(state.instance.id.NetNode, clone.id.NetNode); } } catch (Exception e) { Debug.Log($"Undo Bulldoze failed on {(state is InstanceState ? state.prefabName : "unknown")}\n{e}"); } } // Recreate everything except nodes and segments foreach (InstanceState state in m_states) { try { if (state.instance.id.Type == InstanceType.NetNode) { continue; } if (state.instance.id.Type == InstanceType.NetSegment) { continue; } if (state is ProcState) { continue; } Instance clone = state.instance.Clone(state, clonedNodes); toReplace.Add(state.instance, clone); stateToClone.Add(state, clone); InstanceID_origToClone.Add(state.instance.id, clone.id); if (state.instance.id.Type == InstanceType.Prop) { PropManager.instance.m_props.m_buffer[clone.id.Prop].FixedHeight = ((PropState)state).fixedHeight; } else if (state.instance.id.Type == InstanceType.Building) { // Add attached nodes to the clonedNode list so other segments reconnect BuildingState buildingState = state as BuildingState; List <ushort> origNodeIds = new List <ushort>(); MoveableBuilding cb = clone as MoveableBuilding; ushort cloneNodeId = ((Building)cb.data).m_netNode; if (reset) { ushort cloneId = cb.id.Building; buildingBuffer[cloneId].m_flags = buildingBuffer[cloneId].m_flags & ~Building.Flags.BurnedDown; buildingBuffer[cloneId].m_flags = buildingBuffer[cloneId].m_flags & ~Building.Flags.Collapsed; buildingBuffer[cloneId].m_flags = buildingBuffer[cloneId].m_flags & ~Building.Flags.Abandoned; buildingBuffer[cloneId].m_flags = buildingBuffer[cloneId].m_flags | Building.Flags.Active; Thread.Sleep(50); } if (cloneNodeId != 0) { int c = 0; foreach (InstanceState i in buildingState.subStates) { if (i is NodeState ns) { InstanceID instanceID = default; instanceID.RawData = ns.id; origNodeIds.Insert(c++, instanceID.NetNode); } } c = 0; while (cloneNodeId != 0) { ushort origNodeId = origNodeIds[c]; NetNode clonedAttachedNode = Singleton <NetManager> .instance.m_nodes.m_buffer[cloneNodeId]; if (clonedAttachedNode.Info.GetAI() is TransportLineAI) { cloneNodeId = clonedAttachedNode.m_nextBuildingNode; continue; } if (clonedNodes.ContainsKey(origNodeId)) { Debug.Log($"Node #{origNodeId} is already in clone list!"); } clonedNodes.Add(origNodeId, cloneNodeId); cloneNodeId = clonedAttachedNode.m_nextBuildingNode; if (++c > 32768) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Nodes: Invalid list detected!\n" + Environment.StackTrace); break; } } } } } catch (Exception e) { Debug.Log($"Undo Bulldoze failed on {(state is InstanceState ? state.prefabName : "unknown")}\n{e}"); } } // Recreate segments foreach (InstanceState state in m_states) { try { if (state is SegmentState segmentState) { if (!clonedNodes.ContainsKey(segmentState.startNodeId)) { InstanceID instanceID = InstanceID.Empty; instanceID.NetNode = segmentState.startNodeId; // Don't clone if node is missing if (!((Instance)instanceID).isValid) { continue; } clonedNodes.Add(segmentState.startNodeId, segmentState.startNodeId); } if (!clonedNodes.ContainsKey(segmentState.endNodeId)) { InstanceID instanceID = InstanceID.Empty; instanceID.NetNode = segmentState.endNodeId; // Don't clone if node is missing if (!((Instance)instanceID).isValid) { continue; } clonedNodes.Add(segmentState.endNodeId, segmentState.endNodeId); } Instance clone = state.instance.Clone(state, clonedNodes); toReplace.Add(state.instance, clone); stateToClone.Add(state, clone); InstanceID_origToClone.Add(state.instance.id, clone.id); MoveItTool.NS.SetSegmentModifiers(clone.id.NetSegment, segmentState); } } catch (Exception e) { Debug.Log($"Undo Bulldoze failed on {(state is InstanceState ? state.prefabName : "unknown")}\n{e}"); } } // clone integrations. foreach (var item in stateToClone) { foreach (var data in item.Key.IntegrationData) { try { data.Key.Paste(item.Value.id, data.Value, InstanceID_origToClone); } catch (Exception e) { InstanceID sourceInstanceID = item.Key.instance.id; InstanceID targetInstanceID = item.Value.id; Debug.LogError($"integration {data.Key} Failed to paste from " + $"{sourceInstanceID.Type}:{sourceInstanceID.Index} to {targetInstanceID.Type}:{targetInstanceID.Index}"); DebugUtils.LogException(e); } } } if (replaceInstances) { ReplaceInstances(toReplace); ActionQueue.instance.ReplaceInstancesBackward(toReplace); selection = new HashSet <Instance>(); foreach (Instance i in m_oldSelection) { if (i is MoveableProc) { continue; } selection.Add(i); } MoveItTool.m_debugPanel.UpdatePanel(); } // Does not check MoveItTool.advancedPillarControl, because even if disabled now advancedPillarControl may have been active earlier in action queue foreach (KeyValuePair <BuildingState, BuildingState> pillarClone in pillarsOriginalToClone) { BuildingState originalState = pillarClone.Key; originalState.instance.isHidden = false; buildingBuffer[originalState.instance.id.Building].m_flags &= ~Building.Flags.Hidden; selection.Add(originalState.instance); m_states.Add(originalState); } if (pillarsOriginalToClone.Count > 0) { MoveItTool.UpdatePillarMap(); } }
public override void Do() { if (MoveItTool.POProcessing > 0) { return; } MoveItTool.instance.m_lastInstance = null; m_clones = new HashSet <Instance>(); m_origToCloneUpdate = new Dictionary <Instance, Instance>(); m_nodeOrigToClone = new Dictionary <ushort, ushort>(); var stateToClone = new Dictionary <InstanceState, Instance>(); var InstanceID_origToClone = new Dictionary <InstanceID, InstanceID>(); matrix4x.SetTRS(center + moveDelta, Quaternion.AngleAxis(angleDelta * Mathf.Rad2Deg, Vector3.down), Vector3.one); // Clone nodes first foreach (InstanceState state in m_states) { if (state is NodeState) { Instance clone = state.instance.Clone(state, ref matrix4x, moveDelta.y, angleDelta, center, followTerrain, m_nodeOrigToClone, this); if (clone != null) { m_clones.Add(clone); stateToClone.Add(state, clone); InstanceID_origToClone.Add(state.instance.id, clone.id); m_origToCloneUpdate.Add(state.instance.id, clone.id); m_nodeOrigToClone.Add(state.instance.id.NetNode, clone.id.NetNode); } } } // Clone everything else except PO foreach (InstanceState state in m_states) { if (!(state is NodeState || state is ProcState)) { Instance clone = state.instance.Clone(state, ref matrix4x, moveDelta.y, angleDelta, center, followTerrain, m_nodeOrigToClone, this); if (clone == null) { Debug.Log($"Failed to clone {state}"); continue; } m_clones.Add(clone); stateToClone.Add(state, clone); InstanceID_origToClone.Add(state.instance.id, clone.id); m_origToCloneUpdate.Add(state.instance.id, clone.id); ; if (state is SegmentState segmentState) { MoveItTool.NS.SetSegmentModifiers(clone.id.NetSegment, segmentState); if (segmentState.LaneIDs != null) { // old version does not store lane ids var clonedLaneIds = MoveableSegment.GetLaneIds(clone.id.NetSegment); DebugUtils.AssertEq(clonedLaneIds.Count, segmentState.LaneIDs.Count, "clonedLaneIds.Count, segmentState.LaneIDs.Count"); for (int i = 0; i < clonedLaneIds.Count; ++i) { var lane0 = new InstanceID { NetLane = segmentState.LaneIDs[i] }; var lane = new InstanceID { NetLane = clonedLaneIds[i] }; // Debug.Log($"Mapping lane:{lane0.NetLane} to {lane.NetLane}"); InstanceID_origToClone.Add(lane0, lane); } } } } } // backward compatibility. // Clone NodeController after segments have been added. foreach (var item in stateToClone) { if (item.Key is NodeState nodeState) { Instance clone = item.Value; ushort nodeID = clone.id.NetNode; MoveItTool.NodeController.PasteNode(nodeID, nodeState); } } // Clone TMPE rules // TODO remove when TMPE switches to integration foreach (var state in m_states) { if (state is NodeState nodeState) { MoveItTool.TMPE.Paste(nodeState.TMPE_NodeRecord, InstanceID_origToClone); } else if (state is SegmentState segmentState) { MoveItTool.TMPE.Paste(segmentState.TMPE_SegmentRecord, InstanceID_origToClone); MoveItTool.TMPE.Paste(segmentState.TMPE_SegmentStartRecord, InstanceID_origToClone); MoveItTool.TMPE.Paste(segmentState.TMPE_SegmentEndRecord, InstanceID_origToClone); } } // Clone PO foreach (InstanceState state in m_states) { if (state is ProcState) { _ = state.instance.Clone(state, ref matrix4x, moveDelta.y, angleDelta, center, followTerrain, m_nodeOrigToClone, this); } } // clone integrations. foreach (var item in stateToClone) { foreach (var data in item.Key.IntegrationData) { try { data.Key.Paste(item.Value.id, data.Value, InstanceID_origToClone); } catch (Exception e) { InstanceID sourceInstanceID = item.Key.instance.id; InstanceID targetInstanceID = item.Value.id; Debug.LogError($"integration {data.Key} Failed to paste from " + $"{sourceInstanceID.Type}:{sourceInstanceID.Index} to {targetInstanceID.Type}:{targetInstanceID.Index}"); DebugUtils.LogException(e); } } } if (m_origToClone != null) { Dictionary <Instance, Instance> toReplace = new Dictionary <Instance, Instance>(); foreach (Instance key in m_origToClone.Keys) { toReplace.Add(m_origToClone[key], m_origToCloneUpdate[key]); DebugUtils.Log("To replace: " + m_origToClone[key].id.RawData + " -> " + m_origToCloneUpdate[key].id.RawData); } ActionQueue.instance.ReplaceInstancesForward(toReplace); } m_origToClone = m_origToCloneUpdate; // Select clones selection = m_clones; MoveItTool.m_debugPanel.UpdatePanel(); UpdateArea(GetTotalBounds(false)); MoveItTool.UpdatePillarMap(); }