public override Instance Clone(InstanceState instanceState, ref Matrix4x4 matrix4x, float deltaHeight, float deltaAngle, Vector3 center, bool followTerrain, Dictionary <ushort, ushort> clonedNodes) { NodeState state = instanceState as NodeState; Vector3 newPosition = matrix4x.MultiplyPoint(state.position - center); newPosition.y = state.position.y + deltaHeight; if (followTerrain) { newPosition.y = newPosition.y + TerrainManager.instance.SampleOriginalRawHeightSmooth(newPosition) - state.terrainHeight; } Instance cloneInstance = null; if (NetManager.instance.CreateNode(out ushort clone, ref SimulationManager.instance.m_randomizer, state.info as NetInfo, newPosition, SimulationManager.instance.m_currentBuildIndex)) { SimulationManager.instance.m_currentBuildIndex++; InstanceID cloneID = default(InstanceID); cloneID.NetNode = clone; cloneInstance = new MoveableNode(cloneID); nodeBuffer[clone].m_flags = state.flags; // TODO: Clone pillar instead? nodeBuffer[clone].Info.m_netAI.GetNodeBuilding(clone, ref nodeBuffer[clone], out BuildingInfo newBuilding, out float heightOffset); nodeBuffer[clone].UpdateBuilding(clone, newBuilding, heightOffset); } 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 Instance Clone(InstanceState instanceState, Dictionary <ushort, ushort> clonedNodes) { NodeState state = instanceState as NodeState; MoveableNode cloneInstance = null; if (NetManager.instance.CreateNode(out ushort clone, ref SimulationManager.instance.m_randomizer, state.Info.Prefab as NetInfo, state.position, SimulationManager.instance.m_currentBuildIndex)) { SimulationManager.instance.m_currentBuildIndex++; InstanceID cloneID = default; cloneID.NetNode = clone; cloneInstance = new MoveableNode(cloneID); nodeBuffer[clone].m_flags = state.flags; // TODO: Clone pillar instead? nodeBuffer[clone].Info.m_netAI.GetNodeBuilding(clone, ref nodeBuffer[clone], out BuildingInfo newBuilding, out float heightOffset); nodeBuffer[clone].UpdateBuilding(clone, newBuilding, heightOffset); } return(cloneInstance); }
private void OnLeftClick() { DebugUtils.Log("OnLeftClick: " + ToolState); if (POProcessing > 0) { return; } if (ToolState == ToolStates.Default || ToolState == ToolStates.DrawingSelection || ToolState == ToolStates.ToolActive) { Event e = Event.current; if (m_hoverInstance == null) { return; } #region Debug Ouput //Instance instance = m_hoverInstance; //InstanceID instanceID = instance.id; //Log.Debug($"instance:{(instance == null ? "null" : instance.GetType().ToString())}"); //if (instanceID.Building > 0) //{ // MoveableBuilding mb = (MoveableBuilding)instance; // string msg = $"{mb.id.Building}:{mb.Info.Name}\n"; // //Log.Debug(msg); // foreach (Instance subInstance in mb.subInstances) // { // msg += $" - {subInstance.id.Building}/{subInstance.id.NetNode}: {subInstance.Info.Name}\n"; // //Log.Debug(msg); // if (subInstance.id.Building > 0) // { // foreach (Instance subSubInstance in ((MoveableBuilding)subInstance).subInstances) // { // msg += $" - {subSubInstance.id.Building}/{subSubInstance.id.NetNode}: {subSubInstance.Info.Name}\n"; // //Log.Debug(msg); // } // } // } // msg += "End"; // Log.Debug(msg); //} #endregion if (!(ActionQueue.instance.current is SelectAction)) { ActionQueue.instance.Push(new SelectAction(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))); } else { ActionQueue.instance.Invalidate(); } if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //"if (e.shift)" apparently fails in Linux { if (e.alt && m_hoverInstance is MoveableSegment ms && FindOwnerBuilding(ms.id.NetSegment, 363f) == 0) { MoveableNode closest = ms.GetNodeByDistance(); MoveableNode furthest = ms.GetNodeByDistance(true); if (!Action.selection.Contains(closest)) { Action.selection.Add(closest); } else if (!Action.selection.Contains(furthest)) { Action.selection.Add(furthest); } else { Action.selection.Remove(furthest); } } else { if (Action.selection.Contains(m_hoverInstance)) { Action.selection.RemoveObject(m_hoverInstance); } else { Action.selection.AddObject(m_hoverInstance); } } } else { if (e.alt && m_hoverInstance is MoveableSegment ms && FindOwnerBuilding(ms.id.NetSegment, 363f) == 0) { MoveableNode closest = ms.GetNodeByDistance(); MoveableNode furthest = ms.GetNodeByDistance(true); if (Action.selection.Contains(closest) && !Action.selection.Contains(furthest)) { Action.selection.Clear(); Action.selection.Add(furthest); } else { Action.selection.Clear(); Action.selection.Add(closest); } }
public BulldozeAction() { HashSet <Instance> newSelection = new HashSet <Instance>(selection); HashSet <Instance> extraNodes = new HashSet <Instance>(); HashSet <ushort> segments = new HashSet <ushort>(); // Segments to be removed NetManager netManager = Singleton <NetManager> .instance; // Add any segments whose node is selected foreach (Instance instance in selection) { if (instance.isValid) { if (instance.id.Type == InstanceType.NetSegment) { segments.Add(instance.id.NetSegment); } else if (instance.id.Type == InstanceType.NetNode) { for (int i = 0; i < 8; i++) { ushort segment = netManager.m_nodes.m_buffer[instance.id.NetNode].GetSegment(i); if (segment != 0) { InstanceID instanceID = default; instanceID.NetSegment = segment; if (selection.Contains(instanceID)) { continue; } newSelection.Add(new MoveableSegment(instanceID)); segments.Add(segment); } } } else if (instance.id.Type == InstanceType.Building) { Building building = (Building)((MoveableBuilding)instance).data; ushort nodeId = building.m_netNode; int c = 0; while (nodeId != 0) { NetNode node = netManager.m_nodes.m_buffer[nodeId]; if (node.m_building == 0 || node.Info.m_class.m_layer == ItemClass.Layer.WaterPipes) { // Exclude attached nodes with attached buildings (e.g. water buildings) for (int i = 0; i < 8; i++) { ushort segmentId = node.GetSegment(i); if (segmentId != 0 && MoveableSegment.isSegmentValid(segmentId) && ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Untouchable) == NetSegment.Flags.None)) { InstanceID instanceID = default; instanceID.NetSegment = segmentId; if (selection.Contains(instanceID)) { continue; } newSelection.Add(new MoveableSegment(instanceID)); segments.Add(segmentId); } } } nodeId = node.m_nextBuildingNode; if (++c > 32768) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Nodes: Invalid list detected!\n" + Environment.StackTrace); break; } } } } } // Add any nodes whose segments are all selected foreach (Instance instance in newSelection) { if (instance.isValid) { if (instance.id.Type == InstanceType.NetSegment) { ushort segId = instance.id.NetSegment; ushort[] nodeIds = { netManager.m_segments.m_buffer[segId].m_startNode, netManager.m_segments.m_buffer[segId].m_endNode }; foreach (ushort id in nodeIds) { bool toDelete = true; NetNode node = netManager.m_nodes.m_buffer[id]; for (int i = 0; i < 8; i++) { if (node.GetSegment(i) != 0 && !segments.Contains(node.GetSegment(i))) { toDelete = false; break; } } if (toDelete) { if (node.Info.m_class.m_layer == ItemClass.Layer.WaterPipes) { foreach (Building b in BuildingManager.instance.m_buildings.m_buffer) { if (b.m_netNode == id) { toDelete = false; break; } } } } if (toDelete) { InstanceID instanceId = default; instanceId.NetNode = id; MoveableNode mn = new MoveableNode(instanceId); if (newSelection.Contains(mn)) { continue; } extraNodes.Add(mn); } } } } } // Sort segments by buildIndex HashSet <Instance> sorted = new HashSet <Instance>(); List <uint> indexes = new List <uint>(); foreach (Instance instance in newSelection) { if (instance.id.Type != InstanceType.NetSegment) { sorted.Add(instance); } else { uint bi = ((NetSegment)instance.data).m_buildIndex; if (!indexes.Contains(bi)) { indexes.Add(bi); } } } indexes.Sort(); foreach (uint i in indexes) { foreach (Instance instance in newSelection) { if (instance.id.Type == InstanceType.NetSegment) { if (((NetSegment)instance.data).m_buildIndex == i) { sorted.Add(instance); } } } } foreach (Instance instance in sorted) { m_states.Add(instance.SaveToState()); } foreach (Instance instance in extraNodes) { m_states.Add(instance.SaveToState()); } }
public override void Do() { float angleDelta; float heightDelta; float distance; Matrix4x4 matrix = default(Matrix4x4); if (IsQuick) { if (selection.Count != 1) { return; } foreach (Instance instance in selection) // Is this really the best way to get the value of selection[0]? { if (!instance.isValid || !(instance is MoveableNode nodeInstance)) { return; } NetNode node = nodeBuffer[nodeInstance.id.NetNode]; int c = 0; for (int i = 0; i < 8; i++) { ushort segId = 0; if ((segId = node.GetSegment(i)) > 0) { if (c > 1) { return; // More than 2 segments found } NetSegment segment = segmentBuffer[segId]; InstanceID instanceID = default(InstanceID); if (segment.m_startNode == nodeInstance.id.NetNode) { instanceID.NetNode = segment.m_endNode; } else { instanceID.NetNode = segment.m_startNode; } keyInstance[c] = new MoveableNode(instanceID); c++; } } if (c != 2) { return; } } } //bool isAllNodes = true; //foreach (Instance instance in selection) //{ // if(instance.isValid && instance.id.Type != InstanceType.NetNode && instance.id.Type != InstanceType.NetSegment) // { // isAllNodes = false; // break; // } //} //if (isAllNodes) //{ // List<ushort> unsortedNodes = new List<ushort>(); // List<ushort> connectingSegments = new List<ushort>(); // foreach(Instance instance in selection) // { // if (instance.id.Type != InstanceType.NetSegment) // { // unsortedNodes.Add(instance.id.NetNode); // } // } // if (unsortedNodes.Count < 3) // { // Debug.LogError("[Segment Slope Smoother] Not enough nodes to complete process. You probably won't see this error but it's best to put it here anyway, just in case."); // return; // } // List<List<ushort>> fragmentXZ = new List<List<ushort>>(); // List<ushort> endpoints = new List<ushort>(); // for (int i = 0; i < unsortedNodes.Count; i++) // { // NetNode node = nodeBuffer[unsortedNodes[i]]; // List<ushort> connections = new List<ushort>(); // connections.Add(unsortedNodes[i]); // for (int j = 0; j < unsortedNodes.Count; j++) // { // if (i == j) continue; // if (node.IsConnectedTo(unsortedNodes[j])) // { // connections.Add(unsortedNodes[j]); // } // if (connections.Count > 3) // { // the node itself, then both connections // Debug.LogError("[Segment Slope Smoother] Validation error: Too many connections! Each node should be only connected by segments to at most two other nodes."); // return; // } // } // if (connections.Count == 2) // { // the node itself, and one connection // endpoints.Add(unsortedNodes[i]); // } // if (connections.Count == 1) // { // Debug.LogError("[Segment Slope Smoother] Validation error: No connections! Each node needs at least one other segment connection."); // return; // } // fragmentXZ.Add(connections); // } // List<ushort> sortedNodes = new List<ushort>(); // bool incomplete = true; // int index = -1; // for (int i = 0; i < fragmentXZ.Count; i++) // { // if (fragmentXZ[i].Count == 2) // { // index = fragmentXZ.FindIndex(e => e[0] == fragmentXZ[i][1]); // sortedNodes.Add(fragmentXZ[i][0]); // sortedNodes.Add(fragmentXZ[i][1]); // if (index == -1) // { // Debug.LogError("[Segment Slope Smoother] Sort error: Invalid path! Endpoint is connected to undefined node."); // return; // } // break; // } // } // while (incomplete) // { // for (int i = 0; i < fragmentXZ.Count; i++) // { // for (int j = 1; j <= 2; j++) // { // if ((fragmentXZ[i][0] == fragmentXZ[index][j] && !sortedNodes.Contains(fragmentXZ[index][j]))) // { // sortedNodes.Add(fragmentXZ[i][0]); // if (fragmentXZ[i].Count == 2) // { // incomplete = false; // break; // } // index = i; // } // } // } // } // for (var i = 0; i < sortedNodes.Count - 1; i++) // { // for (var j = 0; j < nodeBuffer[sortedNodes[i]].CountSegments(); j++) // { // ushort segmentID = nodeBuffer[sortedNodes[i]].GetSegment(j); // NetSegment testedSegment = segmentBuffer[segmentID]; // if ((testedSegment.m_startNode == sortedNodes[i] || testedSegment.m_endNode == sortedNodes[i]) && (testedSegment.m_startNode == sortedNodes[i + 1] || testedSegment.m_endNode == sortedNodes[i + 1])) // { // connectingSegments.Add(segmentID); // break; // } // } // } // float totalLength = 0; // List<float> segmentLinearLengthsXZ = new List<float>(); // for (int i = 0; i < connectingSegments.Count; i++) // { // NetSegment calcSegment = segmentBuffer[connectingSegments[i]]; // float linearDistanceXZ = (float)Math.Sqrt(Math.Pow(calcSegment.m_averageLength, 2) - Math.Pow(nodeBuffer[calcSegment.m_startNode].m_position.y - nodeBuffer[calcSegment.m_endNode].m_position.y, 2)); // totalLength += linearDistanceXZ; // segmentLinearLengthsXZ.Add(linearDistanceXZ); // } // float incrementLength = 0; // NetNode startNode = nodeBuffer[sortedNodes[0]]; // NetNode endNode = nodeBuffer[sortedNodes[sortedNodes.Count - 1]]; // for (int i = 0; i < sortedNodes.Count; i++) // { // float Nln = ((endNode.m_position.y - startNode.m_position.y) / totalLength) * incrementLength + startNode.m_position.y; // Singleton<NetManager>.instance.MoveNode(sortedNodes[i], new Vector3(nodeBuffer[sortedNodes[i]].m_position.x, Nln, nodeBuffer[sortedNodes[i]].m_position.z)); // if (i != sortedNodes.Count - 1) incrementLength += segmentLinearLengthsXZ[i]; // } //} //else { angleDelta = 0 - (float)Math.Atan2(PointB.position.z - PointA.position.z, PointB.position.x - PointA.position.x); heightDelta = PointB.position.y - PointA.position.y; distance = (float)Math.Sqrt(Math.Pow(PointB.position.z - PointA.position.z, 2) + Math.Pow(PointB.position.x - PointA.position.x, 2)); //string msg = $"\nA:{PointA.position}, B:{PointB.position}\nAng:{angleDelta} ({angleDelta * Mathf.Rad2Deg}) - H:{heightDelta} - D:{distance}"; foreach (InstanceState state in m_states) { float distanceOffset, heightOffset; matrix.SetTRS(PointA.position, Quaternion.AngleAxis(angleDelta * Mathf.Rad2Deg, Vector3.down), Vector3.one); distanceOffset = (matrix.MultiplyPoint(state.position - PointA.position) - PointA.position).x; heightOffset = distanceOffset / distance * heightDelta; state.instance.SetHeight(Mathf.Clamp(PointA.position.y + heightOffset, 0f, 1000f)); //msg += $"\nx-offset:{distanceOffset} h-offset:{heightOffset}"; } //Debug.Log(msg); } }
public override void Do() { float angleDelta; float heightDelta; float distance; Matrix4x4 matrix = default; if (mode == Modes.Quick) { if (selection.Count != 1) { return; } Instance instance = selection.First(); if (!instance.isValid || !(instance is MoveableNode nodeInstance)) { return; } NetNode node = nodeBuffer[nodeInstance.id.NetNode]; int c = 0; for (int i = 0; i < 8; i++) { ushort segId; if ((segId = node.GetSegment(i)) > 0) { if (c > 1) { return; // More than 2 segments found } NetSegment segment = segmentBuffer[segId]; InstanceID instanceID = default; if (segment.m_startNode == nodeInstance.id.NetNode) { instanceID.NetNode = segment.m_endNode; } else { instanceID.NetNode = segment.m_startNode; } keyInstance[c] = new MoveableNode(instanceID); c++; } } } else if (mode == Modes.Auto) { if (selection.Count < 2) { return; } GetExtremeObjects(out keyInstance[0], out keyInstance[1]); } angleDelta = 0 - (float)Math.Atan2(PointB.position.z - PointA.position.z, PointB.position.x - PointA.position.x); heightDelta = PointB.position.y - PointA.position.y; distance = (float)Math.Sqrt(Math.Pow(PointB.position.z - PointA.position.z, 2) + Math.Pow(PointB.position.x - PointA.position.x, 2)); foreach (InstanceState state in m_states) { float distanceOffset, heightOffset; matrix.SetTRS(PointA.position, Quaternion.AngleAxis(angleDelta * Mathf.Rad2Deg, Vector3.down), Vector3.one); distanceOffset = (matrix.MultiplyPoint(state.position - PointA.position) - PointA.position).x; heightOffset = distanceOffset / distance * heightDelta; state.instance.SetHeight(Mathf.Clamp(PointA.position.y + heightOffset, 0f, 1000f)); } }
public BulldozeAction() { HashSet <Instance> newSelection = new HashSet <Instance>(selection); HashSet <Instance> extraNodes = new HashSet <Instance>(); HashSet <ushort> segments = new HashSet <ushort>(); // Segments to be removed //string msg = $"\nBasic Selection: {selection.Count}\n"; // Add any segments whose node is selected foreach (Instance instance in selection) { if (instance.isValid) { if (instance.id.Type == InstanceType.NetSegment) { segments.Add(instance.id.NetSegment); } else if (instance.id.Type == InstanceType.NetNode) { for (int i = 0; i < 8; i++) { ushort segment = NetManager.instance.m_nodes.m_buffer[instance.id.NetNode].GetSegment(i); if (segment != 0) { InstanceID instanceID = default(InstanceID); instanceID.NetSegment = segment; if (selection.Contains(instanceID)) { continue; } newSelection.Add(new MoveableSegment(instanceID)); segments.Add(segment); } } } else if (instance.id.Type == InstanceType.Building) { Building building = (Building)((MoveableBuilding)instance).data; ushort nodeId = building.m_netNode; int c = 0; while (nodeId != 0) { NetNode node = NetManager.instance.m_nodes.m_buffer[nodeId]; for (int i = 0; i < 8; i++) { ushort segmentId = node.GetSegment(i); if (segmentId != 0 && MoveableSegment.isSegmentValid(segmentId) && ((NetManager.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Untouchable) == NetSegment.Flags.None)) { InstanceID instanceID = default(InstanceID); instanceID.NetSegment = segmentId; if (selection.Contains(instanceID)) { continue; } newSelection.Add(new MoveableSegment(instanceID)); segments.Add(segmentId); } } nodeId = node.m_nextBuildingNode; if (++c > 32768) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } } } } //msg += $"Selection With Extra Segments: {newSelection.Count}\nTotal Segments: {segments.Count}\n"; // Add any nodes whose segments are all selected foreach (Instance instance in newSelection) { if (instance.isValid) { if (instance.id.Type == InstanceType.NetSegment) { ushort segId = instance.id.NetSegment; ushort[] nodeIds = { NetManager.instance.m_segments.m_buffer[segId].m_startNode, NetManager.instance.m_segments.m_buffer[segId].m_endNode }; foreach (ushort id in nodeIds) { bool toDelete = true; NetNode node = NetManager.instance.m_nodes.m_buffer[id]; for (int i = 0; i < 8; i++) { if (node.GetSegment(i) != 0 && !segments.Contains(node.GetSegment(i))) { toDelete = false; break; } } if (toDelete) { InstanceID instanceId = default(InstanceID); instanceId.NetNode = id; MoveableNode mn = new MoveableNode(instanceId); if (newSelection.Contains(mn)) { continue; } extraNodes.Add(mn); } } } } } foreach (Instance instance in newSelection) { m_states.Add(instance.GetState()); } foreach (Instance instance in extraNodes) { m_states.Add(instance.GetState()); } //Debug.Log(msg + $"Final Selection: {m_states.Count}"); }
public override void Do() { float angleDelta; float heightDelta; float distance; Matrix4x4 matrix = default(Matrix4x4); if (IsQuick) { if (selection.Count != 1) { return; } foreach (Instance instance in selection)// Is this really the best way to get the value of selection[0]? { if (!instance.isValid || !(instance is MoveableNode nodeInstance)) { return; } NetNode node = nodeBuffer[nodeInstance.id.NetNode]; int c = 0; for (int i = 0; i < 8; i++) { ushort segId = 0; if ((segId = node.GetSegment(i)) > 0) { if (c > 1) { return; // More than 2 segments found } NetSegment segment = segmentBuffer[segId]; if (segment.m_startNode == nodeInstance.id.NetNode) { InstanceID instanceID = default(InstanceID); instanceID.NetNode = segment.m_endNode; keyInstance[c] = new MoveableNode(instanceID); } else { InstanceID instanceID = default(InstanceID); instanceID.NetNode = segment.m_startNode; keyInstance[c] = new MoveableNode(instanceID); } c++; } } if (c != 2) { return; } } } angleDelta = 0 - (float)Math.Atan2(PointB.position.z - PointA.position.z, PointB.position.x - PointA.position.x); heightDelta = PointB.position.y - PointA.position.y; distance = (float)Math.Sqrt(Math.Pow(PointB.position.z - PointA.position.z, 2) + Math.Pow(PointB.position.x - PointA.position.x, 2)); string msg = $"\nA:{PointA.position}, B:{PointB.position}\nAng:{angleDelta} ({angleDelta * Mathf.Rad2Deg}) - H:{heightDelta} - D:{distance}"; foreach (InstanceState state in m_states) { float distanceOffset, heightOffset; matrix.SetTRS(PointA.position, Quaternion.AngleAxis(angleDelta * Mathf.Rad2Deg, Vector3.down), Vector3.one); distanceOffset = (matrix.MultiplyPoint(state.position - PointA.position) - PointA.position).x; heightOffset = distanceOffset / distance * heightDelta; state.instance.SetHeight(Mathf.Clamp(PointA.position.y + heightOffset, 0f, 4000f)); msg += $"\nx-offset:{distanceOffset} h-offset:{heightOffset}"; } //Debug.Log(msg); }