112 lines
5.3 KiB
C#
112 lines
5.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace MagnetismHeroes.Core
|
|
{
|
|
public enum SessionScreen { Home, StageSelection, Puzzle, FreePlay }
|
|
|
|
// Owns committed game state. A gesture preview never reaches this class.
|
|
public sealed class GameSession
|
|
{
|
|
private readonly StageDefinition[] _definitions;
|
|
private readonly IProgressStore _store;
|
|
private ProgressSave _progress;
|
|
public SessionScreen Screen { get; private set; } = SessionScreen.Home;
|
|
public BoardRules Board { get; private set; }
|
|
public CameraState Camera { get; private set; } = DefaultCamera();
|
|
public string LastSaveError { get; private set; }
|
|
public bool HasFreePlay => _progress.latestFreePlay != null;
|
|
public int[] CompletedStageIds => (int[])_progress.completedStageIds.Clone();
|
|
public IReadOnlyList<StageDefinition> Definitions => Array.AsReadOnly(_definitions);
|
|
|
|
public GameSession(IEnumerable<StageDefinition> definitions, IProgressStore store)
|
|
{
|
|
_definitions = definitions.ToArray();
|
|
_store = store ?? throw new ArgumentNullException(nameof(store));
|
|
if (!_store.TryLoad(out var loaded, out var error)) loaded = new ProgressSave();
|
|
LastSaveError = error;
|
|
if (!SaveValidator.TryValidate(loaded, _definitions, out error))
|
|
{ loaded = new ProgressSave(); LastSaveError = error; }
|
|
_progress = loaded;
|
|
}
|
|
|
|
public bool IsUnlocked(int stageId) => stageId == 1 || _progress.completedStageIds.Contains(stageId)
|
|
|| (stageId >= 2 && stageId <= 30 && _progress.completedStageIds.Contains(stageId - 1));
|
|
|
|
public void ShowSelection() { SaveCurrent(); Screen = SessionScreen.StageSelection; Board = null; }
|
|
public void ShowHome() { SaveCurrent(); Screen = SessionScreen.Home; Board = null; }
|
|
|
|
public bool StartStage(int stageId)
|
|
{
|
|
var definition = _definitions.FirstOrDefault(value => value.Id == stageId);
|
|
if (definition == null || !IsUnlocked(stageId)) return false;
|
|
SaveCurrent();
|
|
var restored = _progress.puzzleStates.FirstOrDefault(state => state.stageId == stageId);
|
|
Board = new BoardRules(definition, restored);
|
|
Camera = restored == null ? DefaultCamera() : CopyCamera(restored.camera);
|
|
Screen = SessionScreen.Puzzle;
|
|
SaveCurrent();
|
|
return true;
|
|
}
|
|
|
|
public bool ContinueFreePlay()
|
|
{
|
|
if (_progress.latestFreePlay == null) return false;
|
|
SaveCurrent();
|
|
Board = BoardRules.CreateFreePlay(_progress.latestFreePlay);
|
|
Camera = CopyCamera(_progress.latestFreePlay.camera);
|
|
Screen = SessionScreen.FreePlay;
|
|
return true;
|
|
}
|
|
|
|
public RuleResult MoveGroup(string groupId, Position3 delta) => Commit(() => Board.TryMoveGroup(groupId, delta));
|
|
public RuleResult Merge(string first, string second) => Commit(() => Board.TryMerge(first, second));
|
|
public RuleResult Count(string ballId) => Commit(() => Board.TryCount(ballId));
|
|
|
|
public void CommitCamera(CameraState camera)
|
|
{
|
|
// Board snapshot methods validate all camera coordinates and rotation.
|
|
Camera = Board == null ? CopyCamera(camera) : Board.ToFreePlayState(camera).camera;
|
|
SaveCurrent();
|
|
}
|
|
|
|
private RuleResult Commit(Func<RuleResult> mutation)
|
|
{
|
|
if (Board == null) return RuleResult.Reject(RejectionReason.InvalidGroup);
|
|
var result = mutation();
|
|
if (!result.Accepted) return result;
|
|
if (Screen == SessionScreen.Puzzle && Board.IsSolved)
|
|
{
|
|
// Completion, removing that unfinished puzzle, and creating free play are one saved update.
|
|
_progress.completedStageIds = _progress.completedStageIds.Concat(new[] { Board.StageId }).Distinct().OrderBy(id => id).ToArray();
|
|
_progress.puzzleStates = _progress.puzzleStates.Where(state => state.stageId != Board.StageId).ToArray();
|
|
_progress.latestFreePlay = Board.ToFreePlayState(Camera);
|
|
Board = BoardRules.CreateFreePlay(_progress.latestFreePlay);
|
|
Screen = SessionScreen.FreePlay;
|
|
}
|
|
SaveCurrent();
|
|
return result;
|
|
}
|
|
|
|
public bool SaveCurrent()
|
|
{
|
|
if (Board != null)
|
|
{
|
|
if (Screen == SessionScreen.Puzzle)
|
|
_progress.puzzleStates = _progress.puzzleStates.Where(state => state.stageId != Board.StageId)
|
|
.Concat(new[] { Board.ToPuzzleState(Camera) }).OrderBy(state => state.stageId).ToArray();
|
|
else if (Screen == SessionScreen.FreePlay) _progress.latestFreePlay = Board.ToFreePlayState(Camera);
|
|
}
|
|
var success = _store.TrySave(_progress, out var error);
|
|
LastSaveError = error;
|
|
return success;
|
|
}
|
|
|
|
public static CameraState DefaultCamera() => new CameraState {
|
|
target = new Position3(0, 0.6f, 0), rotation = new Rotation4(0, 0, 0, 1), distance = 8 };
|
|
private static CameraState CopyCamera(CameraState camera) => new CameraState {
|
|
target = camera.target, rotation = camera.rotation, distance = camera.distance };
|
|
}
|
|
}
|