implements PLN-0006
This commit is contained in:
parent
da79d61e9f
commit
21cece1977
@ -31,6 +31,15 @@ namespace Editor.GON.StageEditor
|
|||||||
window.Show();
|
window.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Open(StageDefinition stageDefinition)
|
||||||
|
{
|
||||||
|
var window = GetWindow<StageEditorWindow>();
|
||||||
|
window.titleContent = new GUIContent("Gardens Stage Editor");
|
||||||
|
window.minSize = new Vector2(900, 600);
|
||||||
|
window.Show();
|
||||||
|
window.SetStage(stageDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
public void CreateGUI()
|
public void CreateGUI()
|
||||||
{
|
{
|
||||||
state ??= new StageEditorState();
|
state ??= new StageEditorState();
|
||||||
|
|||||||
253
Assets/Scripts/Editor/GON/WorldEditor/StageDefinitionSyncer.cs
Normal file
253
Assets/Scripts/Editor/GON/WorldEditor/StageDefinitionSyncer.cs
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using Editor.GON.StageEditor;
|
||||||
|
using Intrepid.Gameplay.Journey;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Editor.GON.WorldEditor
|
||||||
|
{
|
||||||
|
public static class StageDefinitionSyncer
|
||||||
|
{
|
||||||
|
public const int ContractVersion = 1;
|
||||||
|
|
||||||
|
public static StageDefinition CreateOrSyncStageDefinition(WorldLayoutSO layout, WorldStageAuthoring stage)
|
||||||
|
{
|
||||||
|
if (layout is null || stage is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.EnsureDefaults();
|
||||||
|
stage.EnsureDefaults();
|
||||||
|
|
||||||
|
var definition = stage.stageDefinition;
|
||||||
|
if (definition is null)
|
||||||
|
{
|
||||||
|
definition = CreateStageDefinitionAsset(layout, stage);
|
||||||
|
if (definition is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.stageDefinition = definition;
|
||||||
|
EditorUtility.SetDirty(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncStageDefinition(layout, stage, definition);
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SyncStageDefinition(WorldLayoutSO layout, WorldStageAuthoring stage, StageDefinition definition)
|
||||||
|
{
|
||||||
|
if (layout is null || stage is null || definition is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Undo.RecordObject(definition, "Sync stage definition world contract");
|
||||||
|
definition.EnsureDefaults();
|
||||||
|
definition.worldSync = BuildSyncData(layout, stage);
|
||||||
|
EditorUtility.SetDirty(definition);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StageWorldSyncData BuildSyncData(WorldLayoutSO layout, WorldStageAuthoring stage)
|
||||||
|
{
|
||||||
|
layout.EnsureDefaults();
|
||||||
|
stage.EnsureDefaults();
|
||||||
|
|
||||||
|
var data = new StageWorldSyncData
|
||||||
|
{
|
||||||
|
worldLayoutId = layout.id,
|
||||||
|
worldStageId = stage.id,
|
||||||
|
worldStageDisplayName = stage.displayName,
|
||||||
|
worldStageKind = stage.stageKind,
|
||||||
|
worldLayer = stage.worldLayer,
|
||||||
|
worldTileSize = layout.worldTileSize,
|
||||||
|
worldLayerHeight = layout.worldLayerHeight,
|
||||||
|
syncedAt = DateTime.UtcNow.ToString("O"),
|
||||||
|
contractVersion = ContractVersion
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var tile in stage.occupiedTiles)
|
||||||
|
{
|
||||||
|
data.worldTiles.Add(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var endpoint in GetStageEndpoints(layout, stage.id))
|
||||||
|
{
|
||||||
|
data.expectedEndpoints.Add(BuildEndpoint(layout, endpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
data.contractHash = ComputeContractHash(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ComputeExpectedContractHash(WorldLayoutSO layout, WorldStageAuthoring stage)
|
||||||
|
{
|
||||||
|
var data = BuildSyncData(layout, stage);
|
||||||
|
return data.contractHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ComputeContractHash(StageWorldSyncData data)
|
||||||
|
{
|
||||||
|
if (data is null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.EnsureDefaults();
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.Append("v=").Append(ContractVersion).Append('\n');
|
||||||
|
builder.Append("layout=").Append(data.worldLayoutId).Append('\n');
|
||||||
|
builder.Append("stage=").Append(data.worldStageId).Append('\n');
|
||||||
|
builder.Append("name=").Append(data.worldStageDisplayName).Append('\n');
|
||||||
|
builder.Append("kind=").Append((int)data.worldStageKind).Append('\n');
|
||||||
|
builder.Append("layer=").Append(data.worldLayer).Append('\n');
|
||||||
|
builder.Append("tileSize=").Append(data.worldTileSize.ToString("R", CultureInfo.InvariantCulture)).Append('\n');
|
||||||
|
builder.Append("layerHeight=").Append(data.worldLayerHeight.ToString("R", CultureInfo.InvariantCulture)).Append('\n');
|
||||||
|
|
||||||
|
var tiles = new List<JWorldTile>(data.worldTiles);
|
||||||
|
tiles.Sort(CompareTiles);
|
||||||
|
foreach (var tile in tiles)
|
||||||
|
{
|
||||||
|
builder.Append("tile=").Append(tile.x).Append(',').Append(tile.y).Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
var endpoints = new List<StageWorldSyncEndpoint>(data.expectedEndpoints);
|
||||||
|
endpoints.Sort(CompareEndpoints);
|
||||||
|
foreach (var endpoint in endpoints)
|
||||||
|
{
|
||||||
|
builder.Append("endpoint=")
|
||||||
|
.Append(endpoint.endpointId).Append('|')
|
||||||
|
.Append((int)endpoint.kind).Append('|')
|
||||||
|
.Append(endpoint.worldTile.x).Append(',').Append(endpoint.worldTile.y).Append('|')
|
||||||
|
.Append((int)endpoint.socketFace).Append('|')
|
||||||
|
.Append(endpoint.required ? "1" : "0").Append('|')
|
||||||
|
.Append(endpoint.connectionId).Append('|')
|
||||||
|
.Append(endpoint.connectedEndpointId).Append('|')
|
||||||
|
.Append(endpoint.debugLabel).Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
using var sha = SHA256.Create();
|
||||||
|
var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(builder.ToString()));
|
||||||
|
var hash = new StringBuilder(bytes.Length * 2);
|
||||||
|
foreach (var b in bytes)
|
||||||
|
{
|
||||||
|
hash.Append(b.ToString("x2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StageDefinition CreateStageDefinitionAsset(WorldLayoutSO layout, WorldStageAuthoring stage)
|
||||||
|
{
|
||||||
|
var layoutPath = AssetDatabase.GetAssetPath(layout);
|
||||||
|
if (string.IsNullOrEmpty(layoutPath))
|
||||||
|
{
|
||||||
|
Debug.LogError("Cannot create StageDefinition because the WorldLayoutSO is not saved as an asset.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var layoutDirectory = Path.GetDirectoryName(layoutPath)?.Replace('\\', '/');
|
||||||
|
if (string.IsNullOrEmpty(layoutDirectory))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stagesFolder = layoutDirectory + "/Stages";
|
||||||
|
if (!AssetDatabase.IsValidFolder(stagesFolder))
|
||||||
|
{
|
||||||
|
AssetDatabase.CreateFolder(layoutDirectory, "Stages");
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = MakeSafeFileName(string.IsNullOrWhiteSpace(stage.displayName) ? stage.id : stage.displayName);
|
||||||
|
var path = AssetDatabase.GenerateUniqueAssetPath(stagesFolder + "/" + fileName + ".stage-definition.asset");
|
||||||
|
|
||||||
|
var definition = ScriptableObject.CreateInstance<StageDefinition>();
|
||||||
|
definition.Id = Path.GetFileNameWithoutExtension(path).ToLowerInvariant();
|
||||||
|
definition.EnsureDefaults();
|
||||||
|
AssetDatabase.CreateAsset(definition, path);
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StageWorldSyncEndpoint BuildEndpoint(WorldLayoutSO layout, WorldEndpointAuthoring endpoint)
|
||||||
|
{
|
||||||
|
var connection = FindFirstConnection(layout, endpoint.id);
|
||||||
|
var connectedEndpointId = connection is null
|
||||||
|
? string.Empty
|
||||||
|
: connection.endpointAId == endpoint.id
|
||||||
|
? connection.endpointBId
|
||||||
|
: connection.endpointAId;
|
||||||
|
|
||||||
|
return new StageWorldSyncEndpoint
|
||||||
|
{
|
||||||
|
endpointId = endpoint.id,
|
||||||
|
kind = endpoint.kind,
|
||||||
|
worldTile = endpoint.worldTile,
|
||||||
|
socketFace = endpoint.socketFace,
|
||||||
|
required = endpoint.required,
|
||||||
|
connectionId = connection?.id ?? string.Empty,
|
||||||
|
connectedEndpointId = connectedEndpointId,
|
||||||
|
debugLabel = endpoint.kind + " " + endpoint.id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<WorldEndpointAuthoring> GetStageEndpoints(WorldLayoutSO layout, string stageId)
|
||||||
|
{
|
||||||
|
var endpoints = new List<WorldEndpointAuthoring>();
|
||||||
|
foreach (var endpoint in layout.endpoints)
|
||||||
|
{
|
||||||
|
if (endpoint != null && endpoint.stageId == stageId)
|
||||||
|
{
|
||||||
|
endpoints.Add(endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints.Sort((a, b) => string.CompareOrdinal(a.id, b.id));
|
||||||
|
return endpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WorldConnectionAuthoring FindFirstConnection(WorldLayoutSO layout, string endpointId)
|
||||||
|
{
|
||||||
|
foreach (var connection in layout.connections)
|
||||||
|
{
|
||||||
|
if (connection != null &&
|
||||||
|
(connection.endpointAId == endpointId || connection.endpointBId == endpointId))
|
||||||
|
{
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CompareTiles(JWorldTile a, JWorldTile b)
|
||||||
|
{
|
||||||
|
var x = a.x.CompareTo(b.x);
|
||||||
|
return x != 0 ? x : a.y.CompareTo(b.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CompareEndpoints(StageWorldSyncEndpoint a, StageWorldSyncEndpoint b)
|
||||||
|
{
|
||||||
|
return string.CompareOrdinal(a?.endpointId, b?.endpointId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string MakeSafeFileName(string value)
|
||||||
|
{
|
||||||
|
var safe = new StringBuilder();
|
||||||
|
foreach (var c in value)
|
||||||
|
{
|
||||||
|
safe.Append(char.IsLetterOrDigit(c) || c == '-' || c == '_' ? c : '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = safe.ToString().Trim('-');
|
||||||
|
return string.IsNullOrEmpty(result) ? "StageDefinition" : result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 26d7437e85a64a85a49c28877b7e145b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Editor.GON.StageEditor;
|
using Editor.GON.StageEditor;
|
||||||
using Intrepid.Gameplay.Journey;
|
using Intrepid.Gameplay.Journey;
|
||||||
|
using UnityEditor;
|
||||||
using UnityEditor.UIElements;
|
using UnityEditor.UIElements;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
@ -128,6 +129,39 @@ namespace Editor.GON.WorldEditor
|
|||||||
stage.stageDefinition = evt.newValue as StageDefinition;
|
stage.stageDefinition = evt.newValue as StageDefinition;
|
||||||
}));
|
}));
|
||||||
Add(definition);
|
Add(definition);
|
||||||
|
|
||||||
|
var syncButton = new Button(delegate
|
||||||
|
{
|
||||||
|
owner.MutateLayout(
|
||||||
|
stage.stageDefinition is null ? "Create stage definition" : "Sync stage definition",
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
StageDefinitionSyncer.CreateOrSyncStageDefinition(worldLayout, stage);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
{
|
||||||
|
text = stage.stageDefinition is null ? "Create StageDefinition" : "Sync StageDefinition",
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
marginTop = 8
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Add(syncButton);
|
||||||
|
|
||||||
|
var openButton = new Button(delegate
|
||||||
|
{
|
||||||
|
Selection.activeObject = stage.stageDefinition;
|
||||||
|
StageEditorWindow.Open(stage.stageDefinition);
|
||||||
|
})
|
||||||
|
{
|
||||||
|
text = "Open Stage Editor",
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
marginTop = 4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
openButton.SetEnabled(stage.stageDefinition is not null);
|
||||||
|
Add(openButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildEndpointInspector(WorldEndpointAuthoring endpoint)
|
private void BuildEndpointInspector(WorldEndpointAuthoring endpoint)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user