public bool SetPawnPosition(Pawn pawn, Point newPosition) { if (!_pawns.Contains(pawn)) { throw new ArgumentException("Pawn " + pawn.Name + " has not been added to this Board", "pawn"); } bool moved = false; List<HashSet<Pawn>> toRemove = new List<HashSet<Pawn>>(); List<HashSet<Pawn>> toAdd = new List<HashSet<Pawn>>(); HashSet<Pawn> enterCollisions = new HashSet<Pawn>(); HashSet<Pawn> exitCollisions = new HashSet<Pawn>(); Point oldPoint; Point newPoint; bool walled = false; // Gather all pawns that were in the original and new position's footprint foreach (Point offset in pawn.Footprint) { HashSet<Pawn> oldBucket; HashSet<Pawn> newBucket; oldPoint = pawn.Position + offset; newPoint = newPosition + offset; // Check if there's a wall at the new point. If so, early out. if (pawn.IsSolid) { if (InBounds(newPoint) && GetTile(newPoint) == WALL_TILE) { return false; } } // Add all pawns in the current bucket to the exit collision set oldBucket = GetBucket(oldPoint, false); if (oldBucket != null) { exitCollisions.UnionWith(oldBucket); toRemove.Add(oldBucket); } // Add all pawns in the new position to the enter set newBucket = GetBucket(newPoint, true); enterCollisions.UnionWith(newBucket); toAdd.Add(newBucket); } // If the pawn is solid, and any of the entering collision pawns are solid, don't allow the move bool willMove = true; if (pawn.IsSolid) { if (walled) { willMove = false; } else { foreach (Pawn other in enterCollisions) { if (other != pawn && other.IsSolid) { willMove = false; break; } } } } // Move the pawn between buckets if (willMove) { for (int i = 0; i < toRemove.Count; i++) { toRemove[i].Remove(pawn); } for (int i = 0; i < toAdd.Count; i++) { toAdd[i].Add(pawn); } pawn.SetPositionInternal(newPosition); moved = true; } if (pawn.IsCollidable) { // The stay set consists of pawns that are in both the exit and enter sets HashSet<Pawn> stayCollisions = new HashSet<Pawn>(exitCollisions); stayCollisions.IntersectWith(enterCollisions); // Remove the stay collisions from both the exit and enter sets enterCollisions.ExceptWith(stayCollisions); exitCollisions.ExceptWith(stayCollisions); // Call the collision methods foreach (Pawn other in enterCollisions) { if (pawn != other && other.IsCollidable) { pawn.OnCollisionEnter(other); other.OnCollisionEnter(pawn); } } foreach (Pawn other in stayCollisions) { if (pawn != other && other.IsCollidable) { pawn.OnCollisionStay(other); other.OnCollisionStay(pawn); } } foreach (Pawn other in exitCollisions) { if (pawn != other && other.IsCollidable) { pawn.OnCollisionExit(other); other.OnCollisionExit(pawn); } } } return moved; }