Fix editor drag feedback and surface overlays

This commit is contained in:
2026-05-12 00:06:12 +02:00
parent 99482c7011
commit 06d37aac10
2 changed files with 81 additions and 34 deletions

View File

@@ -136,8 +136,10 @@
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock x:Name="HoveredCellText" Text="Hovered Cell:" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
<TextBlock x:Name="SelectedCellText" Text="Selected Cell:" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
<TextBlock x:Name="HoveredCellText" Text="Hovered Cell:" FontSize="16" FontWeight="SemiBold"
Foreground="#F4F1E8" />
<TextBlock x:Name="SelectedCellText" Text="Selected Cell:" FontSize="16" FontWeight="SemiBold"
Foreground="#F4F1E8" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -148,7 +150,8 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Terrain" Foreground="#9EA7AE" FontSize="11" />
<TextBlock Grid.Column="0" Grid.Row="1" x:Name="TerrainText" Foreground="#F4F1E8" FontSize="16" Margin="0,0,10,0" />
<TextBlock Grid.Column="0" Grid.Row="1" x:Name="TerrainText" Foreground="#F4F1E8" FontSize="16"
Margin="0,0,10,0" />
<TextBlock Grid.Column="1" Grid.Row="0" Text="Prop" Foreground="#9EA7AE" FontSize="11" />
<TextBlock Grid.Column="1" Grid.Row="1" x:Name="PropText" Foreground="#F4F1E8" FontSize="16" />
</Grid>
@@ -167,13 +170,14 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Label}" Foreground="#9EA7AE" FontSize="11" TextWrapping="NoWrap" />
<TextBlock Text="{Binding Label}" Foreground="#9EA7AE" FontSize="11"
TextWrapping="NoWrap" />
<TextBlock Grid.Row="1" Text="{Binding Value}" Foreground="#F4F1E8" FontSize="16" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="Services:" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
<ItemsControl x:Name="ServicesGrid">
<ItemsControl.ItemsPanel>
@@ -188,13 +192,14 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Label}" Foreground="#9EA7AE" FontSize="11" TextWrapping="NoWrap" />
<TextBlock Text="{Binding Label}" Foreground="#9EA7AE" FontSize="11"
TextWrapping="NoWrap" />
<TextBlock Grid.Row="1" Text="{Binding Value}" Foreground="#F4F1E8" FontSize="16" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="Leaks:" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
<ItemsControl x:Name="LeaksGrid">
<ItemsControl.ItemTemplate>
@@ -204,8 +209,10 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Label}" Foreground="#F4F1E8" FontSize="16" Margin="0,0,10,0" />
<TextBlock Grid.Column="1" Text="{Binding Value}" Foreground="#F4F1E8" FontSize="16" />
<TextBlock Text="{Binding Label}" Foreground="#F4F1E8" FontSize="16"
Margin="0,0,10,0" />
<TextBlock Grid.Column="1" Text="{Binding Value}" Foreground="#F4F1E8"
FontSize="16" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
@@ -225,7 +232,8 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Label}" Foreground="#9EA7AE" FontSize="11" TextWrapping="NoWrap" />
<TextBlock Text="{Binding Label}" Foreground="#9EA7AE" FontSize="11"
TextWrapping="NoWrap" />
<TextBlock Grid.Row="1" Text="{Binding Value}" Foreground="#F4F1E8" FontSize="16" />
</Grid>
</DataTemplate>

View File

@@ -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;