public override InstanceState GetState() { BuildingState state = new BuildingState(); state.instance = this; state.info = info; state.position = buildingBuffer[id.Building].m_position; state.angle = buildingBuffer[id.Building].m_angle; state.terrainHeight = TerrainManager.instance.SampleOriginalRawHeightSmooth(state.position); state.flags = buildingBuffer[id.Building].m_flags; state.length = buildingBuffer[id.Building].Length; List <InstanceState> subStates = new List <InstanceState>(); foreach (Instance instance in subInstances) { if (instance != null && instance.isValid) { subStates.Add(instance.GetState()); } } if (subStates.Count > 0) { state.subStates = subStates.ToArray(); } return(state); }
public override void RenderCloneGeometry(InstanceState instanceState, ref Matrix4x4 matrix4x, Vector3 deltaPosition, float deltaAngle, Vector3 center, bool followTerrain, RenderManager.CameraInfo cameraInfo, Color toolColor) { BuildingState state = instanceState as BuildingState; BuildingInfo buildingInfo = state.info as BuildingInfo; Color color = GetColor(state.instance.id.Building, buildingInfo); Vector3 newPosition = matrix4x.MultiplyPoint(state.position - center); newPosition.y = state.position.y + deltaPosition.y; if (followTerrain) { newPosition.y = newPosition.y - state.terrainHeight + TerrainManager.instance.SampleOriginalRawHeightSmooth(newPosition); } float newAngle = state.angle + deltaAngle; buildingInfo.m_buildingAI.RenderBuildGeometry(cameraInfo, newPosition, newAngle, 0); BuildingTool.RenderGeometry(cameraInfo, buildingInfo, state.length, newPosition, newAngle, false, color); if (buildingInfo.m_subBuildings != null && buildingInfo.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 < buildingInfo.m_subBuildings.Length; i++) { BuildingInfo buildingInfo2 = buildingInfo.m_subBuildings[i].m_buildingInfo; Vector3 position = subMatrix4x.MultiplyPoint(buildingInfo.m_subBuildings[i].m_position); float angle = buildingInfo.m_subBuildings[i].m_angle * 0.0174532924f + newAngle; buildingInfo2.m_buildingAI.RenderBuildGeometry(cameraInfo, position, angle, 0); BuildingTool.RenderGeometry(cameraInfo, buildingInfo2, 0, position, angle, true, color); } } }
// Constructor for FindIt object public CloneActionFindIt(PrefabInfo prefab) { m_oldSelection = selection; m_states.Clear(); Vector3 position = MoveItTool.RaycastMouseLocation(); InstanceState state = new InstanceState(); if (prefab is BuildingInfo) { state = new BuildingState { isSubInstance = false, isHidden = false, flags = Building.Flags.Completed }; state.Info.Prefab = prefab; InstanceID id = new InstanceID { Building = 1, Type = InstanceType.Building }; state.instance = new MoveableBuilding(id); } else if (prefab is PropInfo) { state = new PropState { fixedHeight = false, single = false, }; state.Info.Prefab = prefab; InstanceID id = new InstanceID { Prop = 1, Type = InstanceType.Prop }; state.instance = new MoveableProp(id); } else if (prefab is TreeInfo) { state = new TreeState { fixedHeight = false, single = false, }; state.Info.Prefab = prefab; InstanceID id = new InstanceID { Tree = 1, Type = InstanceType.Tree }; state.instance = new MoveableTree(id); } state.position = position; state.terrainHeight = position.y; m_states.Add(state); center = position; }
// 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); MoveableNode node = instance as MoveableNode; if (pillarState != null) { pillarState.instance = node.Pillar; if (pillarState.instance == null) { pillarState = null; } } }
public override InstanceState SaveToState(bool integrate = true) { BuildingState state = new BuildingState { instance = this, Info = Info, position = buildingBuffer[id.Building].m_position, angle = buildingBuffer[id.Building].m_angle, flags = buildingBuffer[id.Building].m_flags, length = buildingBuffer[id.Building].Length, isSubInstance = isSubInstance, isHidden = isHidden, isCustomContent = Info.Prefab.m_isCustomContent }; state.terrainHeight = TerrainManager.instance.SampleOriginalRawHeightSmooth(state.position); List <InstanceState> subStates = new List <InstanceState>(); foreach (Instance subInstance in subInstances) { if (subInstance != null && subInstance.isValid) { if (subInstance.id.Building > 0) { subStates.Add(((MoveableBuilding)subInstance).SaveToState()); } else { subStates.Add(subInstance.SaveToState()); } } } if (subStates.Count > 0) { state.subStates = subStates.ToArray(); } state.SaveIntegrations(integrate); return(state); }
public InstanceState GetBuildingState(int depth) { BuildingState state = new BuildingState { instance = this, Info = Info, position = buildingBuffer[id.Building].m_position, angle = buildingBuffer[id.Building].m_angle, flags = buildingBuffer[id.Building].m_flags, length = buildingBuffer[id.Building].Length }; state.terrainHeight = TerrainManager.instance.SampleOriginalRawHeightSmooth(state.position); List <InstanceState> subStates = new List <InstanceState>(); foreach (Instance subInstance in subInstances) { if (subInstance != null && subInstance.isValid) { if (subInstance.id.Building > 0) { if (depth < 1) { subStates.Add(((MoveableBuilding)subInstance).GetBuildingState(depth + 1)); } } else { subStates.Add(subInstance.GetState()); } } } if (subStates.Count > 0) { state.subStates = subStates.ToArray(); } return(state); }
public override void Transform(InstanceState instanceState, ref Matrix4x4 matrix4x, float deltaHeight, float deltaAngle, Vector3 center, bool followTerrain) { 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; } // TODO: when should the flag be set? if (Mathf.Abs(terrainHeight - newPosition.y) > 0.01f) { AddFixedHeightFlag(id.Building); } else { RemoveFixedHeightFlag(id.Building); } Move(newPosition, state.angle + deltaAngle); if (state.subStates != null) { foreach (InstanceState subState in state.subStates) { Vector3 subPosition = subState.position - center; subPosition = matrix4x.MultiplyPoint(subPosition); subPosition.y = subState.position.y - state.position.y + newPosition.y; subState.instance.Move(subPosition, subState.angle + deltaAngle); } } }
public override void RenderCloneOverlay(InstanceState instanceState, ref Matrix4x4 matrix4x, Vector3 deltaPosition, float deltaAngle, Vector3 center, bool followTerrain, RenderManager.CameraInfo cameraInfo, Color toolColor) { if (MoveItTool.m_isLowSensitivity && MoveItTool.hideSelectorsOnLowSensitivity) { return; } BuildingState state = instanceState as BuildingState; BuildingInfo buildingInfo = state.Info.Prefab as BuildingInfo; Vector3 newPosition = matrix4x.MultiplyPoint(state.position - center); newPosition.y = state.position.y + deltaPosition.y; if (followTerrain) { newPosition.y = newPosition.y - state.terrainHeight + TerrainManager.instance.SampleOriginalRawHeightSmooth(newPosition); } float newAngle = state.angle + deltaAngle; buildingInfo.m_buildingAI.RenderBuildOverlay(cameraInfo, toolColor, newPosition, newAngle, default); BuildingTool.RenderOverlay(cameraInfo, buildingInfo, state.length, newPosition, newAngle, toolColor, false); if (buildingInfo.m_subBuildings != null && buildingInfo.m_subBuildings.Length != 0) { Matrix4x4 subMatrix4x = default; subMatrix4x.SetTRS(newPosition, Quaternion.AngleAxis(newAngle * Mathf.Rad2Deg, Vector3.down), Vector3.one); for (int i = 0; i < buildingInfo.m_subBuildings.Length; i++) { BuildingInfo buildingInfo2 = buildingInfo.m_subBuildings[i].m_buildingInfo; Vector3 position = subMatrix4x.MultiplyPoint(buildingInfo.m_subBuildings[i].m_position); float angle = buildingInfo.m_subBuildings[i].m_angle * 0.0174532924f + newAngle; buildingInfo2.m_buildingAI.RenderBuildOverlay(cameraInfo, toolColor, position, angle, default); BuildingTool.RenderOverlay(cameraInfo, buildingInfo2, 0, position, angle, toolColor, true); } } }
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 Transform(InstanceState instanceState, ref Matrix4x4 matrix4x, float deltaHeight, float deltaAngle, Vector3 center, bool followTerrain) { BuildingState state = instanceState as BuildingState; Vector3 newPosition = matrix4x.MultiplyPoint(state.position - center); newPosition.y = state.position.y + deltaHeight; float terrainHeight = TerrainManager.instance.SampleOriginalRawHeightSmooth(newPosition); bool isFixed = GetFixedHeightFlag(id.Building); if (!isFixed) { AddFixedHeightFlag(id.Building); } if (followTerrain) { newPosition.y = newPosition.y + terrainHeight - state.terrainHeight; } AddFixedHeightFlag(id.Building); Move(newPosition, state.angle + deltaAngle); Matrix4x4 matrixSub = default; matrixSub.SetTRS(Vector3.zero, Quaternion.AngleAxis(deltaAngle * Mathf.Rad2Deg, Vector3.down), Vector3.one); if (state.subStates != null) { foreach (InstanceState subState in state.subStates) { Vector3 subOffset = (subState.position - center) - (state.position - center); Vector3 subPosition = TransformPosition + matrixSub.MultiplyPoint(subOffset); subPosition.y = subState.position.y - state.position.y + newPosition.y; subState.instance.Move(subPosition, subState.angle + deltaAngle); if (subState.instance is MoveableNode mn) { if (mn.Pillar != null) { mn.Pillar.Move(subPosition, subState.angle + deltaAngle); } } if (subState is BuildingState bs) { if (bs.subStates != null) { foreach (InstanceState subSubState in bs.subStates) { Vector3 subSubOffset = (subSubState.position - center) - (state.position - center); Vector3 subSubPosition = TransformPosition + matrixSub.MultiplyPoint(subSubOffset); subSubPosition.y = subSubState.position.y - state.position.y + newPosition.y; subSubState.instance.Move(subSubPosition, subSubState.angle + deltaAngle); if (subSubState.instance is MoveableNode mn2) { if (mn2.Pillar != null) { mn2.Pillar.Move(subSubPosition, subSubState.angle + deltaAngle); } } } } } } } if (!isFixed && Mathf.Abs(terrainHeight - newPosition.y) < 0.01f) { RemoveFixedHeightFlag(id.Building); } }
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; } }