public void Adjust(Vector3 adjustDirection) { MergeStoredSelected(); // now we can safely look only the addSelected property and the selectedThings list // and ignore the storedSelected property and the storedSelectedThings list int adjustDirFaceI = Voxel.FaceIForDirection(adjustDirection); int oppositeAdjustDirFaceI = Voxel.OppositeFaceI(adjustDirFaceI); int adjustAxis = Voxel.FaceIAxis(adjustDirFaceI); bool negativeAdjustAxis = adjustDirFaceI % 2 == 0; // sort selectedThings in order along the adjustDirection vector selectedThings.Sort(delegate(Selectable a, Selectable b) { // positive means A is greater than B // so positive means B will be adjusted before A Vector3 aCenter = a.bounds.center; Vector3 bCenter = b.bounds.center; float diff = 0; switch (adjustAxis) { case 0: diff = bCenter.x - aCenter.x; break; case 1: diff = bCenter.y - aCenter.y; break; case 2: diff = bCenter.z - aCenter.z; break; } if (negativeAdjustAxis) { diff = -diff; } if (diff > 0) { return(1); } if (diff < 0) { return(-1); } if (a is VoxelFaceReference && b is VoxelFaceReference) { var aFace = (VoxelFaceReference)a; var bFace = (VoxelFaceReference)b; if (aFace.faceI == oppositeAdjustDirFaceI) { if (bFace.faceI != oppositeAdjustDirFaceI) { return(-1); // move one substance back before moving other forward } else if (bFace.faceI == oppositeAdjustDirFaceI) { return(1); } } } return(0); }); // HashSets prevent duplicate elements var voxelsToUpdate = new HashSet <Voxel>(); bool createdSubstance = false; bool temporarilyBlockPushingANewSubstance = false; for (int i = 0; i < selectedThings.Count; i++) { Selectable thing = selectedThings[i]; if (thing is ObjectMarker) { var obj = ((ObjectMarker)thing).objectEntity; Vector3Int objNewPos = obj.position + Vector3ToInt(adjustDirection); MoveObject(obj, objNewPos); Voxel objNewVoxel = VoxelAt(objNewPos, false); if (objNewVoxel != null && objNewVoxel.substance == null && !objNewVoxel.faces[oppositeAdjustDirFaceI].IsEmpty() && !objNewVoxel.faces[oppositeAdjustDirFaceI].addSelected) { // carve a hole for the object if it's being pushed into a wall objNewVoxel.faces[oppositeAdjustDirFaceI].addSelected = true; selectedThings.Insert(i + 1, new VoxelFaceReference(objNewVoxel, oppositeAdjustDirFaceI)); } continue; } else if (!(thing is VoxelFaceReference)) { continue; } VoxelFaceReference faceRef = (VoxelFaceReference)thing; Voxel oldVoxel = faceRef.voxel; Vector3 oldPos = oldVoxel.transform.position; Vector3 newPos = oldPos + adjustDirection; Voxel newVoxel = VoxelAt(newPos, true); int faceI = faceRef.faceI; int oppositeFaceI = Voxel.OppositeFaceI(faceI); bool pushing = adjustDirFaceI == oppositeFaceI; bool pulling = adjustDirFaceI == faceI; if (pulling && (!newVoxel.faces[oppositeFaceI].IsEmpty()) && !newVoxel.faces[oppositeFaceI].addSelected) { // usually this means there's another substance. push it away before this face if (substanceToCreate != null && newVoxel.substance == substanceToCreate) { // substance has already been created there! // substanceToCreate has never existed in the map before Adjust() was called // so it must have been created earlier in the loop // remove selection oldVoxel.faces[faceI].addSelected = false; selectedThings[i] = new VoxelFaceReference(null, -1); voxelsToUpdate.Add(oldVoxel); } else { newVoxel.faces[oppositeFaceI].addSelected = true; selectedThings.Insert(i, new VoxelFaceReference(newVoxel, oppositeFaceI)); i -= 1; // need to move the other substance out of the way first temporarilyBlockPushingANewSubstance = true; } continue; } VoxelFace movingFace = oldVoxel.faces[faceI]; movingFace.addSelected = false; Substance movingSubstance = oldVoxel.substance; bool blocked = false; // is movement blocked? Voxel newSubstanceBlock = null; if (pushing) { for (int sideNum = 0; sideNum < 4; sideNum++) { int sideFaceI = Voxel.SideFaceI(faceI, sideNum); if (oldVoxel.faces[sideFaceI].IsEmpty()) { // add side Vector3 sideFaceDir = Voxel.DirectionForFaceI(sideFaceI); Voxel sideVoxel = VoxelAt(oldPos + sideFaceDir, true); int oppositeSideFaceI = Voxel.OppositeFaceI(sideFaceI); // if possible, the new side should have the properties of the adjacent side Voxel adjacentSideVoxel = VoxelAt(oldPos - adjustDirection + sideFaceDir, false); if (adjacentSideVoxel != null && !adjacentSideVoxel.faces[oppositeSideFaceI].IsEmpty() && movingSubstance == adjacentSideVoxel.substance) { sideVoxel.faces[oppositeSideFaceI] = adjacentSideVoxel.faces[oppositeSideFaceI]; sideVoxel.faces[oppositeSideFaceI].addSelected = false; } else { sideVoxel.faces[oppositeSideFaceI] = movingFace; } voxelsToUpdate.Add(sideVoxel); } } if (!oldVoxel.faces[oppositeFaceI].IsEmpty()) { blocked = true; } oldVoxel.Clear(); if (substanceToCreate != null && !temporarilyBlockPushingANewSubstance) { newSubstanceBlock = CreateSubstanceBlock(oldPos, substanceToCreate, movingFace); } } else if (pulling && substanceToCreate != null) { newSubstanceBlock = CreateSubstanceBlock(newPos, substanceToCreate, movingFace); oldVoxel.faces[faceI].addSelected = false; blocked = true; } else if (pulling) { if (movingSubstance == null && newVoxel != null && newVoxel.objectEntity != null) { // blocked by object oldVoxel.faces[faceI].addSelected = false; selectedThings[i] = new VoxelFaceReference(null, -1); voxelsToUpdate.Add(oldVoxel); continue; } for (int sideNum = 0; sideNum < 4; sideNum++) { int sideFaceI = Voxel.SideFaceI(faceI, sideNum); int oppositeSideFaceI = Voxel.OppositeFaceI(sideFaceI); Voxel sideVoxel = VoxelAt(newPos + Voxel.DirectionForFaceI(sideFaceI), false); if (sideVoxel == null || sideVoxel.faces[oppositeSideFaceI].IsEmpty() || movingSubstance != sideVoxel.substance) { // add side // if possible, the new side should have the properties of the adjacent side if (!oldVoxel.faces[sideFaceI].IsEmpty()) { newVoxel.faces[sideFaceI] = oldVoxel.faces[sideFaceI]; newVoxel.faces[sideFaceI].addSelected = false; } else { newVoxel.faces[sideFaceI] = movingFace; } } else { // delete side sideVoxel.faces[oppositeSideFaceI].Clear(); voxelsToUpdate.Add(sideVoxel); } } Voxel blockingVoxel = VoxelAt(newPos + adjustDirection, false); if (blockingVoxel != null && !blockingVoxel.faces[oppositeFaceI].IsEmpty()) { if (movingSubstance == blockingVoxel.substance) { blocked = true; blockingVoxel.faces[oppositeFaceI].Clear(); voxelsToUpdate.Add(blockingVoxel); } } oldVoxel.faces[faceI].Clear(); } else // sliding { oldVoxel.faces[faceI].addSelected = false; if (newVoxel.faces[faceI].IsEmpty() || newVoxel.substance != movingSubstance) { blocked = true; } } if (!blocked) { // move the face newVoxel.faces[faceI] = movingFace; newVoxel.faces[faceI].addSelected = true; newVoxel.substance = movingSubstance; selectedThings[i] = new VoxelFaceReference(newVoxel, faceI); } else { // clear the selection; will be deleted later selectedThings[i] = new VoxelFaceReference(null, -1); if (pulling && substanceToCreate == null) { newVoxel.substance = movingSubstance; } } if (newSubstanceBlock != null) { createdSubstance = true; if (!newSubstanceBlock.faces[adjustDirFaceI].IsEmpty()) { newSubstanceBlock.faces[adjustDirFaceI].addSelected = true; selectedThings.Insert(0, new VoxelFaceReference(newSubstanceBlock, adjustDirFaceI)); i += 1; } } voxelsToUpdate.Add(newVoxel); voxelsToUpdate.Add(oldVoxel); temporarilyBlockPushingANewSubstance = false; } // end for each selected face foreach (Voxel voxel in voxelsToUpdate) { VoxelModified(voxel); } for (int i = selectedThings.Count - 1; i >= 0; i--) { Selectable thing = selectedThings[i]; if ((thing is VoxelFaceReference) && ((VoxelFaceReference)thing).voxel == null) { selectedThings.RemoveAt(i); } } selectionChanged = true; if (substanceToCreate != null && createdSubstance) { substanceToCreate = null; } AutoSetMoveAxesEnabled(); } // end Adjust()