Chessistics/Scripts/Main.cs

619 lines
17 KiB
C#
Raw Normal View History

using Godot;
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
using System;
using System.Collections.Generic;
using System.Linq;
using Chessistics.Engine.Commands;
using Chessistics.Engine.Events;
using Chessistics.Engine.Loading;
using Chessistics.Engine.Model;
using Chessistics.Engine.Simulation;
using Chessistics.Scripts.Board;
using Chessistics.Scripts.Input;
using Chessistics.Scripts.Pieces;
using Chessistics.Scripts.Presentation;
using Chessistics.Scripts.UI;
namespace Chessistics.Scripts;
public partial class Main : Node2D
{
private GameSim? _sim;
private LevelDef? _currentLevel;
private int _currentLevelIndex;
// Views
private BoardView _boardView = null!;
private InputMapper _inputMapper = null!;
private EventAnimator _eventAnimator = null!;
// UI
private CanvasLayer _uiLayer = null!;
private ObjectivePanel _objectivePanel = null!;
private PieceStockPanel _pieceStockPanel = null!;
private DetailPanel _detailPanel = null!;
private ControlBar _controlBar = null!;
private MetricsOverlay _metricsOverlay = null!;
private LevelSelectScreen _levelSelectScreen = null!;
private Label _levelTitle = null!;
private PanelContainer _sidePanel = null!;
private PanelContainer _controlBarWrapper = null!;
private Camera2D _camera = null!;
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
private ColorRect _fadeOverlay = null!;
// Simulation timer
private Godot.Timer _simTimer = null!;
private float _simInterval = 1.0f;
private bool _running;
private bool _panning;
private static readonly string[] LevelFiles = ["level_01.json", "level_02.json", "level_03.json", "level_04.json", "level_05.json", "level_06.json"];
private const float SidePanelWidth = 280f;
private const float ControlBarHeight = 48f;
private static readonly Color BackgroundColor = new("#2D2D2D");
public override void _Ready()
{
RenderingServer.SetDefaultClearColor(BackgroundColor);
BuildSceneTree();
ConnectSignals();
ShowLevelSelect();
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
// Fade in from black on startup
FadeIn(0.5f);
}
public override void _UnhandledInput(InputEvent @event)
{
if (@event is InputEventMouseButton mb)
{
if (mb.ButtonIndex == MouseButton.Middle)
_panning = mb.Pressed;
}
else if (@event is InputEventMouseMotion motion && _panning)
{
_camera.Position -= motion.Relative / _camera.Zoom;
}
}
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
private void FadeIn(float duration)
{
_fadeOverlay.Color = new Color(0, 0, 0, 1);
var tween = CreateTween();
tween.TweenProperty(_fadeOverlay, "color:a", 0f, duration)
.SetEase(Tween.EaseType.Out);
}
private void FadeOut(float duration, Action onComplete)
{
_fadeOverlay.Color = new Color(0, 0, 0, 0);
var tween = CreateTween();
tween.TweenProperty(_fadeOverlay, "color:a", 1f, duration)
.SetEase(Tween.EaseType.In);
tween.TweenCallback(Callable.From(onComplete));
}
private void BuildSceneTree()
{
// Camera
_camera = new Camera2D { Enabled = true };
AddChild(_camera);
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
// SFX
var sfx = new SfxManager();
AddChild(sfx);
// Board
_boardView = new BoardView();
AddChild(_boardView);
// Input
_inputMapper = new InputMapper();
_inputMapper.Initialize(_boardView);
AddChild(_inputMapper);
// Animator
_eventAnimator = new EventAnimator();
AddChild(_eventAnimator);
// Sim timer
_simTimer = new Godot.Timer { OneShot = false, WaitTime = _simInterval };
_simTimer.Timeout += OnSimTimerTick;
AddChild(_simTimer);
// --- UI Layer ---
_uiLayer = new CanvasLayer();
AddChild(_uiLayer);
// Root control anchored to viewport (required for child anchoring)
var uiRoot = new Control();
uiRoot.SetAnchorsPreset(Control.LayoutPreset.FullRect);
uiRoot.MouseFilter = Control.MouseFilterEnum.Ignore;
_uiLayer.AddChild(uiRoot);
// Level title bar (top-left)
var titleBar = new HBoxContainer();
titleBar.SetAnchorsPreset(Control.LayoutPreset.TopLeft);
titleBar.OffsetLeft = 12;
titleBar.OffsetTop = 8;
titleBar.AddThemeConstantOverride("separation", 12);
var backButton = new Button { Text = "← Menu", CustomMinimumSize = new Vector2(70, 28) };
backButton.AddThemeFontSizeOverride("font_size", 11);
var backStyle = new StyleBoxFlat
{
BgColor = new Color("#2A2A2E"),
BorderColor = new Color("#444448"),
BorderWidthBottom = 1, BorderWidthTop = 1, BorderWidthLeft = 1, BorderWidthRight = 1,
CornerRadiusTopLeft = 4, CornerRadiusTopRight = 4,
CornerRadiusBottomLeft = 4, CornerRadiusBottomRight = 4,
ContentMarginLeft = 8, ContentMarginRight = 8,
ContentMarginTop = 2, ContentMarginBottom = 2
};
backButton.AddThemeStyleboxOverride("normal", backStyle);
backButton.Pressed += OnBackToMenu;
titleBar.AddChild(backButton);
_levelTitle = new Label { Text = "CHESSISTICS" };
_levelTitle.AddThemeFontSizeOverride("font_size", 20);
_levelTitle.MouseFilter = Control.MouseFilterEnum.Ignore;
titleBar.AddChild(_levelTitle);
uiRoot.AddChild(titleBar);
// --- Side Panel (anchored to right edge) ---
_sidePanel = new PanelContainer();
_sidePanel.AnchorLeft = 1.0f;
_sidePanel.AnchorRight = 1.0f;
_sidePanel.AnchorTop = 0.0f;
_sidePanel.AnchorBottom = 1.0f;
_sidePanel.OffsetLeft = -SidePanelWidth;
_sidePanel.OffsetRight = 0;
_sidePanel.OffsetTop = 0;
_sidePanel.OffsetBottom = -ControlBarHeight;
var sidePanelStyle = new StyleBoxFlat
{
BgColor = new Color(0.14f, 0.14f, 0.16f, 0.97f),
BorderColor = new Color(0.25f, 0.25f, 0.28f),
BorderWidthLeft = 1,
ContentMarginLeft = 16,
ContentMarginRight = 16,
ContentMarginTop = 16,
ContentMarginBottom = 16
};
_sidePanel.AddThemeStyleboxOverride("panel", sidePanelStyle);
var sideScroll = new ScrollContainer
{
HorizontalScrollMode = ScrollContainer.ScrollMode.Disabled,
SizeFlagsVertical = Control.SizeFlags.ExpandFill
};
var sideVBox = new VBoxContainer();
sideVBox.AddThemeConstantOverride("separation", 12);
_objectivePanel = new ObjectivePanel();
sideVBox.AddChild(_objectivePanel);
sideVBox.AddChild(new HSeparator());
_pieceStockPanel = new PieceStockPanel();
sideVBox.AddChild(_pieceStockPanel);
_detailPanel = new DetailPanel();
sideVBox.AddChild(_detailPanel);
sideScroll.AddChild(sideVBox);
_sidePanel.AddChild(sideScroll);
uiRoot.AddChild(_sidePanel);
// --- Control Bar (anchored to bottom, left of side panel) ---
_controlBarWrapper = new PanelContainer();
_controlBarWrapper.AnchorLeft = 0.0f;
_controlBarWrapper.AnchorRight = 1.0f;
_controlBarWrapper.AnchorTop = 1.0f;
_controlBarWrapper.AnchorBottom = 1.0f;
_controlBarWrapper.OffsetTop = -ControlBarHeight;
_controlBarWrapper.OffsetRight = -SidePanelWidth;
var controlBarStyle = new StyleBoxFlat
{
BgColor = new Color(0.11f, 0.11f, 0.13f, 0.97f),
BorderColor = new Color(0.25f, 0.25f, 0.28f),
BorderWidthTop = 1,
ContentMarginLeft = 12,
ContentMarginRight = 12,
ContentMarginTop = 4,
ContentMarginBottom = 4
};
_controlBarWrapper.AddThemeStyleboxOverride("panel", controlBarStyle);
_controlBar = new ControlBar();
_controlBarWrapper.AddChild(_controlBar);
uiRoot.AddChild(_controlBarWrapper);
// --- Metrics Overlay (centered in board area) ---
var metricsCenter = new CenterContainer();
metricsCenter.SetAnchorsPreset(Control.LayoutPreset.FullRect);
metricsCenter.OffsetRight = -SidePanelWidth;
metricsCenter.OffsetBottom = -ControlBarHeight;
metricsCenter.MouseFilter = Control.MouseFilterEnum.Ignore;
_metricsOverlay = new MetricsOverlay();
_metricsOverlay.CustomMinimumSize = new Vector2(340, 260);
metricsCenter.AddChild(_metricsOverlay);
uiRoot.AddChild(metricsCenter);
// --- Level Select Screen (full viewport) ---
_levelSelectScreen = new LevelSelectScreen();
_levelSelectScreen.SetAnchorsPreset(Control.LayoutPreset.FullRect);
uiRoot.AddChild(_levelSelectScreen);
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
// --- Fade overlay (on top of everything) ---
_fadeOverlay = new ColorRect
{
Color = new Color(0, 0, 0, 1),
MouseFilter = Control.MouseFilterEnum.Ignore
};
_fadeOverlay.SetAnchorsPreset(Control.LayoutPreset.FullRect);
uiRoot.AddChild(_fadeOverlay);
// Initialize animator
_eventAnimator.Initialize(_boardView, _objectivePanel, _controlBar, _metricsOverlay);
}
private void ConnectSignals()
{
_levelSelectScreen.LevelSelected += OnLevelSelected;
_pieceStockPanel.PieceSelected += OnPieceKindSelected;
_inputMapper.PlacementRequested += OnPlacementRequested;
_inputMapper.Cancelled += OnPlacementCancelled;
_controlBar.PlayPressed += OnPlay;
_controlBar.PausePressed += OnPause;
_controlBar.StepPressed += OnStep;
_controlBar.StopPressed += OnStop;
_controlBar.SpeedChanged += OnSpeedChanged;
_eventAnimator.TurnAnimationCompleted += OnTurnAnimationCompleted;
_eventAnimator.VictoryReached += OnVictory;
_metricsOverlay.RetryPressed += OnRetry;
_metricsOverlay.NextLevelPressed += OnNextLevel;
_detailPanel.RemoveRequested += OnRemoveRequested;
_inputMapper.CellClicked += OnCellClicked;
}
private void OnCellClicked(int col, int row)
{
if (_sim == null) return;
var snap = _sim.GetSnapshot();
if (snap.Phase != SimPhase.Edit) return;
_boardView.ClearHighlights();
var coords = new Coords(col, row);
var piece = snap.Pieces.FirstOrDefault(p => p.StartCell == coords || p.EndCell == coords);
if (piece != null)
{
_detailPanel.ShowPiece(piece);
// Highlight start and end cells to show trajectory
var pieceColor = PieceView.GetPieceColor(piece.Kind);
var highlightColor = new Color(pieceColor, 0.3f);
_boardView.HighlightCells([piece.StartCell, piece.EndCell], highlightColor);
}
else
{
_detailPanel.Hide();
}
}
// --- Level Management ---
private void ShowLevelSelect()
{
_levelSelectScreen.Visible = true;
_boardView.Visible = false;
_sidePanel.Visible = false;
_controlBarWrapper.Visible = false;
_levelTitle.Visible = false;
}
private void OnLevelSelected(int levelIndex)
{
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
SfxManager.Instance?.PlayClick();
_currentLevelIndex = levelIndex;
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
// Fade out, load, fade in
FadeOut(0.25f, () =>
{
LoadLevel(levelIndex);
FadeIn(0.3f);
});
}
private void LoadLevel(int index)
{
if (index < 0 || index >= LevelFiles.Length) return;
var path = $"res://Data/levels/{LevelFiles[index]}";
var file = Godot.FileAccess.Open(path, Godot.FileAccess.ModeFlags.Read);
if (file == null)
{
GD.PrintErr($"Cannot open level file: {path}");
return;
}
var json = file.GetAsText();
file.Close();
_currentLevel = LevelLoader.Load(json);
_sim = new GameSim(_currentLevel);
_levelSelectScreen.Visible = false;
_boardView.Visible = true;
_sidePanel.Visible = true;
_controlBarWrapper.Visible = true;
_levelTitle.Visible = true;
_boardView.BuildBoard(_currentLevel);
_objectivePanel.Setup(_currentLevel.Demands);
_pieceStockPanel.Setup(_currentLevel.Stock);
_controlBar.UpdateForPhase(SimPhase.Edit);
_controlBar.ResetTurn();
_metricsOverlay.Hide();
_detailPanel.Hide();
_eventAnimator.ClearAll();
_levelTitle.Text = $"CHESSISTICS — {_currentLevel.Name}";
// Center camera on board
// Board X spans 0..W*Cell, Y spans -(H-1)*Cell .. Cell
_camera.Position = new Vector2(
_currentLevel.Width * BoardView.CellSize / 2f,
-_currentLevel.Height * BoardView.CellSize / 2f + BoardView.CellSize
);
_camera.Offset = new Vector2(-SidePanelWidth / 2f, -ControlBarHeight / 2f);
var snapshot = _sim.GetSnapshot();
GD.Print($"[Main] LoadLevel — snapshot null? {snapshot == null}, width={snapshot?.Width}, height={snapshot?.Height}");
_inputMapper.SetSnapshot(snapshot);
}
// --- Edit Phase ---
private void OnPieceKindSelected(int kindIndex)
{
_inputMapper.SelectPieceKind((PieceKind)kindIndex);
}
private void OnPlacementRequested(int kindIndex, int startCol, int startRow, int endCol, int endRow)
{
if (_sim == null) return;
var kind = (PieceKind)kindIndex;
var start = new Coords(startCol, startRow);
var end = new Coords(endCol, endRow);
var events = _sim.ProcessCommand(new PlacePieceCommand(kind, start, end));
HandleEditEvents(events);
_inputMapper.SetSnapshot(_sim.GetSnapshot());
}
private void OnPlacementCancelled()
{
_pieceStockPanel.ClearSelection();
}
private void OnRemoveRequested(int pieceId)
{
if (_sim == null) return;
var events = _sim.ProcessCommand(new RemovePieceCommand(pieceId));
HandleEditEvents(events);
_inputMapper.SetSnapshot(_sim.GetSnapshot());
}
private void HandleEditEvents(IReadOnlyList<IWorldEvent> events)
{
foreach (var evt in events)
{
switch (evt)
{
case PiecePlacedEvent placed:
CreatePieceVisual(placed);
UpdateStockFromSnapshot();
break;
case PieceRemovedEvent removed:
_eventAnimator.UnregisterPiece(removed.PieceId);
UpdateStockFromSnapshot();
_detailPanel.Hide();
break;
case PlacementRejectedEvent rejected:
GD.Print($"Placement rejected: {rejected.Reason}");
break;
case CommandRejectedEvent rejected:
GD.Print($"Command rejected: {rejected.Reason}");
break;
}
}
}
private void CreatePieceVisual(PiecePlacedEvent placed)
{
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
SfxManager.Instance?.PlayPlace();
var pieceView = new PieceView();
pieceView.Setup(placed.PieceId, placed.Kind, placed.Start, placed.End, _boardView);
_boardView.AddChild(pieceView);
Juice pass: procedural SFX, particles, polished visuals Sound (SfxManager.cs): - Procedural audio synthesis via AudioStreamWav — no external files - Distinct tones for place, produce, transfer, deliver, move, destroy, victory - Simple ADSR envelope, sine/triangle waveforms, filtered noise for swooshes Pieces (PieceView.cs): - Warm earthy palette: sage green, deep teal, dusty rose, burnt sienna - Drop shadow under each piece for depth - 3-stop radial gradient (bright center → main → dark rim) - Scale bounce on placement (0 → 1.15 → 1.0 with back-out easing) - Cargo indicator pulses gently when carrying Trajectories (TrajectView.cs): - Arrowhead at endpoint showing movement direction - Antialiased lines with piece-matched colors Cells (CellView.cs): - Warmer palette: parchment/walnut board, deep forest production, aged gold demand - Production flash uses warm golden glow instead of white - Subtle inner shadow for visual depth Animations (EventAnimator.cs): - Production: golden particles burst from production cells - Transfer: cargo slides with 2-particle trail + back-out whip easing - Destruction: pieces shrink + spin + red particle explosion - Victory: 40 confetti particles rain across the screen - All phases trigger appropriate SFX UI polish: - ControlBar: styled buttons with rounded corners, disabled states - MetricsOverlay: fade-in + scale animation, sequential metric reveals - ObjectivePanel: animated progress bars, styled fills, green flash on completion - Main: fade-in/out transitions between level select and gameplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:05:55 +02:00
var color = PieceView.GetPieceColor(placed.Kind);
var trajectView = new TrajectView();
trajectView.Setup(placed.PieceId,
_boardView.CoordsToPixel(placed.Start),
_boardView.CoordsToPixel(placed.End),
color);
_boardView.AddChild(trajectView);
_eventAnimator.RegisterPiece(placed.PieceId, pieceView, trajectView);
}
private void UpdateStockFromSnapshot()
{
if (_sim == null) return;
var snap = _sim.GetSnapshot();
foreach (var (kind, remaining) in snap.RemainingStock)
_pieceStockPanel.UpdateCount(kind, remaining);
}
// --- Exec Phase ---
private void OnPlay()
{
if (_sim == null) return;
var snap = _sim.GetSnapshot();
if (snap.Phase == SimPhase.Edit)
{
var events = _sim.ProcessCommand(new StartSimulationCommand());
foreach (var evt in events)
{
if (evt is CommandRejectedEvent r)
{
GD.Print($"Cannot start: {r.Reason}");
return;
}
}
}
else if (snap.Phase == SimPhase.Paused)
{
_sim.ProcessCommand(new ResumeSimulationCommand());
}
_running = true;
_controlBar.UpdateForPhase(SimPhase.Running);
_simTimer.WaitTime = _simInterval;
_simTimer.Start();
}
private void OnPause()
{
if (_sim == null) return;
_sim.ProcessCommand(new PauseSimulationCommand());
_running = false;
_simTimer.Stop();
_controlBar.UpdateForPhase(SimPhase.Paused);
}
private void OnStep()
{
if (_sim == null || _eventAnimator.IsAnimating) return;
var events = _sim.ProcessCommand(new StepSimulationCommand());
_eventAnimator.ProcessEvents(events);
_controlBar.UpdateForPhase(_sim.GetSnapshot().Phase);
}
private void OnStop()
{
if (_sim == null) return;
_running = false;
_simTimer.Stop();
_sim.ProcessCommand(new StopSimulationCommand());
// Full visual rebuild: clear everything and recreate from snapshot
_eventAnimator.ClearAll();
var snap = _sim.GetSnapshot();
foreach (var ps in snap.Pieces)
{
var pieceView = new PieceView();
pieceView.Setup(ps.Id, ps.Kind, ps.StartCell, ps.EndCell, _boardView);
_boardView.AddChild(pieceView);
var color = PieceView.GetPieceColor(ps.Kind);
var trajectView = new TrajectView();
trajectView.Setup(ps.Id,
_boardView.CoordsToPixel(ps.StartCell),
_boardView.CoordsToPixel(ps.EndCell),
color);
_boardView.AddChild(trajectView);
_eventAnimator.RegisterPiece(ps.Id, pieceView, trajectView);
}
_controlBar.UpdateForPhase(SimPhase.Edit);
_controlBar.ResetTurn();
_metricsOverlay.Hide();
_inputMapper.SetSnapshot(snap);
if (_currentLevel != null)
_objectivePanel.Setup(_currentLevel.Demands);
}
private void OnSpeedChanged(float interval)
{
_simInterval = interval;
if (_simTimer.TimeLeft > 0)
_simTimer.WaitTime = interval;
}
private void OnSimTimerTick()
{
if (_sim == null || _eventAnimator.IsAnimating) return;
var events = _sim.ProcessCommand(new StepSimulationCommand());
_eventAnimator.ProcessEvents(events);
}
private void OnTurnAnimationCompleted()
{
if (_sim == null) return;
var phase = _sim.GetSnapshot().Phase;
_controlBar.UpdateForPhase(phase);
if (phase == SimPhase.Victory || phase == SimPhase.Defeat)
{
_running = false;
_simTimer.Stop();
}
}
private void OnVictory()
{
_running = false;
_simTimer.Stop();
}
// --- Navigation ---
private void OnRetry()
{
LoadLevel(_currentLevelIndex);
}
private void OnNextLevel()
{
if (_currentLevelIndex + 1 < LevelFiles.Length)
LoadLevel(_currentLevelIndex + 1);
else
ShowLevelSelect();
}
private void OnBackToMenu()
{
SfxManager.Instance?.PlayClick();
_running = false;
_simTimer.Stop();
_eventAnimator.ClearAll();
FadeOut(0.2f, () =>
{
ShowLevelSelect();
FadeIn(0.3f);
});
}
}