diff --git a/Assets/Scripts/City3D/City3DController.cs b/Assets/Scripts/City3D/City3DController.cs index b4e480a..36ca5d2 100644 --- a/Assets/Scripts/City3D/City3DController.cs +++ b/Assets/Scripts/City3D/City3DController.cs @@ -540,11 +540,38 @@ public class City3DController : MonoBehaviour, City3DSave.ILive Vector3 c = cameraRig.target; ghostX = Mathf.Clamp(Mathf.RoundToInt(c.x - t.W / 2f), 0, City3DLayout.Size - t.W); ghostY = Mathf.Clamp(Mathf.RoundToInt(c.z - t.H / 2f), 0, City3DLayout.Size - t.H); + // Не на Ратуше и не в чужом здании: ближайшее к центру экрана место, где здание можно поставить + if (FindFreeSpot(t, ghostX, ghostY, out int fx, out int fy, out int frot)) + { + ghostX = fx; ghostY = fy; ghostRot = frot; + // камера плавно подъезжает к месту, не меняя приближения + float size = cameraRig.distance / 1.15f; + cameraRig.FocusOn(new Rect(fx + t.W / 2f - size / 2f, fy + t.H / 2f - size / 2f, size, size)); + } EnsureFootprint(); RefreshGround(); UpdateGhost(); } + // Ближайшее свободное место (по кольцам от x0, y0), сначала без поворота, потом с поворотом на 90 + bool FindFreeSpot(BuildingType t, int x0, int y0, out int x, out int y, out int rot) + { + for (int r = 0; r < City3DLayout.Size; r++) + for (int dy = -r; dy <= r; dy++) + for (int dx = -r; dx <= r; dx++) + { + if (Mathf.Max(Mathf.Abs(dx), Mathf.Abs(dy)) != r) continue; // только кольцо радиуса r + foreach (int tryRot in new[] { 0, 90 }) + if (map.Validate(x0 + dx, y0 + dy, t.W, t.H, tryRot, townHallLevel, null, out _) == PlaceError.None) + { + x = x0 + dx; y = y0 + dy; rot = tryRot; + return true; + } + } + x = x0; y = y0; rot = 0; + return false; // места нет -- призрак там, где камера, красный с причиной + } + void StartMoving(string id) { if (!map.TryGetBuilding(id, out var r)) return;