128 lines
6.3 KiB
C#
128 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
using BlockOutReplica.Domain;
|
|
using UnityEngine;
|
|
|
|
namespace BlockOutReplica.Infrastructure
|
|
{
|
|
public static class OriginalLevelJsonParser
|
|
{
|
|
public static LevelDefinition Parse(string json)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(json))
|
|
throw new ArgumentException("Original level JSON is empty.", nameof(json));
|
|
// Bundled t238-level-19 uses X/Y while neighboring seeds use x/y.
|
|
// Normalize property names, not numeric values: zero remains valid
|
|
// and mixed-case coordinates cannot be mistaken for missing data.
|
|
json = Regex.Replace(json, "\"([XY])\"\\s*:", match => "\"" + match.Groups[1].Value.ToLowerInvariant() + "\":");
|
|
OriginalLevelDto source = JsonUtility.FromJson<OriginalLevelDto>(json);
|
|
if (source == null || source.BMS == null || source.DMS == null || source.CMS == null)
|
|
throw new FormatException("Original level JSON is missing BMS, DMS or CMS.");
|
|
if (source.CMS.Length == 0)
|
|
throw new FormatException("Original level contains no board cells.");
|
|
|
|
List<GridPosition> cells = ConvertCells(source.CMS);
|
|
GetBounds(cells, out int minX, out int maxX, out int minY, out int maxY);
|
|
List<PieceDefinition> pieces = new List<PieceDefinition>(source.BMS.Length);
|
|
foreach (BlockDto block in source.BMS)
|
|
pieces.Add(new PieceDefinition(block.BCT, ConvertPositions(block.BPMS), block.BIC, block.ILE, block.LBCT));
|
|
List<DoorDefinition> doors = new List<DoorDefinition>(source.DMS.Length);
|
|
foreach (DoorDto door in source.DMS)
|
|
{
|
|
List<GridPosition> positions = ConvertPositions(door.BPMS);
|
|
doors.Add(new DoorDefinition(door.BCT, positions,
|
|
ResolveDoorSide(positions, minX, maxX, minY, maxY), door.DIC));
|
|
}
|
|
var curtains = new List<CurtainLockDefinition>();
|
|
if (source.CLMS != null)
|
|
foreach (CurtainDto curtain in source.CLMS)
|
|
curtains.Add(new CurtainLockDefinition(ConvertPositions(curtain.BPMS), curtain.CLC));
|
|
List<GridPosition> walls = source.WMS == null
|
|
? new List<GridPosition>() : ConvertWalls(source.WMS);
|
|
GridPosition start = source.BSP != null && source.BSP.Length > 0
|
|
? Convert(source.BSP[0]) : new GridPosition(minX - 1, minY - 1);
|
|
return new LevelDefinition(pieces, doors, walls, cells, start,
|
|
minX, maxX, minY, maxY, curtains);
|
|
}
|
|
|
|
private static DoorSide ResolveDoorSide(IReadOnlyList<GridPosition> positions,
|
|
int minX, int maxX, int minY, int maxY)
|
|
{
|
|
float x = 0f, y = 0f;
|
|
for (int i = 0; i < positions.Count; i++) { x += positions[i].X; y += positions[i].Y; }
|
|
x /= positions.Count; y /= positions.Count;
|
|
if (y > maxY) return DoorSide.Top;
|
|
if (y < minY) return DoorSide.Bottom;
|
|
if (x < minX) return DoorSide.Left;
|
|
if (x > maxX) return DoorSide.Right;
|
|
throw new FormatException("Door is not outside the board boundary.");
|
|
}
|
|
|
|
private static void GetBounds(IReadOnlyList<GridPosition> cells,
|
|
out int minX, out int maxX, out int minY, out int maxY)
|
|
{
|
|
minX = maxX = cells[0].X; minY = maxY = cells[0].Y;
|
|
for (int i = 1; i < cells.Count; i++)
|
|
{
|
|
minX = Mathf.Min(minX, cells[i].X); maxX = Mathf.Max(maxX, cells[i].X);
|
|
minY = Mathf.Min(minY, cells[i].Y); maxY = Mathf.Max(maxY, cells[i].Y);
|
|
}
|
|
}
|
|
|
|
private static List<GridPosition> ConvertCells(CellDto[] values)
|
|
{
|
|
List<GridPosition> result = new List<GridPosition>(values.Length);
|
|
var unique = new HashSet<GridPosition>();
|
|
foreach (CellDto value in values)
|
|
{
|
|
GridPosition position = Convert(value.BPM);
|
|
if (unique.Add(position)) result.Add(position);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static List<GridPosition> ConvertWalls(WallDto[] values)
|
|
{
|
|
List<GridPosition> result = new List<GridPosition>(values.Length);
|
|
var unique = new HashSet<GridPosition>();
|
|
foreach (WallDto value in values)
|
|
{
|
|
GridPosition position = Convert(value.BPM);
|
|
if (unique.Add(position)) result.Add(position);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static List<GridPosition> ConvertPositions(GridPositionDto[] values)
|
|
{
|
|
if (values == null || values.Length == 0)
|
|
throw new FormatException("Piece or door contains no positions.");
|
|
List<GridPosition> result = new List<GridPosition>(values.Length);
|
|
var unique = new HashSet<GridPosition>();
|
|
foreach (GridPositionDto value in values)
|
|
{
|
|
GridPosition position = Convert(value);
|
|
if (unique.Add(position)) result.Add(position);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static GridPosition Convert(GridPositionDto value)
|
|
{
|
|
if (value == null) throw new FormatException("Level contains a null grid position.");
|
|
return new GridPosition(value.x, value.y);
|
|
}
|
|
|
|
[Serializable] private sealed class OriginalLevelDto
|
|
{ public BlockDto[] BMS; public DoorDto[] DMS; public WallDto[] WMS; public CellDto[] CMS; public GridPositionDto[] BSP; public CurtainDto[] CLMS; }
|
|
[Serializable] private sealed class BlockDto
|
|
{ public int BCT; public GridPositionDto[] BPMS; public int BIC; public bool ILE; public int LBCT; }
|
|
[Serializable] private sealed class DoorDto { public int BCT; public GridPositionDto[] BPMS; public int DIC; }
|
|
[Serializable] private sealed class CurtainDto { public GridPositionDto[] BPMS; public int CLC; }
|
|
[Serializable] private sealed class WallDto { public GridPositionDto BPM; }
|
|
[Serializable] private sealed class CellDto { public GridPositionDto BPM; }
|
|
[Serializable] private sealed class GridPositionDto { public int x; public int y; }
|
|
}
|
|
}
|