diff --git a/src/ReactorMaintenance.Win2D/MainWindow.xaml b/src/ReactorMaintenance.Win2D/MainWindow.xaml index 20c4533..ca2a8a4 100644 --- a/src/ReactorMaintenance.Win2D/MainWindow.xaml +++ b/src/ReactorMaintenance.Win2D/MainWindow.xaml @@ -136,8 +136,10 @@ - - + + @@ -148,7 +150,8 @@ - + @@ -167,13 +170,14 @@ - + - + @@ -188,13 +192,14 @@ - + - + @@ -204,8 +209,10 @@ - - + + @@ -225,7 +232,8 @@ - + diff --git a/src/ReactorMaintenance.Win2D/MainWindow.xaml.cs b/src/ReactorMaintenance.Win2D/MainWindow.xaml.cs index 0295d4e..86704d0 100644 --- a/src/ReactorMaintenance.Win2D/MainWindow.xaml.cs +++ b/src/ReactorMaintenance.Win2D/MainWindow.xaml.cs @@ -266,7 +266,7 @@ public sealed partial class MainWindow m_EditorFeedback = string.Empty; m_LastPaintedCell = null; if (!m_IsPanning && m_SelectedTool.Tool == EEditorTool.Cursor && TryGetGridPosition(point.Position, out var position)) - StartCursorDrag(position); + m_CursorDragStartCell = position; else if (!m_IsPanning && IsDragPaintTool(m_SelectedTool.Tool) && TryGetGridPosition(point.Position, out position)) { m_LastPaintedCell = position; @@ -306,7 +306,7 @@ public sealed partial class MainWindow } else if (m_CursorDragStartCell is not null && m_DragExceededClickThreshold && TryGetGridPosition(point.Position, out var destination)) { - m_DragPreviewDestination = destination; + UpdateCursorDragPreview(destination); LevelCanvas.Invalidate(); } @@ -322,7 +322,7 @@ public sealed partial class MainWindow { LevelCanvas.Invalidate(); } - else if (m_CursorDragStartCell is { } source && m_DragExceededClickThreshold && TryGetGridPosition(point.Position, out var destination)) + else if (m_CursorDragStartCell is { } source && m_DragExceededClickThreshold && !m_CursorDragStartRejected && TryGetGridPosition(point.Position, out var destination)) { var result = LevelEditor.TryMoveOccupant(m_Level, source, destination); m_Level = result.Level; @@ -343,6 +343,7 @@ public sealed partial class MainWindow } } + var clearRejectedDragFeedback = m_CursorDragStartRejected; m_LeftPointerDown = false; m_IsPanning = false; m_CursorDragStartCell = null; @@ -350,6 +351,13 @@ public sealed partial class MainWindow m_DragPreviewDestination = null; m_LastPaintedCell = null; m_DragExceededClickThreshold = false; + if (clearRejectedDragFeedback) + { + ClearDragFeedback(); + RefreshInspector(); + LevelCanvas.Invalidate(); + } + LevelCanvas.ReleasePointerCapture(e.Pointer); e.Handled = true; } @@ -363,6 +371,9 @@ public sealed partial class MainWindow m_DragPreviewDestination = null; m_LastPaintedCell = null; m_DragExceededClickThreshold = false; + ClearDragFeedback(); + RefreshInspector(); + LevelCanvas.Invalidate(); } private void LevelCanvas_PointerWheelChanged(object sender, PointerRoutedEventArgs e) @@ -415,22 +426,34 @@ public sealed partial class MainWindow return tool is EEditorTool.Floor or EEditorTool.Wall or EEditorTool.Underground; } - private void StartCursorDrag(GridPosition position) + private void UpdateCursorDragPreview(GridPosition destination) { - m_SelectedCell = position; - if (HasMovableAt(position)) + if (m_CursorDragStartCell is not { } source) + return; + + if (m_CursorDragStartRejected) + return; + + if (!HasMovableAt(source)) { - m_CursorDragStartCell = position; + m_CursorDragStartRejected = true; + m_DragPreviewDestination = null; + m_InvalidDragCell = source; + m_EditorFeedback = "No movable robot, prop, source, or leak starts here."; RefreshInspector(); - LevelCanvas.Invalidate(); return; } - m_CursorDragStartRejected = true; - m_InvalidDragCell = position; - m_EditorFeedback = "No movable robot, prop, source, or leak starts here."; + m_DragPreviewDestination = destination; + m_InvalidDragCell = null; + m_EditorFeedback = string.Empty; RefreshInspector(); - LevelCanvas.Invalidate(); + } + + private void ClearDragFeedback() + { + m_InvalidDragCell = null; + m_EditorFeedback = string.Empty; } private void ClearAt(Point point) @@ -613,23 +636,39 @@ public sealed partial class MainWindow { var surface = m_Level.GetSurface(position); var rect = layout.CellRect(position); - FillHazard(drawing, rect, surface.Fuel, c_FuelColor, 0.08, opacity); - FillHazard(drawing, rect, surface.Coolant, c_CoolantColor, 0.18, opacity); - FillHazard(drawing, rect, surface.Electricity, c_ElectricityColor, 0.28, opacity); - if (surface.Heat > 0) - DrawImage(drawing, m_HeatSprite, Inset(rect, 0.18), Math.Clamp(surface.Heat / Balancing.Current.MaxValue, 0.25f, 0.9f) * opacity); + FillHazard(drawing, rect, surface.Fuel, c_FuelColor, 0.08, opacity, Balancing.Current.FuelCaution, Balancing.Current.FuelCritical); + FillHazard(drawing, rect, surface.Coolant, c_CoolantColor, 0.18, opacity, Balancing.Current.CoolantCaution, Balancing.Current.CoolantCritical); + FillHazard(drawing, rect, surface.Electricity, c_ElectricityColor, 0.28, opacity, Balancing.Current.ElectricityCaution, Balancing.Current.ElectricityCritical); + + var heatOpacity = SurfaceOverlayOpacity(surface.Heat, Balancing.Current.HeatCaution, Balancing.Current.HeatCritical); + if (heatOpacity > 0) + DrawImage(drawing, m_HeatSprite, Inset(rect, 0.18), heatOpacity * opacity); } } - private static void FillHazard(CanvasDrawingSession drawing, Rect rect, float amount, Color color, double inset, float opacity) + private static void FillHazard(CanvasDrawingSession drawing, Rect rect, float amount, Color color, double inset, float opacity, float caution, float critical) { - if (amount <= 0) + var overlayOpacity = SurfaceOverlayOpacity(amount, caution, critical); + if (overlayOpacity <= 0) return; - var alpha = (byte)Math.Clamp((40 + (amount / Balancing.Current.MaxValue * 130)) * opacity, 0, 170); + var alpha = (byte)Math.Clamp(170 * overlayOpacity * opacity, 0, 170); drawing.FillRectangle(Inset(rect, inset), ColorHelper.FromArgb(alpha, color.R, color.G, color.B)); } + private static float SurfaceOverlayOpacity(float amount, float caution, float critical) + { + if (amount < caution) + return 0; + + if (amount >= critical) + return 0.9f; + + var cautionRange = Math.Max(0.001f, critical - caution); + var t = (amount - caution) / cautionRange; + return 0.3f + (t * 0.35f); + } + private void DrawDoors(CanvasDrawingSession drawing, CanvasLayout layout, float opacity) { foreach (var position in AllPositions()) @@ -805,7 +844,7 @@ public sealed partial class MainWindow new InspectorItemViewModel("Fuel", $"{m_Level.Robot.FuelNeutralizers}"), new InspectorItemViewModel("Coolant", $"{m_Level.Robot.CoolantNeutralizers}"), new InspectorItemViewModel("Electricity", $"{m_Level.Robot.ElectricityNeutralizers}"), - new InspectorItemViewModel("Heat", $"{m_Level.Robot.HeatShields} ({m_Level.Robot.HeatImmunitySteps.ToString(CultureInfo.InvariantCulture)} steps)"), + new InspectorItemViewModel("Heat", $"{m_Level.Robot.HeatShields} ({m_Level.Robot.HeatImmunitySteps.ToString(CultureInfo.InvariantCulture)} steps)") }; RequiredGrid.ItemsSource = new[] { new InspectorItemViewModel("Fuel", m_Level.RequiredFuelConsumers.ToString(CultureInfo.InvariantCulture)), @@ -822,13 +861,13 @@ public sealed partial class MainWindow ServicesGrid.ItemsSource = new[] { new InspectorItemViewModel("Fuel", prop.FuelServiceState.ToString()), new InspectorItemViewModel("Coolant", prop.CoolantServiceState.ToString()), - new InspectorItemViewModel("Electricity", prop.ElectricityServiceState.ToString()), + new InspectorItemViewModel("Electricity", prop.ElectricityServiceState.ToString()) }; var surface = m_Level.GetSurface(position); ConsumersGrid.ItemsSource = new[] { new InspectorItemViewModel("Fuel", surface.FuelBlockTurns.ToString()), new InspectorItemViewModel("Coolant", surface.CoolantBlockTurns.ToString()), - new InspectorItemViewModel("Electricity", surface.ElectricityBlockTurns.ToString()), + new InspectorItemViewModel("Electricity", surface.ElectricityBlockTurns.ToString()) }; LeaksGrid.ItemsSource = m_Level.Leaks.Select(leak => new InspectorItemViewModel(leak.Carrier.ToString(), $"{leak.UndergroundPosition.X}, {leak.UndergroundPosition.Y}")); SurfaceGrid.ItemsSource = SurfaceInspectionItems(position); @@ -1267,7 +1306,7 @@ public sealed partial class MainWindow { m_Level = m_Level with { Forecasts = m_Simulation.Forecast(m_Level) }; } - + public GridPosition? HoverCell { get; private set; } private const double c_MinZoom = 0.5;