public override InstanceState GetState() { ushort node = id.NetNode; NodeState state = new NodeState(); state.instance = this; state.Info = Info; state.position = nodeBuffer[node].m_position; state.terrainHeight = TerrainManager.instance.SampleOriginalRawHeightSmooth(state.position); state.flags = nodeBuffer[node].m_flags; MoveableBuilding pillarInstance = Pillar; if (Pillar != null) { state.pillarState = Pillar.GetState() as BuildingState; } for (int i = 0; i < 8; i++) { ushort segment = nodeBuffer[node].GetSegment(i); if (segment != 0) { state.segmentsSave[i].startDirection = segmentBuffer[segment].m_startDirection; state.segmentsSave[i].endDirection = segmentBuffer[segment].m_endDirection; } } return(state); }
// For Deletion Undo public override Instance Clone(InstanceState instanceState, Dictionary <ushort, ushort> clonedNodes) { BuildingState state = instanceState as BuildingState; MoveableBuilding cloneInstance = null; BuildingInfo info = state.Info.Prefab as BuildingInfo; if (BuildingManager.instance.CreateBuilding(out ushort clone, ref SimulationManager.instance.m_randomizer, info, state.position, state.angle, state.length, SimulationManager.instance.m_currentBuildIndex)) { SimulationManager.instance.m_currentBuildIndex++; InstanceID cloneID = default; cloneID.Building = clone; cloneInstance = new MoveableBuilding(cloneID); buildingBuffer[clone].m_flags = state.flags; if (info.m_subBuildings != null && info.m_subBuildings.Length != 0) { Matrix4x4 subMatrix4x = default; subMatrix4x.SetTRS(state.position, Quaternion.AngleAxis(state.angle * Mathf.Rad2Deg, Vector3.down), Vector3.one); for (int i = 0; i < info.m_subBuildings.Length; i++) { BuildingInfo subInfo = info.m_subBuildings[i].m_buildingInfo; Vector3 subPosition = subMatrix4x.MultiplyPoint(info.m_subBuildings[i].m_position); float subAngle = info.m_subBuildings[i].m_angle * 0.0174532924f + state.angle; if (BuildingManager.instance.CreateBuilding(out ushort subClone, ref SimulationManager.instance.m_randomizer, subInfo, subPosition, subAngle, 0, SimulationManager.instance.m_currentBuildIndex)) { SimulationManager.instance.m_currentBuildIndex++; if (info.m_subBuildings[i].m_fixedHeight) { buildingBuffer[subClone].m_flags = buildingBuffer[subClone].m_flags | Building.Flags.FixedHeight; } } if (clone != 0 && subClone != 0) { buildingBuffer[clone].m_subBuilding = subClone; buildingBuffer[subClone].m_parentBuilding = clone; buildingBuffer[subClone].m_flags = buildingBuffer[subClone].m_flags | Building.Flags.Untouchable; clone = subClone; } } } cloneInstance.ResetSubInstances(); } return(cloneInstance); }
public override void ReplaceInstance(Instance instance) { base.ReplaceInstance(instance); MoveableBuilding building = instance as MoveableBuilding; int count = 0; foreach (Instance subInstance in building.subInstances) { subStates[count++].instance = subInstance; } }
public void FinaliseDrag() { MoveItTool.dragging = false; foreach (InstanceState instanceState in savedStates) { MoveableBuilding mb = instanceState.instance as MoveableBuilding; if (mb != null) { mb.FinaliseDrag(); } } }
public void InitialiseDrag() { MoveItTool.dragging = true; foreach (InstanceState instanceState in m_states) { MoveableBuilding mb = instanceState.instance as MoveableBuilding; if (mb != null) { mb.InitialiseDrag(); } } }
public override void SetHeight(float height) { Vector3 newPosition = position; MoveableBuilding nodePillar = Pillar; if (nodePillar != null) { Vector3 subPosition = nodePillar.position; subPosition.y = subPosition.y - newPosition.y + height; nodePillar.Move(subPosition, nodePillar.angle); } newPosition.y = height; Move(newPosition, angle); }
public override Instance Clone(InstanceState instanceState, ref Matrix4x4 matrix4x, float deltaHeight, float deltaAngle, Vector3 center, bool followTerrain, Dictionary <ushort, ushort> clonedNodes) { BuildingState state = instanceState as BuildingState; Vector3 newPosition = matrix4x.MultiplyPoint(state.position - center); newPosition.y = state.position.y + deltaHeight; float terrainHeight = TerrainManager.instance.SampleOriginalRawHeightSmooth(newPosition); if (followTerrain) { newPosition.y = newPosition.y + terrainHeight - state.terrainHeight; } MoveableBuilding cloneInstance = null; BuildingInfo info = state.info as BuildingInfo; float newAngle = state.angle + deltaAngle; if (BuildingManager.instance.CreateBuilding(out ushort clone, ref SimulationManager.instance.m_randomizer, info, newPosition, newAngle, state.length, SimulationManager.instance.m_currentBuildIndex)) { SimulationManager.instance.m_currentBuildIndex++; InstanceID cloneID = default(InstanceID); cloneID.Building = clone; cloneInstance = new MoveableBuilding(cloneID); if ((state.flags & Building.Flags.Completed) != Building.Flags.None) { buildingBuffer[clone].m_flags = buildingBuffer[clone].m_flags | Building.Flags.Completed; } if ((state.flags & Building.Flags.FixedHeight) != Building.Flags.None) { buildingBuffer[clone].m_flags = buildingBuffer[clone].m_flags | Building.Flags.FixedHeight; } // TODO: when should the flag be set? if (Mathf.Abs(terrainHeight - newPosition.y) > 0.01f) { AddFixedHeightFlag(clone); } else { RemoveFixedHeightFlag(clone); } if (info.m_subBuildings != null && info.m_subBuildings.Length != 0) { Matrix4x4 subMatrix4x = default(Matrix4x4); subMatrix4x.SetTRS(newPosition, Quaternion.AngleAxis(newAngle * Mathf.Rad2Deg, Vector3.down), Vector3.one); for (int i = 0; i < info.m_subBuildings.Length; i++) { BuildingInfo subInfo = info.m_subBuildings[i].m_buildingInfo; Vector3 subPosition = subMatrix4x.MultiplyPoint(info.m_subBuildings[i].m_position); float subAngle = info.m_subBuildings[i].m_angle * 0.0174532924f + newAngle; if (BuildingManager.instance.CreateBuilding(out ushort subClone, ref SimulationManager.instance.m_randomizer, subInfo, subPosition, subAngle, 0, SimulationManager.instance.m_currentBuildIndex)) { SimulationManager.instance.m_currentBuildIndex++; if (info.m_subBuildings[i].m_fixedHeight) { buildingBuffer[subClone].m_flags = buildingBuffer[subClone].m_flags | Building.Flags.FixedHeight; } } if (clone != 0 && subClone != 0) { buildingBuffer[clone].m_subBuilding = subClone; buildingBuffer[subClone].m_parentBuilding = clone; buildingBuffer[subClone].m_flags = buildingBuffer[subClone].m_flags | Building.Flags.Untouchable; clone = subClone; } } } } return(cloneInstance); }
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>(); Building[] buildingBuffer = BuildingManager.instance.m_buildings.m_buffer; // Recreate nodes foreach (InstanceState state in m_states) { if (state.instance.id.Type == InstanceType.NetNode) { Instance clone = state.instance.Clone(state, null); toReplace.Add(state.instance, clone); clonedNodes.Add(state.instance.id.NetNode, clone.id.NetNode); ActionQueue.instance.UpdateNodeIdInStateHistory(state.instance.id.NetNode, clone.id.NetNode); } } // Recreate everything except nodes and segments foreach (InstanceState state in m_states) { 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); 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; } } } } } // Recreate segments foreach (InstanceState state in m_states) { 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); MoveItTool.NS.SetSegmentModifiers(clone.id.NetSegment, segmentState); } } 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(); } }
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 Undo() { if (m_states == null) { return; } Dictionary <Instance, Instance> toReplace = new Dictionary <Instance, Instance>(); Dictionary <ushort, ushort> clonedNodes = new Dictionary <ushort, ushort>(); // Recreate nodes foreach (InstanceState state in m_states) { if (state.instance.id.Type == InstanceType.NetNode) { Instance clone = state.instance.Clone(state, null); toReplace.Add(state.instance, clone); clonedNodes.Add(state.instance.id.NetNode, clone.id.NetNode); ActionQueue.instance.UpdateNodeIdInStateHistory(state.instance.id.NetNode, clone.id.NetNode); //Debug.Log($"Cloned N:{state.instance.id.NetNode}->{clone.id.NetNode}"); } } // Recreate everything except nodes and segments foreach (InstanceState state in m_states) { if (state.instance.id.Type == InstanceType.NetNode) { continue; } if (state.instance.id.Type == InstanceType.NetSegment) { continue; } Instance clone = state.instance.Clone(state, clonedNodes); toReplace.Add(state.instance, clone); // Add attached nodes to the clonedNode list so other segments reconnect if (state.instance.id.Type == InstanceType.Building) { BuildingState buildingState = state as BuildingState; List <ushort> origNodeIds = new List <ushort>(); MoveableBuilding cb = clone as MoveableBuilding; ushort cloneNodeId = ((Building)cb.data).m_netNode; if (cloneNodeId != 0) { int c = 0; string msg2 = "Original attached nodes:"; foreach (InstanceState i in buildingState.subStates) { if (i is NodeState ns) { InstanceID instanceID = default(InstanceID); instanceID.RawData = ns.id; //msg2 += $"\n{c} - Attached node #{instanceID.NetNode}: {ns.Info.Name}"; origNodeIds.Insert(c++, instanceID.NetNode); } } //Debug.Log(msg2); c = 0; msg2 = ""; while (cloneNodeId != 0) { ushort origNodeId = origNodeIds[c]; NetNode clonedAttachedNode = 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); msg2 += $"\n{c} - {origNodeId} -> {cloneNodeId} {clonedAttachedNode.Info.GetAI()}"; cloneNodeId = clonedAttachedNode.m_nextBuildingNode; if (++c > 32768) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } Debug.Log(msg2); } } } //string msg = "Cloned Nodes:\n"; //foreach (KeyValuePair<ushort, ushort> kvp in clonedNodes) //{ // msg += $"{kvp.Key} => {kvp.Value}\n"; //} //Debug.Log(msg); // Recreate segments foreach (InstanceState state in m_states) { if (state.instance.id.Type == InstanceType.NetSegment) { SegmentState segState = state as SegmentState; if (!clonedNodes.ContainsKey(segState.startNode)) { InstanceID instanceID = InstanceID.Empty; instanceID.NetNode = segState.startNode; // Don't clone if node is missing if (!((Instance)instanceID).isValid) { continue; } clonedNodes.Add(segState.startNode, segState.startNode); } if (!clonedNodes.ContainsKey(segState.endNode)) { InstanceID instanceID = InstanceID.Empty; instanceID.NetNode = segState.endNode; // Don't clone if node is missing if (!((Instance)instanceID).isValid) { continue; } clonedNodes.Add(segState.endNode, segState.endNode); } Instance clone = state.instance.Clone(state, clonedNodes); toReplace.Add(state.instance, clone); } } if (replaceInstances) { ReplaceInstances(toReplace); ActionQueue.instance.ReplaceInstancesBackward(toReplace); selection = m_oldSelection; } }