Rework Win2D editor for design model
This commit is contained in:
36
README.md
36
README.md
@@ -1,18 +1,20 @@
|
|||||||
# Reactor Maintenance
|
# Reactor Maintenance
|
||||||
|
|
||||||
C# WinUI 3 + Win2D level editor for the deterministic grid simulation described in `design.md`.
|
C# WinUI 3 + Win2D level editor for the deterministic grid simulation described in `docs/design.md`.
|
||||||
|
|
||||||
## Projects
|
## Projects
|
||||||
|
|
||||||
- `src/ReactorMaintenance.Simulation`: UI-independent level model, editor operations, forecasts, simulation turns, versioned JSON serialization, and swappable difficulty balancing profiles.
|
- `src/ReactorMaintenance.Simulation`: UI-independent level model, editor operations, validation, forecasts, simulation turns, versioned JSON serialization, and deterministic balancing defaults.
|
||||||
- `src/ReactorMaintenance.Win2D`: Win2D editor app for painting floor/wall terrain plus cell props, loading/saving levels, advancing simulation turns, and activating the reactor.
|
- `src/ReactorMaintenance.Win2D`: Win2D editor app for authoring terrain, underground fuel/coolant/electricity networks, props, leaks, doors, surface hazards, robot start, loading/saving levels, ending turns, interacting with props, and activating a ready reactor.
|
||||||
- `tests/ReactorMaintenance.Simulation.Tests`: unit tests for deterministic simulation behavior.
|
- `tests/ReactorMaintenance.Simulation.Tests`: unit tests for deterministic simulation behavior.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
dotnet test tests\ReactorMaintenance.Simulation.Tests\ReactorMaintenance.Simulation.Tests.csproj
|
dotnet test tests\ReactorMaintenance.Simulation.Tests\ReactorMaintenance.Simulation.Tests.csproj
|
||||||
dotnet build src\ReactorMaintenance.Win2D\ReactorMaintenance.Win2D.csproj -p:Platform=x64
|
dotnet build src\ReactorMaintenance.Win2D\ReactorMaintenance.Win2D.csproj -p:Platform=x64 -p:EnableWindowsTargeting=true
|
||||||
dotnet run --project src\ReactorMaintenance.Win2D\ReactorMaintenance.Win2D.csproj -p:Platform=x64
|
dotnet run --project src\ReactorMaintenance.Win2D\ReactorMaintenance.Win2D.csproj -p:Platform=x64
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The WinUI/XAML compiler is Windows-specific. On Linux, the simulation tests run normally, but the Win2D app build must be verified in a Windows-capable environment.
|
||||||
|
|
||||||
|
|||||||
17
TASKS.md
17
TASKS.md
@@ -6,7 +6,9 @@
|
|||||||
- Scope approved: implement `docs/design.md` end-to-end with deterministic defaults and no backward compatibility.
|
- Scope approved: implement `docs/design.md` end-to-end with deterministic defaults and no backward compatibility.
|
||||||
- Simulation core has been replaced with the first design-native model and deterministic engine slice.
|
- Simulation core has been replaced with the first design-native model and deterministic engine slice.
|
||||||
- Simulation and test projects now target `net10.0` because this Linux environment only has the .NET 10 runtime.
|
- Simulation and test projects now target `net10.0` because this Linux environment only has the .NET 10 runtime.
|
||||||
- Win2D editor still references the removed legacy model and is the next major implementation area.
|
- Win2D editor has been rewritten against the new design model.
|
||||||
|
- Win2D project now targets `net10.0-windows10.0.19041.0` to match the simulation project.
|
||||||
|
- Linux can restore and compile the referenced simulation project, but full WinUI/XAML compilation still requires a Windows-capable XAML compiler environment.
|
||||||
|
|
||||||
## Completed Work
|
## Completed Work
|
||||||
|
|
||||||
@@ -24,16 +26,23 @@
|
|||||||
- Attempted `dotnet jb cleanupcode --build=False ...`; unavailable in this environment because `dotnet-jb` is not installed.
|
- Attempted `dotnet jb cleanupcode --build=False ...`; unavailable in this environment because `dotnet-jb` is not installed.
|
||||||
- Reviewed the first slice and fixed an action-resolution maintainability issue before commit.
|
- Reviewed the first slice and fixed an action-resolution maintainability issue before commit.
|
||||||
- Verified `git diff --check` reports no whitespace errors.
|
- Verified `git diff --check` reports no whitespace errors.
|
||||||
|
- Ran `dotnet jb cleanupcode --build=False ...` successfully after ReSharper install and normalized line endings back to LF.
|
||||||
|
- Reworked the Win2D editor for the new model: full tool list, layer-aware painting, terrain, underground carriers, surface hazards, props, doors, leaks, robot, forecasts, save validation, starter level, and simple play actions.
|
||||||
|
- Removed old editor dependencies on legacy props, pressure pipes, smoke, fire, and global power/cooling/core-stability fields.
|
||||||
|
- Verified `dotnet test tests/ReactorMaintenance.Simulation.Tests/ReactorMaintenance.Simulation.Tests.csproj` passes after the editor rewrite: 11 passed.
|
||||||
|
- Attempted Win2D build on Linux with `dotnet build src/ReactorMaintenance.Win2D/ReactorMaintenance.Win2D.csproj -p:EnableWindowsTargeting=true -p:Platform=x64`; it fails at Windows `XamlCompiler.exe` with exec format error.
|
||||||
|
- Attempted managed XAML compiler path with `-p:UseXamlCompilerExecutable=false`; it fails loading the WinUI XAML compiler task dependency under this Linux/.NET 10 setup.
|
||||||
|
- Updated `README.md` for the new design-model editor, .NET 10 target, and Linux/Windows build expectations.
|
||||||
|
|
||||||
## Current Work
|
## Current Work
|
||||||
|
|
||||||
- Commit the first simulation-core rewrite slice.
|
- Commit the Win2D editor rewrite slice.
|
||||||
|
|
||||||
## Future Work
|
## Future Work
|
||||||
|
|
||||||
1. Expand simulation fidelity where the first slice is intentionally simplified: junction branch inference, ambiguity validation, complete pair table coverage, richer rule predicates/effects, and stronger forecast proof cases.
|
1. Expand simulation fidelity where the first slice is intentionally simplified: junction branch inference, ambiguity validation, complete pair table coverage, richer rule predicates/effects, and stronger forecast proof cases.
|
||||||
2. Update the Win2D editor for all authored layers and new runtime inspection.
|
2. Add advanced editor workflows for explicit reactor binding selection, explicit door edge selection, electricity wall leak face selection, and rule event authoring.
|
||||||
3. Add editor workflows for reactor bindings, door edge selection, electricity wall leak faces, rule events, and layer-specific painting.
|
3. Verify and polish the Win2D app on Windows where the XAML compiler can run.
|
||||||
4. Update README and any affected docs to reflect the new schema, .NET target, editor controls, and deterministic defaults.
|
4. Update README and any affected docs to reflect the new schema, .NET target, editor controls, and deterministic defaults.
|
||||||
5. Build the Win2D project on a Windows-capable environment after the editor rewrite.
|
5. Build the Win2D project on a Windows-capable environment after the editor rewrite.
|
||||||
6. Add broader tests for junction ratios, ambiguous junctions, all rule event families, serialization edge cases, and editor operations.
|
6. Add broader tests for junction ratios, ambiguous junctions, all rule event families, serialization edge cases, and editor operations.
|
||||||
|
|||||||
@@ -69,4 +69,4 @@ public abstract class Balancing
|
|||||||
public abstract int RemedyBlockTurns { get; }
|
public abstract int RemedyBlockTurns { get; }
|
||||||
public abstract int HeatShieldSteps { get; }
|
public abstract int HeatShieldSteps { get; }
|
||||||
public abstract int InventoryCapacityPerRemedy { get; }
|
public abstract int InventoryCapacityPerRemedy { get; }
|
||||||
}
|
}
|
||||||
@@ -52,4 +52,4 @@ public class NormalBalancing : Balancing
|
|||||||
public override int RemedyBlockTurns => 2;
|
public override int RemedyBlockTurns => 2;
|
||||||
public override int HeatShieldSteps => 3;
|
public override int HeatShieldSteps => 3;
|
||||||
public override int InventoryCapacityPerRemedy => 3;
|
public override int InventoryCapacityPerRemedy => 3;
|
||||||
}
|
}
|
||||||
@@ -162,4 +162,4 @@ public static class LevelEditor
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,4 +39,4 @@ public static class LevelSerializer
|
|||||||
public int Version { get; init; }
|
public int Version { get; init; }
|
||||||
public LevelState? Level { get; init; }
|
public LevelState? Level { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,4 +200,4 @@ public sealed class LevelValidator
|
|||||||
{
|
{
|
||||||
return level.InBounds(position) && level.GetProp(position).Type == propType;
|
return level.InBounds(position) && level.GetProp(position).Type == propType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,7 +331,6 @@ public sealed record RuleEventState
|
|||||||
}
|
}
|
||||||
|
|
||||||
public sealed record Forecast(EForecastKind Kind, GridPosition? Position, int Turns, string Message);
|
public sealed record Forecast(EForecastKind Kind, GridPosition? Position, int Turns, string Message);
|
||||||
|
|
||||||
public sealed record ValidationIssue(string Message, GridPosition? Position = null);
|
public sealed record ValidationIssue(string Message, GridPosition? Position = null);
|
||||||
|
|
||||||
public sealed record ValidationReport
|
public sealed record ValidationReport
|
||||||
@@ -532,4 +531,4 @@ public sealed record LevelState
|
|||||||
public RobotState Robot { get; init; } = new();
|
public RobotState Robot { get; init; } = new();
|
||||||
public GlobalState Global { get; init; } = new();
|
public GlobalState Global { get; init; } = new();
|
||||||
public IReadOnlyList<Forecast> Forecasts { get; init; } = Array.Empty<Forecast>();
|
public IReadOnlyList<Forecast> Forecasts { get; init; } = Array.Empty<Forecast>();
|
||||||
}
|
}
|
||||||
@@ -454,16 +454,17 @@ public sealed class SimulationEngine
|
|||||||
|
|
||||||
var hasCritical = level.Surface.Any(surface => BandFuel(surface.Fuel) == EBand.Critical || BandCoolant(surface.Coolant) == EBand.Critical || BandElectricity(surface.Electricity) == EBand.Critical || BandHeat(surface.Heat) == EBand.Critical);
|
var hasCritical = level.Surface.Any(surface => BandFuel(surface.Fuel) == EBand.Critical || BandCoolant(surface.Coolant) == EBand.Critical || BandElectricity(surface.Electricity) == EBand.Critical || BandHeat(surface.Heat) == EBand.Critical);
|
||||||
var hasCaution = hasCritical || level.Props.Any(prop => prop.ServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled) || level.Leaks.Any(leak => !leak.Repaired);
|
var hasCaution = hasCritical || level.Props.Any(prop => prop.ServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled) || level.Leaks.Any(leak => !leak.Repaired);
|
||||||
var state = hasCritical ? ELevelState.Critical : hasCaution ? ELevelState.Caution : ELevelState.Stable;
|
var state = hasCritical ? ELevelState.Critical :
|
||||||
|
hasCaution ? ELevelState.Caution : ELevelState.Stable;
|
||||||
return level with { Reactors = reactors, Global = level.Global with { LevelState = state, Status = state.ToString().ToUpperInvariant() } };
|
return level with { Reactors = reactors, Global = level.Global with { LevelState = state, Status = state.ToString().ToUpperInvariant() } };
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsReactorReady(LevelState level, ReactorBinding reactor)
|
private static bool IsReactorReady(LevelState level, ReactorBinding reactor)
|
||||||
{
|
{
|
||||||
return HasProducingConsumer(level, reactor.FuelConsumerPosition, ECarrierType.Fuel)
|
return HasProducingConsumer(level, reactor.FuelConsumerPosition, ECarrierType.Fuel)
|
||||||
&& HasProducingConsumer(level, reactor.CoolantConsumerPosition, ECarrierType.Coolant)
|
&& HasProducingConsumer(level, reactor.CoolantConsumerPosition, ECarrierType.Coolant)
|
||||||
&& HasProducingConsumer(level, reactor.ElectricityConsumerPosition, ECarrierType.Electricity)
|
&& HasProducingConsumer(level, reactor.ElectricityConsumerPosition, ECarrierType.Electricity)
|
||||||
&& level.GetSurface(reactor.ControlPosition).Heat < Balancing.Current.TerminalHeat;
|
&& level.GetSurface(reactor.ControlPosition).Heat < Balancing.Current.TerminalHeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasProducingConsumer(LevelState level, GridPosition position, ECarrierType carrier)
|
private static bool HasProducingConsumer(LevelState level, GridPosition position, ECarrierType carrier)
|
||||||
@@ -535,10 +536,11 @@ public sealed class SimulationEngine
|
|||||||
private LevelState AdvanceDurations(LevelState level)
|
private LevelState AdvanceDurations(LevelState level)
|
||||||
{
|
{
|
||||||
var surface = level.Surface.Select(cell => cell with {
|
var surface = level.Surface.Select(cell => cell with {
|
||||||
FuelBlockTurns = Math.Max(0, cell.FuelBlockTurns - 1),
|
FuelBlockTurns = Math.Max(0, cell.FuelBlockTurns - 1),
|
||||||
CoolantBlockTurns = Math.Max(0, cell.CoolantBlockTurns - 1),
|
CoolantBlockTurns = Math.Max(0, cell.CoolantBlockTurns - 1),
|
||||||
ElectricityBlockTurns = Math.Max(0, cell.ElectricityBlockTurns - 1)
|
ElectricityBlockTurns = Math.Max(0, cell.ElectricityBlockTurns - 1)
|
||||||
}).ToArray();
|
})
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
return level with { Surface = surface };
|
return level with { Surface = surface };
|
||||||
}
|
}
|
||||||
@@ -732,4 +734,4 @@ public sealed class SimulationEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly LevelValidator m_Validator = new();
|
private readonly LevelValidator m_Validator = new();
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
namespace ReactorMaintenance.Win2D;
|
namespace ReactorMaintenance.Win2D;
|
||||||
|
|
||||||
public partial class App
|
public partial class App
|
||||||
{
|
{
|
||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||||
{
|
{
|
||||||
m_Window = new MainWindow();
|
m_Window = new MainWindow();
|
||||||
m_Window.Activate();
|
m_Window.Activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Window? m_Window;
|
private Window? m_Window;
|
||||||
}
|
}
|
||||||
@@ -15,8 +15,10 @@
|
|||||||
<AppBarButton Icon="OpenFile" Label="Open" Click="Open_Click" />
|
<AppBarButton Icon="OpenFile" Label="Open" Click="Open_Click" />
|
||||||
<AppBarButton Icon="Save" Label="Save" Click="Save_Click" />
|
<AppBarButton Icon="Save" Label="Save" Click="Save_Click" />
|
||||||
<AppBarSeparator />
|
<AppBarSeparator />
|
||||||
<AppBarButton Icon="Play" Label="Simulate" Click="Simulate_Click" />
|
<AppBarButton Icon="Play" Label="End Turn" Click="EndTurn_Click" />
|
||||||
<AppBarButton Icon="Accept" Label="Activate" Click="Activate_Click" />
|
<AppBarButton Label="Interact" Click="Interact_Click" />
|
||||||
|
<AppBarButton Label="Heat Shield" Click="HeatShield_Click" />
|
||||||
|
<AppBarButton Icon="Accept" Label="Activate" Click="Activate_Click" />
|
||||||
</CommandBar>
|
</CommandBar>
|
||||||
|
|
||||||
<Grid Grid.Row="1" ColumnSpacing="0">
|
<Grid Grid.Row="1" ColumnSpacing="0">
|
||||||
@@ -37,19 +39,22 @@
|
|||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<ToggleButton IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
<ToggleButton IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
||||||
Checked="ToolToggle_Checked" ToolTipService.ToolTip="{Binding Label}"
|
Checked="ToolToggle_Checked" ToolTipService.ToolTip="{Binding Label}"
|
||||||
Padding="5" Margin="0,0,8,8">
|
Width="112" MinHeight="46" Padding="6" Margin="0,0,8,8">
|
||||||
<Image Width="96" Height="96" Source="{Binding Icon}" Stretch="Uniform" />
|
<TextBlock Text="{Binding Label}" TextWrapping="WrapWholeWords" TextAlignment="Center"
|
||||||
</ToggleButton>
|
FontSize="12" />
|
||||||
</DataTemplate>
|
</ToggleButton>
|
||||||
</ItemsControl.ItemTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl.ItemTemplate>
|
||||||
<TextBlock Text="Brush applies to the selected cell." Foreground="#9EA7AE" TextWrapping="Wrap" />
|
</ItemsControl>
|
||||||
<TextBlock Text="Left click paints. Use Robot to set the start position." Foreground="#9EA7AE"
|
<TextBlock Text="Left click selects or paints. Right click clears surface prop and hazards."
|
||||||
TextWrapping="Wrap" />
|
Foreground="#9EA7AE" TextWrapping="Wrap" />
|
||||||
</StackPanel>
|
<TextBlock Text="Door chooses the first adjacent floor edge. Reactor controls auto-bind to the first available consumers."
|
||||||
</ScrollViewer>
|
Foreground="#9EA7AE"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
<Grid Grid.Column="1" Background="#101215">
|
<Grid Grid.Column="1" Background="#101215">
|
||||||
<canvas:CanvasControl
|
<canvas:CanvasControl
|
||||||
@@ -95,18 +100,10 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Border BorderBrush="#46515A" BorderThickness="1" Padding="8" Margin="0,0,0,8"
|
<Border BorderBrush="#46515A" BorderThickness="1" Padding="8" Margin="0,0,0,8"
|
||||||
CornerRadius="3">
|
CornerRadius="3">
|
||||||
<Grid ColumnSpacing="8">
|
<TextBlock Text="{Binding Message}" Foreground="#F4F1E8" TextWrapping="Wrap" />
|
||||||
<Grid.ColumnDefinitions>
|
</Border>
|
||||||
<ColumnDefinition Width="32" />
|
</DataTemplate>
|
||||||
<ColumnDefinition Width="*" />
|
</ItemsControl.ItemTemplate>
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Image Width="28" Height="28" Source="{Binding Icon}" />
|
|
||||||
<TextBlock Grid.Column="1" Text="{Binding Message}" Foreground="#F4F1E8"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
<TargetFramework>net10.0-windows10.0.19041.0</TargetFramework>
|
||||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||||
<RootNamespace>ReactorMaintenance.Win2D</RootNamespace>
|
<RootNamespace>ReactorMaintenance.Win2D</RootNamespace>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.4.0" />
|
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.4.0" />
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1839" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1839" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -200,4 +200,4 @@ public sealed class SimulationEngineTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly SimulationEngine m_Engine = new();
|
private readonly SimulationEngine m_Engine = new();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user