Files
MagnetismHeroes/Assets/MagnetismHeroes/Core/GameSession.cs
T

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