From c29a4ae708fc7917705dc78bbb465ab89f0c5351 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 5 Nov 2026 16:56:44 +0100 Subject: [PATCH] =?UTF-8?q?3D:=20=D0=BF=D1=80=D0=B8=D0=B7=D1=80=D0=B0?= =?UTF-8?q?=D0=BA=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20=D0=B7=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8F=20--=20=D0=BD=D0=B0=20=D0=B1=D0=BB?= =?UTF-8?q?=D0=B8=D0=B6=D0=B0=D0=B9=D1=88=D0=B5=D0=BC=20=D1=81=D0=B2=D0=BE?= =?UTF-8?q?=D0=B1=D0=BE=D0=B4=D0=BD=D0=BE=D0=BC=20=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B5,=20=D0=B0=20=D0=BD=D0=B5=20=D0=BD=D0=B0=20=D0=A0=D0=B0?= =?UTF-8?q?=D1=82=D1=83=D1=88=D0=B5=20=D0=B2=20=D1=86=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=B5=20=D0=BA=D0=B0=D0=BC=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- Assets/Scripts/City3D/City3DController.cs | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) 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;