Editor/InteriorLayoutBuilder/RoomLayoutTool.Input.cs
using System;
using System.Linq;
using Editor;
using Sandbox;
namespace ReusableRoomLayout;
public sealed partial class RoomLayoutTool
{
[Shortcut( "editor.undo", "CTRL+Z", typeof( SceneViewWidget ) )]
public static void UndoActiveLayoutTool()
{
if ( LiveTool?.CancelLayoutInteraction( restoreActiveEdit: true ) == true )
{
return;
}
global::Editor.EditorShortcuts.PassShortcut = true;
}
[Shortcut( "editor.redo", "CTRL+Y", typeof( SceneViewWidget ) )]
public static void RedoActiveLayoutTool()
{
global::Editor.EditorShortcuts.PassShortcut = true;
}
[Shortcut( "editor.cancel", "ESC", typeof( SceneViewWidget ) )]
public static void CancelActiveLayoutToolInteraction()
{
if ( LiveTool?.CancelLayoutInteraction( restoreActiveEdit: true ) == true )
{
return;
}
global::Editor.EditorShortcuts.PassShortcut = true;
}
[Shortcut( "editor.delete", "DEL", typeof( SceneViewWidget ) )]
public static void DeleteActiveLayoutToolSelection()
{
if ( LiveTool?.TryDeleteSelectedLayout() == true )
{
return;
}
global::Editor.EditorShortcuts.PassShortcut = true;
}
public void DeleteSelectedLayout()
{
TryDeleteSelectedLayout();
}
private bool TryDeleteSelectedLayout()
{
if ( drawingCorridor )
{
CancelCorridorDraft();
return true;
}
if ( selectedFloorCutoutId != 0 )
{
PushLayoutUndo();
document.FloorCutouts.RemoveAll( cutout => cutout.Id == selectedFloorCutoutId );
selectedFloorCutoutId = 0;
CommitLayoutChange( !HasLayoutContent() );
return true;
}
if ( selectedDoorId != 0 )
{
PushLayoutUndo();
document.Doors.RemoveAll( door => door.Id == selectedDoorId );
document.Corridors.RemoveAll( corridor => corridor.StartDoorId == selectedDoorId || corridor.EndDoorId == selectedDoorId );
selectedRoomId = 0;
selectedDoorId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange( !HasLayoutContent() );
return true;
}
if ( selectedWindowId != 0 )
{
PushLayoutUndo();
document.Windows.RemoveAll( window => window.Id == selectedWindowId );
selectedRoomId = 0;
selectedWindowId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange( !HasLayoutContent() );
return true;
}
if ( selectedCorridorId != 0 )
{
PushLayoutUndo();
document.Doors.RemoveAll( door => door.CorridorId == selectedCorridorId );
document.Windows.RemoveAll( window => window.CorridorId == selectedCorridorId );
document.Corridors.RemoveAll( corridor => corridor.Id == selectedCorridorId );
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange( !HasLayoutContent() );
return true;
}
if ( selectedRoomId != 0 )
{
PushLayoutUndo();
document.RemoveRoom( selectedRoomId );
selectedRoomId = 0;
selectedWindowId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange( !HasLayoutContent() );
return true;
}
return false;
}
private void HandleMouse()
{
if ( !TryGetCursorPoint( out var cursor, out var hitGeneratedLayout, out var hitGameObject ) )
{
return;
}
if ( Gizmo.Pressed.Any && !hitGeneratedLayout )
{
if ( mode is RoomLayoutToolMode.Rooms or RoomLayoutToolMode.FloorCutouts || resizingCorridor )
{
return;
}
}
if ( Gizmo.WasLeftMousePressed &&
(mode is RoomLayoutToolMode.Rooms or RoomLayoutToolMode.FloorCutouts or RoomLayoutToolMode.Select) &&
IsCursorOverExternalSceneObject() )
{
return;
}
var selectedLayoutAtCursor = false;
if ( Gizmo.WasLeftMousePressed && !(mode == RoomLayoutToolMode.Corridors && drawingCorridor) )
{
selectedLayoutAtCursor = mode == RoomLayoutToolMode.Select &&
hitGeneratedLayout &&
TrySelectGeneratedLayoutObject( hitGameObject );
if ( !selectedLayoutAtCursor )
{
selectedLayoutAtCursor = SelectLayoutAtCursor( cursor );
}
}
switch ( mode )
{
case RoomLayoutToolMode.Select:
HandleSelectMouse( cursor, selectedLayoutAtCursor );
break;
case RoomLayoutToolMode.Rooms:
HandleRoomMouse( cursor, selectedLayoutAtCursor );
break;
case RoomLayoutToolMode.Doors:
HandleDoorMouse( cursor );
break;
case RoomLayoutToolMode.Windows:
HandleWindowMouse( cursor );
break;
case RoomLayoutToolMode.Corridors:
HandleCorridorMouse( cursor );
break;
case RoomLayoutToolMode.FloorCutouts:
HandleFloorCutoutMouse( cursor, selectedLayoutAtCursor );
break;
}
}
private void HandleSelectMouse( Vector2 cursor, bool selectedLayoutAtCursor )
{
if ( movingDoor )
{
MoveSelectedDoorByDrag( cursor );
if ( Gizmo.WasLeftMouseReleased )
{
movingDoor = false;
CommitLayoutEdit();
}
return;
}
if ( movingWindow )
{
MoveSelectedWindowByDrag( cursor );
if ( Gizmo.WasLeftMouseReleased )
{
movingWindow = false;
CommitLayoutEdit();
}
return;
}
if ( movingRoom )
{
if ( document.FindRoom( selectedRoomId ) is { } room )
{
var delta = cursor - dragStart;
room.Bounds = SnapRect( moveStartBounds.MovedBy( delta ) );
}
if ( Gizmo.WasLeftMouseReleased )
{
movingRoom = false;
CommitLayoutEdit();
}
return;
}
if ( movingFloorCutout )
{
MoveSelectedFloorCutoutByDrag( cursor );
if ( Gizmo.WasLeftMouseReleased )
{
movingFloorCutout = false;
CommitLayoutEdit();
}
return;
}
if ( selectMovePending )
{
if ( Gizmo.WasLeftMouseReleased || !Gizmo.IsLeftMouseDown )
{
selectMovePending = false;
return;
}
if ( Vector2.DistanceBetween( cursor, dragStart ) < SelectDragThreshold )
{
return;
}
StartPendingSelectMove();
selectMovePending = false;
if ( movingDoor )
{
MoveSelectedDoorByDrag( cursor );
}
else if ( movingWindow )
{
MoveSelectedWindowByDrag( cursor );
}
else if ( movingRoom && document.FindRoom( selectedRoomId ) is { } room )
{
var delta = cursor - dragStart;
room.Bounds = SnapRect( moveStartBounds.MovedBy( delta ) );
}
else if ( movingFloorCutout )
{
MoveSelectedFloorCutoutByDrag( cursor );
}
return;
}
if ( !Gizmo.WasLeftMousePressed || !selectedLayoutAtCursor )
{
return;
}
if ( selectedDoorId != 0 && document.FindDoor( selectedDoorId ) is { } door )
{
BeginPendingSelectMove( RoomLayoutSelectionKind.Door, cursor, door.Offset );
return;
}
if ( selectedWindowId != 0 && document.FindWindow( selectedWindowId ) is { } window )
{
BeginPendingSelectMove( RoomLayoutSelectionKind.Window, cursor, window.Offset );
return;
}
if ( selectedRoomId != 0 && document.FindRoom( selectedRoomId ) is { } selectedRoom )
{
BeginPendingSelectMove( RoomLayoutSelectionKind.Room, cursor, 0.0f );
moveStartBounds = selectedRoom.Bounds;
return;
}
if ( selectedFloorCutoutId != 0 && SelectedFloorCutout() is { } selectedCutout )
{
BeginPendingSelectMove( RoomLayoutSelectionKind.FloorCutout, cursor, 0.0f );
moveStartBounds = selectedCutout.Bounds;
}
}
private void BeginPendingSelectMove( RoomLayoutSelectionKind kind, Vector2 cursor, float offset )
{
selectMovePending = true;
selectMovePendingKind = kind;
dragStart = cursor;
moveStartOffset = offset;
}
private void StartPendingSelectMove()
{
switch ( selectMovePendingKind )
{
case RoomLayoutSelectionKind.Room when document.FindRoom( selectedRoomId ) is { } room:
moveStartBounds = room.Bounds;
movingRoom = true;
BeginLayoutEdit();
break;
case RoomLayoutSelectionKind.Door when document.FindDoor( selectedDoorId ) is not null:
movingDoor = true;
BeginLayoutEdit();
break;
case RoomLayoutSelectionKind.Window when document.FindWindow( selectedWindowId ) is not null:
movingWindow = true;
BeginLayoutEdit();
break;
case RoomLayoutSelectionKind.FloorCutout when SelectedFloorCutout() is { } cutout:
moveStartBounds = cutout.Bounds;
movingFloorCutout = true;
BeginLayoutEdit();
break;
}
}
private void HandleRoomMouse( Vector2 cursor, bool selectedLayoutAtCursor )
{
if ( Gizmo.WasLeftMousePressed )
{
if ( TryFindRoom( cursor, out var room ) )
{
selectedRoomId = room.Id;
selectedDoorId = 0;
selectedWindowId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
movingRoom = true;
BeginLayoutEdit();
dragStart = cursor;
moveStartBounds = room.Bounds;
return;
}
if ( selectedLayoutAtCursor )
{
return;
}
selectedRoomId = 0;
selectedFloorCutoutId = 0;
drawingRoom = true;
dragStart = cursor;
dragCurrent = cursor;
}
if ( drawingRoom )
{
dragCurrent = cursor;
if ( Gizmo.WasLeftMouseReleased )
{
CommitDrawnRoom();
}
return;
}
if ( movingRoom )
{
if ( document.FindRoom( selectedRoomId ) is { } room )
{
var delta = cursor - dragStart;
room.Bounds = SnapRect( moveStartBounds.MovedBy( delta ) );
}
if ( Gizmo.WasLeftMouseReleased )
{
movingRoom = false;
CommitLayoutEdit();
}
}
}
private bool SelectLayoutAtCursor( Vector2 cursor )
{
if ( mode == RoomLayoutToolMode.Select )
{
if ( TryFindFloorCutout( cursor, out var selectCutout ) )
{
SelectFloorCutout( selectCutout );
return true;
}
if ( TryFindNearestDoor( cursor, out var selectDoor, OpeningHitDistance() ) )
{
SelectDoor( selectDoor );
return true;
}
if ( TryFindWindowUnderCursor( out var selectWindow ) ||
TryFindNearestWindow( cursor, out selectWindow, WindowHitDistance() ) )
{
SelectWindow( selectWindow );
return true;
}
if ( TryFindNearestCorridor( cursor, out var selectCorridor ) )
{
SelectCorridor( selectCorridor );
return true;
}
}
if ( mode == RoomLayoutToolMode.Doors && TryFindNearestDoor( cursor, out var door, OpeningHitDistance() ) )
{
SelectDoor( door );
return true;
}
if ( mode == RoomLayoutToolMode.Windows &&
(TryFindWindowUnderCursor( out var window ) ||
TryFindNearestWindow( cursor, out window, WindowHitDistance() )) )
{
SelectWindow( window );
return true;
}
if ( mode == RoomLayoutToolMode.Corridors && TryFindNearestCorridor( cursor, out var modeCorridor ) )
{
SelectCorridor( modeCorridor );
return true;
}
if ( mode == RoomLayoutToolMode.FloorCutouts && TryFindFloorCutout( cursor, out var cutout ) )
{
SelectFloorCutout( cutout );
return true;
}
if ( mode == RoomLayoutToolMode.FloorCutouts )
{
return false;
}
if ( TryFindRoom( cursor, out var room ) || TryFindRoomWall( cursor, out room ) )
{
selectedRoomId = room.Id;
selectedDoorId = 0;
selectedWindowId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
return true;
}
if ( TryFindNearestCorridor( cursor, out var corridor ) )
{
SelectCorridor( corridor );
return true;
}
selectedRoomId = 0;
selectedDoorId = 0;
selectedWindowId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
return false;
}
private void SelectDoor( RoomLayoutDoor door )
{
selectedRoomId = door.RoomId;
selectedDoorId = door.Id;
selectedWindowId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
SetActiveFloorFromSelection();
}
private void SelectWindow( RoomLayoutWindow window )
{
selectedRoomId = window.RoomId;
selectedDoorId = 0;
selectedWindowId = window.Id;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
SetActiveFloorFromSelection();
}
private void SelectCorridor( RoomLayoutCorridor corridor )
{
selectedRoomId = 0;
selectedDoorId = 0;
selectedWindowId = 0;
selectedCorridorId = corridor.Id;
selectedFloorCutoutId = 0;
SetActiveFloorFromSelection();
}
private float OpeningHitDistance()
{
return MathF.Max( 8.0f, document.Settings.GridSize * 0.25f );
}
private float WindowHitDistance()
{
return MathF.Max( 16.0f, document.Settings.GridSize * 0.45f );
}
private void HandleDoorMouse( Vector2 cursor )
{
if ( movingDoor )
{
MoveSelectedDoorTo( cursor );
if ( Gizmo.WasLeftMouseReleased )
{
movingDoor = false;
CommitLayoutEdit();
}
return;
}
if ( !Gizmo.WasLeftMousePressed )
{
return;
}
if ( TryFindNearestDoor( cursor, out var door, OpeningHitDistance() ) )
{
SelectDoor( door );
movingDoor = true;
BeginLayoutEdit();
MoveSelectedDoorTo( cursor );
return;
}
if ( TryFindNearestWall( cursor, out var room, out var side, out var offset ) )
{
PushLayoutUndo();
var created = AddDoor( room, side, offset );
selectedDoorId = created.Id;
selectedRoomId = room.Id;
selectedWindowId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange();
return;
}
if ( TryFindNearestCorridorWall( cursor, out var corridor, out var segmentIndex, out var corridorSide, out var corridorOffset, out var corridorLength ) )
{
PushLayoutUndo();
var created = AddCorridorDoor( corridor, segmentIndex, corridorSide, corridorOffset, corridorLength );
selectedDoorId = created.Id;
selectedRoomId = 0;
selectedWindowId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange();
}
}
private void HandleWindowMouse( Vector2 cursor )
{
if ( movingWindow )
{
MoveSelectedWindowTo( cursor );
if ( Gizmo.WasLeftMouseReleased )
{
movingWindow = false;
CommitLayoutEdit();
}
return;
}
if ( !Gizmo.WasLeftMousePressed )
{
return;
}
if ( TryFindWindowUnderCursor( out var window ) ||
TryFindNearestWindow( cursor, out window, WindowHitDistance() ) )
{
SelectWindow( window );
movingWindow = true;
BeginLayoutEdit();
MoveSelectedWindowTo( cursor );
return;
}
if ( TryFindNearestWall( cursor, out var room, out var side, out var offset ) )
{
PushLayoutUndo();
var created = AddWindow( room, side, offset );
selectedWindowId = created.Id;
selectedRoomId = room.Id;
selectedDoorId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange();
return;
}
if ( TryFindNearestCorridorWall( cursor, out var corridor, out var segmentIndex, out var corridorSide, out var corridorOffset, out var corridorLength ) )
{
PushLayoutUndo();
var created = AddCorridorWindow( corridor, segmentIndex, corridorSide, corridorOffset, corridorLength );
selectedWindowId = created.Id;
selectedRoomId = 0;
selectedDoorId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange();
}
}
private void CommitDrawnRoom()
{
var bounds = SnapRect( RoomLayoutRect.FromPoints( dragStart, dragCurrent ) );
drawingRoom = false;
if ( bounds.Width < document.Settings.GridSize || bounds.Height < document.Settings.GridSize )
{
return;
}
var room = new RoomLayoutRoom
{
Id = document.AllocateId(),
Floor = activeFloor,
Name = $"Room {document.Rooms.Count + 1}",
Bounds = bounds
};
PushLayoutUndo();
document.Rooms.Add( room );
selectedRoomId = room.Id;
selectedDoorId = 0;
selectedWindowId = 0;
selectedCorridorId = 0;
selectedFloorCutoutId = 0;
CommitLayoutChange();
}
private RoomLayoutDoor AddDoor( RoomLayoutRoom room, RoomLayoutWallSide side, float offset )
{
var wallLength = side is RoomLayoutWallSide.North or RoomLayoutWallSide.South
? room.Bounds.Width
: room.Bounds.Height;
var width = MathF.Min( document.Settings.DoorWidth, MathF.Max( 1.0f, wallLength ) );
var halfWidth = width * 0.5f;
var snappedOffset = Snap( offset );
var door = new RoomLayoutDoor
{
Id = document.AllocateId(),
RoomId = room.Id,
Side = side,
Offset = Math.Clamp( snappedOffset, halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) ),
Width = width,
Height = document.Settings.DoorHeight
};
document.Doors.Add( door );
return door;
}
private RoomLayoutDoor AddCorridorDoor( RoomLayoutCorridor corridor, int segmentIndex, int side, float offset, float wallLength )
{
var width = MathF.Min( document.Settings.DoorWidth, MathF.Max( 1.0f, wallLength ) );
var halfWidth = width * 0.5f;
var snappedOffset = SnapHalfGrid( offset );
var door = new RoomLayoutDoor
{
Id = document.AllocateId(),
CorridorId = corridor.Id,
CorridorSegmentIndex = segmentIndex,
CorridorSide = side,
Offset = Math.Clamp( snappedOffset, halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) ),
Width = width,
Height = document.Settings.DoorHeight
};
document.Doors.Add( door );
return door;
}
private RoomLayoutWindow AddWindow( RoomLayoutRoom room, RoomLayoutWallSide side, float offset )
{
var wallLength = side is RoomLayoutWallSide.North or RoomLayoutWallSide.South
? room.Bounds.Width
: room.Bounds.Height;
var width = MathF.Min( document.Settings.WindowWidth, MathF.Max( 1.0f, wallLength ) );
var halfWidth = width * 0.5f;
var snappedOffset = Snap( offset );
var window = new RoomLayoutWindow
{
Id = document.AllocateId(),
RoomId = room.Id,
Side = side,
Offset = Math.Clamp( snappedOffset, halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) ),
Width = width,
Height = document.Settings.WindowHeight,
SillHeight = document.Settings.WindowSillHeight
};
document.Windows.Add( window );
return window;
}
private RoomLayoutWindow AddCorridorWindow( RoomLayoutCorridor corridor, int segmentIndex, int side, float offset, float wallLength )
{
var width = MathF.Min( document.Settings.WindowWidth, MathF.Max( 1.0f, wallLength ) );
var halfWidth = width * 0.5f;
var snappedOffset = SnapHalfGrid( offset );
var window = new RoomLayoutWindow
{
Id = document.AllocateId(),
CorridorId = corridor.Id,
CorridorSegmentIndex = segmentIndex,
CorridorSide = side,
Offset = Math.Clamp( snappedOffset, halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) ),
Width = width,
Height = document.Settings.WindowHeight,
SillHeight = document.Settings.WindowSillHeight
};
document.Windows.Add( window );
return window;
}
private void MoveSelectedDoorTo( Vector2 cursor )
{
if ( document.FindDoor( selectedDoorId ) is not { } door )
{
movingDoor = false;
return;
}
if ( door.CorridorId != 0 )
{
MoveCorridorOpeningTo( door.CorridorId, door.CorridorSegmentIndex, door.Width, cursor, value => door.Offset = value );
return;
}
if ( document.FindRoom( door.RoomId ) is not { } room )
{
movingDoor = false;
return;
}
var wallLength = WallLength( room, door.Side );
var halfWidth = MathF.Max( 0.5f, DoorOpeningWidth( door ) * 0.5f );
var offset = OffsetAlongRoomWall( room, door.Side, cursor );
door.Offset = Math.Clamp( Snap( offset ), halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) );
}
private void MoveSelectedDoorByDrag( Vector2 cursor )
{
if ( document.FindDoor( selectedDoorId ) is not { } door )
{
movingDoor = false;
return;
}
if ( door.CorridorId != 0 )
{
MoveCorridorOpeningByDrag( door.CorridorId, door.CorridorSegmentIndex, door.Width, cursor, value => door.Offset = value );
return;
}
if ( document.FindRoom( door.RoomId ) is not { } room )
{
movingDoor = false;
return;
}
var wallLength = WallLength( room, door.Side );
var halfWidth = MathF.Max( 0.5f, DoorOpeningWidth( door ) * 0.5f );
var offset = moveStartOffset + DeltaAlongRoomWall( door.Side, cursor - dragStart );
door.Offset = Math.Clamp( Snap( offset ), halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) );
}
private void MoveSelectedWindowTo( Vector2 cursor )
{
if ( document.FindWindow( selectedWindowId ) is not { } window )
{
movingWindow = false;
return;
}
if ( window.CorridorId != 0 )
{
MoveCorridorOpeningTo( window.CorridorId, window.CorridorSegmentIndex, window.Width, cursor, value => window.Offset = value );
return;
}
if ( document.FindRoom( window.RoomId ) is not { } room )
{
movingWindow = false;
return;
}
var wallLength = WallLength( room, window.Side );
var halfWidth = MathF.Max( 0.5f, window.Width * 0.5f );
var offset = OffsetAlongRoomWall( room, window.Side, cursor );
window.Offset = Math.Clamp( Snap( offset ), halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) );
}
private void MoveSelectedWindowByDrag( Vector2 cursor )
{
if ( document.FindWindow( selectedWindowId ) is not { } window )
{
movingWindow = false;
return;
}
if ( window.CorridorId != 0 )
{
MoveCorridorOpeningByDrag( window.CorridorId, window.CorridorSegmentIndex, window.Width, cursor, value => window.Offset = value );
return;
}
if ( document.FindRoom( window.RoomId ) is not { } room )
{
movingWindow = false;
return;
}
var wallLength = WallLength( room, window.Side );
var halfWidth = MathF.Max( 0.5f, window.Width * 0.5f );
var offset = moveStartOffset + DeltaAlongRoomWall( window.Side, cursor - dragStart );
window.Offset = Math.Clamp( Snap( offset ), halfWidth, MathF.Max( halfWidth, wallLength - halfWidth ) );
}
private void MoveCorridorOpeningTo( int corridorId, int segmentIndex, float openingWidth, Vector2 cursor, Action<float> setOffset )
{
var corridor = document.Corridors.FirstOrDefault( candidate => candidate.Id == corridorId );
if ( corridor is null || !TryGetCorridorPath( corridor, out var points ) || segmentIndex < 0 || segmentIndex >= points.Count - 1 )
{
movingDoor = false;
movingWindow = false;
return;
}
var a = points[segmentIndex];
var b = points[segmentIndex + 1];
var delta = b - a;
var length = delta.Length;
if ( length < 1.0f )
{
return;
}
var t = (((cursor.x - a.x) * delta.x) + ((cursor.y - a.y) * delta.y)) / (length * length);
var halfWidth = openingWidth * 0.5f;
var maxOffset = MathF.Max( halfWidth, length - halfWidth );
var offset = Math.Clamp( t * length, halfWidth, maxOffset );
setOffset( Math.Clamp( SnapHalfGrid( offset ), halfWidth, maxOffset ) );
}
private void MoveCorridorOpeningByDrag( int corridorId, int segmentIndex, float openingWidth, Vector2 cursor, Action<float> setOffset )
{
var corridor = document.Corridors.FirstOrDefault( candidate => candidate.Id == corridorId );
if ( corridor is null || !TryGetCorridorPath( corridor, out var points ) || segmentIndex < 0 || segmentIndex >= points.Count - 1 )
{
movingDoor = false;
movingWindow = false;
return;
}
var a = points[segmentIndex];
var b = points[segmentIndex + 1];
var delta = b - a;
var length = delta.Length;
if ( length < 1.0f )
{
return;
}
var horizontal = MathF.Abs( delta.x ) >= MathF.Abs( delta.y );
var deltaAlong = horizontal ? cursor.x - dragStart.x : cursor.y - dragStart.y;
var halfWidth = openingWidth * 0.5f;
var maxOffset = MathF.Max( halfWidth, length - halfWidth );
var offset = Math.Clamp( moveStartOffset + deltaAlong, halfWidth, maxOffset );
setOffset( Math.Clamp( SnapHalfGrid( offset ), halfWidth, maxOffset ) );
}
private static float WallLength( RoomLayoutRoom room, RoomLayoutWallSide side )
{
return side is RoomLayoutWallSide.North or RoomLayoutWallSide.South
? room.Bounds.Width
: room.Bounds.Height;
}
private static float DeltaAlongRoomWall( RoomLayoutWallSide side, Vector2 delta )
{
return side is RoomLayoutWallSide.North or RoomLayoutWallSide.South
? delta.x
: delta.y;
}
private static float OffsetAlongRoomWall( RoomLayoutRoom room, RoomLayoutWallSide side, Vector2 cursor )
{
return side is RoomLayoutWallSide.North or RoomLayoutWallSide.South
? cursor.x - room.Bounds.X
: cursor.y - room.Bounds.Y;
}
private bool IsCursorOverExternalSceneObject()
{
var trace = Trace.UseRenderMeshes( true ).UsePhysicsWorld( false ).Run();
return trace.Hit && trace.GameObject.IsValid() && !IsGeneratedLayoutObject( trace.GameObject );
}
private static bool IsGeneratedLayoutObject( GameObject gameObject )
{
for ( var current = gameObject; current.IsValid(); current = current.Parent )
{
if ( current.Name == RoomLayoutGeometryBuilder.GeneratedRootName )
{
return true;
}
}
return false;
}
}