Compare commits

...

10 Commits

Author SHA1 Message Date
Nilton Constantino
1a332897bf
organize project 2026-06-22 05:14:56 +01:00
Nilton Constantino
d0b744d162
integration between World and Stage Editors - added guidances on Stage From World 2026-06-21 09:52:19 +01:00
Nilton Constantino
e889746fc4
center world sync underlay on stage origin 2026-06-21 08:57:09 +01:00
Nilton Constantino
5886cc32d9
allow wider stage editor zoom out 2026-06-21 08:51:29 +01:00
Nilton Constantino
77bc1dff51
fix stage world sync underlay origin 2026-06-21 08:34:25 +01:00
Nilton Constantino
28147da443
implements PLN-0008 2026-06-21 08:31:14 +01:00
Nilton Constantino
f8a8adb80e
implements PLN-0007 2026-06-21 08:29:55 +01:00
Nilton Constantino
21cece1977
implements PLN-0006 2026-06-21 08:29:14 +01:00
Nilton Constantino
da79d61e9f
implements PLN-0005 2026-06-21 08:27:44 +01:00
Nilton Constantino
d0777cf2ba
World Editor / Stage Editor Sync 2026-06-21 08:21:41 +01:00
105 changed files with 70692 additions and 43712 deletions

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8119b8473df143cbb69a18ae2aa430c4
timeCreated: 1779787550

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 78ca007dda30545739e1fce83a853093 guid: 3fe33c5d9e3b94e37b5a7ef0785ddd6d
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: c02ec45113a494816a04c196b1f5ecee guid: ea6d43c28924f466a98addf9d760c4a1
folderAsset: yes NativeFormatImporter:
DefaultImporter:
externalObjects: {} externalObjects: {}
mainObjectFileID: 11400000
userData: userData:
assetBundleName: assetBundleName:
assetBundleVariant: assetBundleVariant:

View File

@ -0,0 +1,160 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 778f9d79eb2c410482efdc25b248dd46, type: 3}
m_Name: Stage-3.stage-definition
m_EditorClassIdentifier: Assembly-CSharp-Editor::Editor.GON.StageEditor.StageDefinition
id: stage-3.stage-definition
cellSize: 1
floorHeight: 6
activeFloorIndex: 0
worldSync:
worldLayoutId: 858671c3949e4543b0d68818c9b2e41b
worldStageId: stage_f5a0fa8cc3a14a94b8b6fa80c4e2413d
worldStageDisplayName: Stage 3
worldStageKind: 0
worldLayer: 0
worldTileSize: 24
worldLayerHeight: 24
worldTiles:
- x: 8
y: 8
- x: 9
y: 8
- x: 10
y: 8
- x: 11
y: 8
- x: 12
y: 8
- x: 8
y: 9
- x: 9
y: 9
- x: 10
y: 9
- x: 11
y: 9
- x: 12
y: 9
- x: 8
y: 10
- x: 9
y: 10
- x: 10
y: 10
- x: 11
y: 10
- x: 12
y: 10
- x: 8
y: 11
- x: 9
y: 11
- x: 10
y: 11
- x: 11
y: 11
- x: 12
y: 11
- x: 8
y: 12
- x: 9
y: 12
- x: 10
y: 12
- x: 11
y: 12
- x: 12
y: 12
- x: 13
y: 9
- x: 13
y: 10
- x: 13
y: 11
- x: 13
y: 12
- x: 8
y: 7
- x: 9
y: 7
- x: 10
y: 7
- x: 11
y: 7
- x: 7
y: 7
- x: 7
y: 8
- x: 7
y: 9
- x: 7
y: 10
expectedEndpoints:
- endpointId: endpoint_15ad351d9d8c42d3ae74bb2f53eab64f
kind: 0
worldTile:
x: 11
y: 7
socketFace: 2
required: 1
connectionId: connection_04970d242c884a1a80a89c76cb63a8aa
connectedEndpointId: endpoint_b86dd04c491644cfa521a963b25df427
debugLabel: Socket endpoint_15ad351d9d8c42d3ae74bb2f53eab64f
- endpointId: endpoint_181357103f5742008cfa73a544a56363
kind: 0
worldTile:
x: 10
y: 12
socketFace: 1
required: 1
connectionId: connection_cdf62cf06a20426b88d3987ea8917967
connectedEndpointId: endpoint_adc34044a48447d0be41389ba398d5f5
debugLabel: Socket endpoint_181357103f5742008cfa73a544a56363
- endpointId: endpoint_34e27be82570488cbcd45531e56f128c
kind: 1
worldTile:
x: 10
y: 10
socketFace: 0
required: 1
connectionId: connection_d8da15ab563d4bb9ba6f0294cec60341
connectedEndpointId: endpoint_e6e40c3366564b96825ecf652721a46d
debugLabel: Anchor endpoint_34e27be82570488cbcd45531e56f128c
- endpointId: endpoint_36134ffa6b66481394ee9b3f57d4041a
kind: 0
worldTile:
x: 12
y: 8
socketFace: 0
required: 1
connectionId: connection_cd513cb9431948c79ecf0f61eb97901e
connectedEndpointId: endpoint_0e12ccb5b4554da6b5ae02739e73763d
debugLabel: Socket endpoint_36134ffa6b66481394ee9b3f57d4041a
- endpointId: endpoint_65e01fe22fe44222919c322ea8580a69
kind: 0
worldTile:
x: 13
y: 12
socketFace: 1
required: 1
connectionId: connection_ef34c049437c491b80364a034880300d
connectedEndpointId: endpoint_dc223745e7994fbd880f1e93f146d8cf
debugLabel: Socket endpoint_65e01fe22fe44222919c322ea8580a69
syncedAt: 2026-06-21T08:02:09.2808120Z
contractVersion: 1
contractHash: a09bfc4e267078e7927d6bdb59732982afff3cd0e0567751e33c168c031b83a2
floors:
- floorId: F0
FloorHeight: 0
cells: []
objects: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 06d41cd3f2d8242b3b93b0b8bc624c82
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,118 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 778f9d79eb2c410482efdc25b248dd46, type: 3}
m_Name: Stage-5.stage-definition
m_EditorClassIdentifier: Assembly-CSharp-Editor::Editor.GON.StageEditor.StageDefinition
id: stage-5.stage-definition
cellSize: 1
floorHeight: 6
activeFloorIndex: 0
worldSync:
worldLayoutId: 858671c3949e4543b0d68818c9b2e41b
worldStageId: stage_aae8c48d41d84a679c9c52acd567ff30
worldStageDisplayName: Stage 5
worldStageKind: 0
worldLayer: 0
worldTileSize: 24
worldLayerHeight: 24
worldTiles:
- x: 8
y: 13
- x: 9
y: 13
- x: 10
y: 13
- x: 11
y: 13
- x: 12
y: 13
- x: 8
y: 14
- x: 9
y: 14
- x: 10
y: 14
- x: 11
y: 14
- x: 12
y: 14
- x: 8
y: 15
- x: 9
y: 15
- x: 10
y: 15
- x: 11
y: 15
- x: 12
y: 15
- x: 8
y: 16
- x: 9
y: 16
- x: 10
y: 16
- x: 11
y: 16
- x: 12
y: 16
- x: 13
y: 14
- x: 13
y: 15
- x: 13
y: 16
- x: 11
y: 17
- x: 12
y: 17
- x: 13
y: 17
expectedEndpoints:
- endpointId: endpoint_9c245891f5734102bc24edb0dcc2c9fd
kind: 1
worldTile:
x: 11
y: 15
socketFace: 0
required: 1
connectionId: connection_335b1c8a1f964b11b72ddf2ef053bd71
connectedEndpointId: endpoint_e6e40c3366564b96825ecf652721a46d
debugLabel: Anchor endpoint_9c245891f5734102bc24edb0dcc2c9fd
- endpointId: endpoint_adc34044a48447d0be41389ba398d5f5
kind: 0
worldTile:
x: 10
y: 13
socketFace: 0
required: 1
connectionId: connection_cdf62cf06a20426b88d3987ea8917967
connectedEndpointId: endpoint_181357103f5742008cfa73a544a56363
debugLabel: Socket endpoint_adc34044a48447d0be41389ba398d5f5
- endpointId: endpoint_d9121bcc789443548c898106dd5f4c43
kind: 0
worldTile:
x: 13
y: 14
socketFace: 0
required: 1
connectionId: connection_3dabe3efe429439bbb49104bed37bb20
connectedEndpointId: endpoint_71bd3851ad2b4d20a90cf3b3f5ade5fb
debugLabel: Socket endpoint_d9121bcc789443548c898106dd5f4c43
syncedAt: 2026-06-21T07:58:09.8342000Z
contractVersion: 1
contractHash: 9a915866b1f094e86c55ab2a3cc9f590c1dd85fb188e96fc42cc67ac4f6bf1fa
floors:
- floorId: F0
FloorHeight: 0
cells: []
objects: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 80f0a2ed01099420fb86c249db820069
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 778f9d79eb2c410482efdc25b248dd46, type: 3}
m_Name: Stage-6.stage-definition
m_EditorClassIdentifier: Assembly-CSharp-Editor::Editor.GON.StageEditor.StageDefinition
id: stage-6.stage-definition
cellSize: 1
floorHeight: 6
activeFloorIndex: 0
worldSync:
worldLayoutId: 858671c3949e4543b0d68818c9b2e41b
worldStageId: stage_526b633d818b4e36b96ad92f540b9415
worldStageDisplayName: Stage 6
worldStageKind: 0
worldLayer: 0
worldTileSize: 24
worldLayerHeight: 24
worldTiles:
- x: 17
y: 7
- x: 18
y: 7
- x: 19
y: 7
- x: 17
y: 8
- x: 18
y: 8
- x: 19
y: 8
expectedEndpoints:
- endpointId: endpoint_27a37615b51f40cdb39acfc9fbffce22
kind: 0
worldTile:
x: 18
y: 8
socketFace: 1
required: 1
connectionId: connection_2b7aa20f26bd48acb636a37ed1d20ad0
connectedEndpointId: endpoint_7cf95989e98a4de49edb7e2080e1c5e6
debugLabel: Socket endpoint_27a37615b51f40cdb39acfc9fbffce22
syncedAt: 2026-06-21T15:26:43.1912120Z
contractVersion: 1
contractHash: 61f8fa773b7b82088215f69785714fb78c7b2bd51cdf88bf807b1524aa002b10
floors:
- floorId: F0
FloorHeight: 0
cells: []
objects: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c20501bce39ca4efeb6a7b89c02ba4f4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,130 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 778f9d79eb2c410482efdc25b248dd46, type: 3}
m_Name: Stage-7.stage-definition
m_EditorClassIdentifier: Assembly-CSharp-Editor::Editor.GON.StageEditor.StageDefinition
id: stage-7.stage-definition
cellSize: 1
floorHeight: 6
activeFloorIndex: 0
worldSync:
worldLayoutId: 858671c3949e4543b0d68818c9b2e41b
worldStageId: stage_3e76ac8acd0447d59b0fbf1a65a25fb3
worldStageDisplayName: Stage 7
worldStageKind: 0
worldLayer: 0
worldTileSize: 24
worldLayerHeight: 24
worldTiles:
- x: 13
y: 13
- x: 14
y: 13
- x: 16
y: 13
- x: 17
y: 13
- x: 15
y: 13
- x: 17
y: 9
- x: 18
y: 9
- x: 19
y: 9
- x: 17
y: 10
- x: 18
y: 10
- x: 19
y: 10
- x: 17
y: 11
- x: 18
y: 11
- x: 19
y: 11
- x: 17
y: 12
- x: 18
y: 12
- x: 18
y: 13
- x: 14
y: 14
- x: 15
y: 14
- x: 16
y: 14
- x: 17
y: 14
- x: 18
y: 14
expectedEndpoints:
- endpointId: endpoint_0e3b0c016a474adb8fe8b05bdfd79f14
kind: 2
worldTile:
x: 18
y: 10
socketFace: 0
required: 1
connectionId: connection_12db74114bf54c8eb5b7038efcae4172
connectedEndpointId: endpoint_ae66297a1c1a4c259e26ea8b51d69851
debugLabel: Lift endpoint_0e3b0c016a474adb8fe8b05bdfd79f14
- endpointId: endpoint_71bd3851ad2b4d20a90cf3b3f5ade5fb
kind: 0
worldTile:
x: 13
y: 13
socketFace: 1
required: 1
connectionId: connection_3dabe3efe429439bbb49104bed37bb20
connectedEndpointId: endpoint_d9121bcc789443548c898106dd5f4c43
debugLabel: Socket endpoint_71bd3851ad2b4d20a90cf3b3f5ade5fb
- endpointId: endpoint_7cf95989e98a4de49edb7e2080e1c5e6
kind: 0
worldTile:
x: 18
y: 9
socketFace: 0
required: 1
connectionId: connection_2b7aa20f26bd48acb636a37ed1d20ad0
connectedEndpointId: endpoint_27a37615b51f40cdb39acfc9fbffce22
debugLabel: Socket endpoint_7cf95989e98a4de49edb7e2080e1c5e6
- endpointId: endpoint_dc223745e7994fbd880f1e93f146d8cf
kind: 0
worldTile:
x: 13
y: 13
socketFace: 0
required: 1
connectionId: connection_ef34c049437c491b80364a034880300d
connectedEndpointId: endpoint_65e01fe22fe44222919c322ea8580a69
debugLabel: Socket endpoint_dc223745e7994fbd880f1e93f146d8cf
- endpointId: endpoint_e6e40c3366564b96825ecf652721a46d
kind: 1
worldTile:
x: 17
y: 13
socketFace: 0
required: 1
connectionId: connection_d8da15ab563d4bb9ba6f0294cec60341
connectedEndpointId: endpoint_34e27be82570488cbcd45531e56f128c
debugLabel: Anchor endpoint_e6e40c3366564b96825ecf652721a46d
syncedAt: 2026-06-21T15:26:32.9022510Z
contractVersion: 1
contractHash: 78fcf2e37e20cc2a2f3886f01c66855bfc05925f47848f83ae372c9665076bff
floors:
- floorId: F0
FloorHeight: 0
cells: []
objects: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 87686391a47f9457c9c54232deb9707e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -18,26 +18,12 @@ MonoBehaviour:
minWorldLayer: -2 minWorldLayer: -2
maxWorldLayer: 1 maxWorldLayer: 1
stages: stages:
- id: stage_9202fe485eaa408d86e379b3fdafa49b
displayName: Stage 1
stageKind: 0
worldLayer: 0
color: {r: 0.43435237, g: 0.72672, b: 0.73008716, a: 0.820845}
stageDefinition: {fileID: 0}
occupiedTiles: []
- id: stage_25e8797ce5d9420b97aaf3e06f760b1d
displayName: Stage 2
stageKind: 0
worldLayer: 0
color: {r: 0.71619093, g: 0.6632371, b: 0.3653053, a: 0.7631802}
stageDefinition: {fileID: 0}
occupiedTiles: []
- id: stage_f5a0fa8cc3a14a94b8b6fa80c4e2413d - id: stage_f5a0fa8cc3a14a94b8b6fa80c4e2413d
displayName: Stage 3 displayName: Stage 3
stageKind: 0 stageKind: 0
worldLayer: 0 worldLayer: 0
color: {r: 0.26295125, g: 0.36055228, b: 0.6102875, a: 0.76864} color: {r: 0.26295125, g: 0.36055228, b: 0.6102875, a: 0.76864}
stageDefinition: {fileID: 0} stageDefinition: {fileID: 11400000, guid: 06d41cd3f2d8242b3b93b0b8bc624c82, type: 2}
occupiedTiles: occupiedTiles:
- x: 8 - x: 8
y: 8 y: 8
@ -113,19 +99,12 @@ MonoBehaviour:
y: 9 y: 9
- x: 7 - x: 7
y: 10 y: 10
- id: stage_92bb328658e04aceb915822ac7ff20ae
displayName: Stage 4
stageKind: 0
worldLayer: 0
color: {r: 0.7052704, g: 0.6072819, b: 0.39938506, a: 0.8043456}
stageDefinition: {fileID: 0}
occupiedTiles: []
- id: stage_aae8c48d41d84a679c9c52acd567ff30 - id: stage_aae8c48d41d84a679c9c52acd567ff30
displayName: Stage 5 displayName: Stage 5
stageKind: 0 stageKind: 0
worldLayer: 0 worldLayer: 0
color: {r: 0.73909104, g: 0.47607818, b: 0.6006857, a: 0.8447911} color: {r: 0.73909104, g: 0.47607818, b: 0.6006857, a: 0.8447911}
stageDefinition: {fileID: 0} stageDefinition: {fileID: 11400000, guid: 80f0a2ed01099420fb86c249db820069, type: 2}
occupiedTiles: occupiedTiles:
- x: 8 - x: 8
y: 13 y: 13
@ -184,7 +163,7 @@ MonoBehaviour:
stageKind: 0 stageKind: 0
worldLayer: 0 worldLayer: 0
color: {r: 0.59988964, g: 0.84526074, b: 0.5487183, a: 0.7954139} color: {r: 0.59988964, g: 0.84526074, b: 0.5487183, a: 0.7954139}
stageDefinition: {fileID: 0} stageDefinition: {fileID: 11400000, guid: c20501bce39ca4efeb6a7b89c02ba4f4, type: 2}
occupiedTiles: occupiedTiles:
- x: 17 - x: 17
y: 7 y: 7
@ -203,7 +182,7 @@ MonoBehaviour:
stageKind: 0 stageKind: 0
worldLayer: 0 worldLayer: 0
color: {r: 0.7086307, g: 0.25315043, b: 0.84043574, a: 0.79866225} color: {r: 0.7086307, g: 0.25315043, b: 0.84043574, a: 0.79866225}
stageDefinition: {fileID: 0} stageDefinition: {fileID: 11400000, guid: 87686391a47f9457c9c54232deb9707e, type: 2}
occupiedTiles: occupiedTiles:
- x: 13 - x: 13
y: 13 y: 13
@ -215,58 +194,30 @@ MonoBehaviour:
y: 13 y: 13
- x: 15 - x: 15
y: 13 y: 13
- x: 14
y: 9
- x: 15
y: 9
- x: 16
y: 9
- x: 17 - x: 17
y: 9 y: 9
- x: 18 - x: 18
y: 9 y: 9
- x: 19 - x: 19
y: 9 y: 9
- x: 14
y: 10
- x: 15
y: 10
- x: 16
y: 10
- x: 17 - x: 17
y: 10 y: 10
- x: 18 - x: 18
y: 10 y: 10
- x: 19 - x: 19
y: 10 y: 10
- x: 14
y: 11
- x: 15
y: 11
- x: 16
y: 11
- x: 17 - x: 17
y: 11 y: 11
- x: 18 - x: 18
y: 11 y: 11
- x: 19 - x: 19
y: 11 y: 11
- x: 14
y: 12
- x: 15
y: 12
- x: 16
y: 12
- x: 17 - x: 17
y: 12 y: 12
- x: 18 - x: 18
y: 12 y: 12
- x: 19
y: 12
- x: 18 - x: 18
y: 13 y: 13
- x: 19
y: 13
- x: 14 - x: 14
y: 14 y: 14
- x: 15 - x: 15
@ -277,44 +228,6 @@ MonoBehaviour:
y: 14 y: 14
- x: 18 - x: 18
y: 14 y: 14
- x: 19
y: 14
- x: 14
y: 15
- x: 15
y: 15
- x: 16
y: 15
- x: 17
y: 15
- x: 18
y: 15
- x: 19
y: 15
- x: 14
y: 16
- x: 15
y: 16
- x: 16
y: 16
- x: 17
y: 16
- x: 18
y: 16
- x: 19
y: 16
- x: 14
y: 17
- x: 15
y: 17
- x: 16
y: 17
- x: 17
y: 17
- x: 18
y: 17
- x: 19
y: 17
- id: stage_1a60b23d8b4c4622905831237c716081 - id: stage_1a60b23d8b4c4622905831237c716081
displayName: Stage 8 displayName: Stage 8
stageKind: 0 stageKind: 0
@ -460,20 +373,15 @@ MonoBehaviour:
y: 12 y: 12
- x: 20 - x: 20
y: 12 y: 12
- id: stage_bfa43a65aced42ceb03f56b11dce0998 - id: stage_7c3044f376a540c98034ac5057038985
displayName: Stage 15 displayName: Save Point 8
stageKind: 0 stageKind: 1
worldLayer: 0 worldLayer: 0
color: {r: 0.82491213, g: 0.683962, b: 0.2155831, a: 0.8341453} color: {r: 0.95, g: 0.25, b: 0.85, a: 0.82}
stageDefinition: {fileID: 0} stageDefinition: {fileID: 11400000, guid: ea6d43c28924f466a98addf9d760c4a1, type: 2}
occupiedTiles: [] occupiedTiles:
- id: stage_b24d4774f45444138b8cfda33ed5034e - x: 12
displayName: Stage 16 y: 7
stageKind: 0
worldLayer: 0
color: {r: 0.44949582, g: 0.6144462, b: 0.7657885, a: 0.82364887}
stageDefinition: {fileID: 0}
occupiedTiles: []
endpoints: endpoints:
- id: endpoint_adc34044a48447d0be41389ba398d5f5 - id: endpoint_adc34044a48447d0be41389ba398d5f5
stageId: stage_aae8c48d41d84a679c9c52acd567ff30 stageId: stage_aae8c48d41d84a679c9c52acd567ff30
@ -595,6 +503,38 @@ MonoBehaviour:
kind: 2 kind: 2
required: 1 required: 1
socketFace: 0 socketFace: 0
- id: endpoint_0e12ccb5b4554da6b5ae02739e73763d
stageId: stage_7c3044f376a540c98034ac5057038985
worldTile:
x: 12
y: 7
kind: 0
required: 1
socketFace: 1
- id: endpoint_36134ffa6b66481394ee9b3f57d4041a
stageId: stage_f5a0fa8cc3a14a94b8b6fa80c4e2413d
worldTile:
x: 12
y: 8
kind: 0
required: 1
socketFace: 0
- id: endpoint_b86dd04c491644cfa521a963b25df427
stageId: stage_7c3044f376a540c98034ac5057038985
worldTile:
x: 12
y: 7
kind: 0
required: 1
socketFace: 3
- id: endpoint_15ad351d9d8c42d3ae74bb2f53eab64f
stageId: stage_f5a0fa8cc3a14a94b8b6fa80c4e2413d
worldTile:
x: 11
y: 7
kind: 0
required: 1
socketFace: 2
connections: connections:
- id: connection_cdf62cf06a20426b88d3987ea8917967 - id: connection_cdf62cf06a20426b88d3987ea8917967
endpointAId: endpoint_adc34044a48447d0be41389ba398d5f5 endpointAId: endpoint_adc34044a48447d0be41389ba398d5f5
@ -636,3 +576,11 @@ MonoBehaviour:
endpointAId: endpoint_263ea59d79614b9b85ed2bb4fe25c9ad endpointAId: endpoint_263ea59d79614b9b85ed2bb4fe25c9ad
endpointBId: endpoint_6f17b532985e4e908e70b3802eccf8cc endpointBId: endpoint_6f17b532985e4e908e70b3802eccf8cc
directionality: 0 directionality: 0
- id: connection_cd513cb9431948c79ecf0f61eb97901e
endpointAId: endpoint_0e12ccb5b4554da6b5ae02739e73763d
endpointBId: endpoint_36134ffa6b66481394ee9b3f57d4041a
directionality: 0
- id: connection_04970d242c884a1a80a89c76cb63a8aa
endpointAId: endpoint_b86dd04c491644cfa521a963b25df427
endpointBId: endpoint_15ad351d9d8c42d3ae74bb2f53eab64f
directionality: 0

View File

@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9cfc5682a0f841a098c1287efc45673b, type: 3}
m_Name: Back Stage Service
m_EditorClassIdentifier: Intrepid General Utilities::Intrepid.Theatre.BackStage.BackStageService
minimal: 1.5

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c58e40006278948db8d8b1f3477f83ea
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 6d9100ceca4ad4823acdd71aee1abdd2 guid: 50ae9a03aff0b452bb4ae075bb5c79f6
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@ -0,0 +1,251 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 10
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 13
m_BakeOnSceneLoad: 0
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 2
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 1
m_PVRFilteringGaussRadiusAO: 1
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
m_LightingSettings: {fileID: 0}
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 3
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1001 &47316499
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 1110237080051008694, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6646150498868371416, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
propertyPath: m_Name
value: GameManager
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3}
--- !u!1 &740018677
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 740018680}
- component: {fileID: 740018679}
- component: {fileID: 740018678}
m_Layer: 0
m_Name: GameMachine
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &740018678
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 740018677}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 42169628482e4d8ebaa2e5d2aa981721, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.GameState.GameStateObject
gameState: 5
scenes:
- {fileID: 11400000, guid: 2ea3d56022e6644b48d5914523eb881b, type: 2}
--- !u!114 &740018679
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 740018677}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c66bd2326f64474394e24e703518b558, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.GameState.GameStateMachine
id: 537d76de3a2d4a1db74ae64d5bdb34e2
bootstrapState: 5
backStageService: {fileID: 11400000, guid: c58e40006278948db8d8b1f3477f83ea, type: 2}
--- !u!4 &740018680
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 740018677}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 47316499}
- {fileID: 740018680}

View File

@ -1,6 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: be54b69524c434f888d7c380a558ccee guid: 3608dfea5a5cd4e858767376f801707c
folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}
userData: userData:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e917eef1f66a43e0ad6a399f4a5fcb08, type: 3}
m_Name: 5. Gameplay
m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Tags.SceneTag
code: 5. Gameplay

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2ea3d56022e6644b48d5914523eb881b
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using __Prototype.Scripts;
using Intrepid.Diagnostics; using Intrepid.Diagnostics;
using Intrepid.Gameplay.Prototyping; using Intrepid.Gameplay.Prototyping;
using Intrepid.Temporary;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;

View File

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Editor.GON.WorldEditor;
using Intrepid.Gameplay.Journey;
using Intrepid.Utilities; using Intrepid.Utilities;
using UnityEngine; using UnityEngine;
@ -55,6 +57,8 @@ namespace Editor.GON.StageEditor
public float floorHeight = 6f; public float floorHeight = 6f;
public int activeFloorIndex; public int activeFloorIndex;
public StageWorldSyncData worldSync = new();
public List<StageFloorDefinition> floors = new(); public List<StageFloorDefinition> floors = new();
public List<StageObjectDefinition> objects = new(); public List<StageObjectDefinition> objects = new();
@ -71,6 +75,9 @@ namespace Editor.GON.StageEditor
public void EnsureDefaults() public void EnsureDefaults()
{ {
worldSync ??= new StageWorldSyncData();
worldSync.EnsureDefaults();
floors ??= new List<StageFloorDefinition>(); floors ??= new List<StageFloorDefinition>();
objects ??= new List<StageObjectDefinition>(); objects ??= new List<StageObjectDefinition>();
@ -141,6 +148,86 @@ namespace Editor.GON.StageEditor
} }
} }
[Serializable]
public sealed class StageWorldSyncData
{
public string worldLayoutId;
public string worldStageId;
public string worldStageDisplayName;
public WorldStageKind worldStageKind;
public int worldLayer;
public float worldTileSize = 24f;
public float worldLayerHeight = 24f;
public List<JWorldTile> worldTiles = new();
public List<StageWorldSyncEndpoint> expectedEndpoints = new();
public string syncedAt;
public int contractVersion = 1;
public string contractHash;
public bool HasSync => !string.IsNullOrEmpty(worldStageId);
public void EnsureDefaults()
{
worldTiles ??= new List<JWorldTile>();
expectedEndpoints ??= new List<StageWorldSyncEndpoint>();
if (worldTileSize <= 0f)
{
worldTileSize = 24f;
}
if (worldLayerHeight <= 0f)
{
worldLayerHeight = 24f;
}
if (contractVersion <= 0)
{
contractVersion = 1;
}
}
public bool TryGetWorldBounds(out RectInt bounds)
{
EnsureDefaults();
if (worldTiles.Count == 0)
{
bounds = default;
return false;
}
var minX = worldTiles[0].x;
var maxX = worldTiles[0].x;
var minY = worldTiles[0].y;
var maxY = worldTiles[0].y;
for (var i = 1; i < worldTiles.Count; i++)
{
var tile = worldTiles[i];
minX = Mathf.Min(minX, tile.x);
maxX = Mathf.Max(maxX, tile.x);
minY = Mathf.Min(minY, tile.y);
maxY = Mathf.Max(maxY, tile.y);
}
bounds = new RectInt(minX, minY, maxX - minX + 1, maxY - minY + 1);
return true;
}
}
[Serializable]
public sealed class StageWorldSyncEndpoint
{
public string endpointId;
public JEndpointKind kind;
public JWorldTile worldTile;
public JSocketFace socketFace;
public bool required;
public string connectionId;
public string connectedEndpointId;
public string debugLabel;
}
[Serializable] [Serializable]
public sealed class StageFloorDefinition public sealed class StageFloorDefinition
{ {
@ -329,8 +416,8 @@ namespace Editor.GON.StageEditor
return kind switch return kind switch
{ {
StageObjectKind.Trap_FallingFloor => 2 * Vector2Int.one, StageObjectKind.Trap_FallingFloor => 2 * Vector2Int.one,
StageObjectKind.DoorVertical => new Vector2Int(1, 2), StageObjectKind.DoorVertical => new Vector2Int(1, 4),
StageObjectKind.DoorHorizontal => new Vector2Int(2, 1), StageObjectKind.DoorHorizontal => new Vector2Int(4, 1),
StageObjectKind.CameraBoundary => new Vector2Int(8, 5), StageObjectKind.CameraBoundary => new Vector2Int(8, 5),
_ => Vector2Int.one _ => Vector2Int.one
}; };

View File

@ -12,6 +12,10 @@ namespace Editor.GON.StageEditor
public static readonly Color PreviewFill = new(1f, 1f, 1f, 0.18f); public static readonly Color PreviewFill = new(1f, 1f, 1f, 0.18f);
public static readonly Color PreviewStroke = new(1f, 1f, 1f, 0.9f); public static readonly Color PreviewStroke = new(1f, 1f, 1f, 0.9f);
public static readonly Color SelectionStroke = new(1f, 0.85f, 0.25f, 1f); public static readonly Color SelectionStroke = new(1f, 0.85f, 0.25f, 1f);
public static readonly Color WorldSyncFootprint = new(1f, 0.1f, 0.95f, 0.42f);
public static readonly Color WorldSyncAnchor = new(0.05f, 0.9f, 1f, 0.48f);
public static readonly Color WorldSyncLift = new(1f, 0.16f, 0.14f, 0.48f);
public static readonly Color WorldSyncSocket = new(1f, 0.9f, 0.12f, 0.9f);
public static Color FromHtml(string html) public static Color FromHtml(string html)
{ {

View File

@ -7,6 +7,7 @@ namespace Editor.GON.StageEditor
public StageLogicalLayer activeLayer = StageLogicalLayer.Structural; public StageLogicalLayer activeLayer = StageLogicalLayer.Structural;
public StagePaletteEntry activePaletteEntry; public StagePaletteEntry activePaletteEntry;
public StageObjectDefinition selectedObject; public StageObjectDefinition selectedObject;
public bool showWorldSyncUnderlay = true;
public event Action Changed; public event Action Changed;
@ -37,6 +38,12 @@ namespace Editor.GON.StageEditor
NotifyChanged(); NotifyChanged();
} }
public void SetWorldSyncUnderlayVisible(bool visible)
{
showWorldSyncUnderlay = visible;
NotifyChanged();
}
public void NotifyChanged() public void NotifyChanged()
{ {
Changed?.Invoke(); Changed?.Invoke();

View File

@ -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();

View File

@ -138,6 +138,52 @@ namespace Editor.GON.StageEditor
Add(new HelpBox("The grid is infinite/sparse. The stage size emerges from painted cells and logical objects.", HelpBoxMessageType.Info)); Add(new HelpBox("The grid is infinite/sparse. The stage size emerges from painted cells and logical objects.", HelpBoxMessageType.Info));
Add(new HelpBox("Editor plane uses X/Y. Later, the stage baker can map editor Y to Unity Z and floor index to Unity Y.", HelpBoxMessageType.Info)); Add(new HelpBox("Editor plane uses X/Y. Later, the stage baker can map editor Y to Unity Z and floor index to Unity Y.", HelpBoxMessageType.Info));
BuildWorldSyncInspector();
}
private void BuildWorldSyncInspector()
{
var spacer = new VisualElement
{
style =
{
height = 10
}
};
Add(spacer);
var section = new Label("World Sync")
{
style =
{
unityFontStyleAndWeight = FontStyle.Bold
}
};
Add(section);
var sync = stage.worldSync;
if (sync is null || !sync.HasSync)
{
Add(new HelpBox("Not synced.", HelpBoxMessageType.Info));
return;
}
sync.EnsureDefaults();
var underlay = new Toggle("Show Underlay")
{
value = state.showWorldSyncUnderlay
};
underlay.RegisterValueChangedCallback(evt => state.SetWorldSyncUnderlayVisible(evt.newValue));
Add(underlay);
AddReadOnlyLine("World Stage", string.IsNullOrEmpty(sync.worldStageDisplayName)
? sync.worldStageId
: sync.worldStageDisplayName);
AddReadOnlyLine("Stage Id", sync.worldStageId);
AddReadOnlyLine("Kind", sync.worldStageKind.ToString());
AddReadOnlyLine("Layer", sync.worldLayer.ToString());
AddReadOnlyLine("Synced At", string.IsNullOrEmpty(sync.syncedAt) ? "-" : sync.syncedAt);
AddReadOnlyLine("Hash", ShortHash(sync.contractHash));
} }
private void BuildObjectInspector(StageObjectDefinition obj) private void BuildObjectInspector(StageObjectDefinition obj)
@ -313,5 +359,15 @@ namespace Editor.GON.StageEditor
Add(row); Add(row);
} }
private static string ShortHash(string hash)
{
if (string.IsNullOrEmpty(hash))
{
return "-";
}
return hash.Length <= 12 ? hash : hash[..12];
}
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using Intrepid.Gameplay.Journey;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
@ -6,7 +7,7 @@ namespace Editor.GON.StageEditor
{ {
public sealed class StageMapCanvas : VisualElement public sealed class StageMapCanvas : VisualElement
{ {
private const float MinCellPixels = 12f; private const float MinCellPixels = 3f;
private const float MaxCellPixels = 96f; private const float MaxCellPixels = 96f;
private const float ResizeHandlePixels = 12f; private const float ResizeHandlePixels = 12f;
@ -495,6 +496,8 @@ namespace Editor.GON.StageEditor
DrawInfiniteGrid(p); DrawInfiniteGrid(p);
DrawWorldSyncUnderlay(p);
DrawLowerFloors(p); DrawLowerFloors(p);
DrawCells(p, floor, 1f); DrawCells(p, floor, 1f);
@ -504,6 +507,58 @@ namespace Editor.GON.StageEditor
DrawHoverCell(p); DrawHoverCell(p);
DrawPreview(p); DrawPreview(p);
} }
private void DrawWorldSyncUnderlay(Painter2D p)
{
var sync = stage.worldSync;
if (!state.showWorldSyncUnderlay || sync is null || !sync.HasSync)
{
return;
}
sync.EnsureDefaults();
var cellsPerWorldTile = sync.worldTileSize / Mathf.Max(0.01f, stage.cellSize);
if (cellsPerWorldTile <= 0f)
{
return;
}
sync.TryGetWorldBounds(out var worldBounds);
var worldCenter = worldBounds.size == Vector2Int.zero
? Vector2.zero
: new Vector2(worldBounds.xMin + worldBounds.width * 0.5f,
worldBounds.yMin + worldBounds.height * 0.5f);
foreach (var tile in sync.worldTiles)
{
DrawHatching(p, WorldTileRect(tile, worldCenter, cellsPerWorldTile),
StageEditorColors.WorldSyncFootprint);
}
foreach (var endpoint in sync.expectedEndpoints)
{
if (endpoint is null)
{
continue;
}
var rect = WorldTileRect(endpoint.worldTile, worldCenter, cellsPerWorldTile);
switch (endpoint.kind)
{
case JEndpointKind.Anchor:
DrawHatching(p, rect.Expanded(-3f), StageEditorColors.WorldSyncAnchor);
break;
case JEndpointKind.Lift:
DrawHatching(p, rect.Expanded(-6f), StageEditorColors.WorldSyncLift);
break;
case JEndpointKind.Socket:
DrawSocketFaceHint(p, rect, endpoint.socketFace);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private void DrawLowerFloors(Painter2D p) private void DrawLowerFloors(Painter2D p)
{ {
@ -919,6 +974,17 @@ namespace Editor.GON.StageEditor
rect.height * cellPixels); rect.height * cellPixels);
} }
private Rect WorldTileRect(JWorldTile tile, Vector2 worldCenter, float cellsPerWorldTile)
{
var localX = tile.x - worldCenter.x;
var localY = tile.y - worldCenter.y;
return new Rect(
pan.x + localX * cellsPerWorldTile * cellPixels,
pan.y + localY * cellsPerWorldTile * cellPixels,
cellsPerWorldTile * cellPixels,
cellsPerWorldTile * cellPixels);
}
private Rect GetResizeHandleRect(StageObjectDefinition obj) private Rect GetResizeHandleRect(StageObjectDefinition obj)
{ {
var rect = GridRect(obj.Bounds); var rect = GridRect(obj.Bounds);
@ -960,6 +1026,58 @@ namespace Editor.GON.StageEditor
p.LineTo(b); p.LineTo(b);
p.Stroke(); p.Stroke();
} }
private void DrawHatching(Painter2D p, Rect rect, Color color)
{
if (rect.width <= 0f || rect.height <= 0f)
{
return;
}
StrokeRect(p, rect, color.WithAlpha(Mathf.Min(0.85f, color.a + 0.12f)), 1.5f);
var spacing = Mathf.Clamp(cellPixels * 2f, 10f, 28f);
var width = Mathf.Clamp(cellPixels * 0.08f, 1.25f, 3f);
for (var d = -rect.height; d < rect.width; d += spacing)
{
var start = d < 0f
? new Vector2(rect.xMin, rect.yMin - d)
: new Vector2(rect.xMin + d, rect.yMin);
var end = d + rect.height < rect.width
? new Vector2(rect.xMin + d + rect.height, rect.yMax)
: new Vector2(rect.xMax, rect.yMin + rect.width - d);
DrawLine(p, start, end, color, width);
}
}
private void DrawSocketFaceHint(Painter2D p, Rect rect, JSocketFace face)
{
var color = StageEditorColors.WorldSyncSocket;
var inset = Mathf.Clamp(cellPixels * 0.25f, 4f, 14f);
var width = Mathf.Clamp(cellPixels * 0.16f, 3f, 8f);
switch (face)
{
case JSocketFace.North:
DrawLine(p, new Vector2(rect.xMin + inset, rect.yMin), new Vector2(rect.xMax - inset, rect.yMin),
color, width);
break;
case JSocketFace.South:
DrawLine(p, new Vector2(rect.xMin + inset, rect.yMax), new Vector2(rect.xMax - inset, rect.yMax),
color, width);
break;
case JSocketFace.East:
DrawLine(p, new Vector2(rect.xMax, rect.yMin + inset), new Vector2(rect.xMax, rect.yMax - inset),
color, width);
break;
case JSocketFace.West:
DrawLine(p, new Vector2(rect.xMin, rect.yMin + inset), new Vector2(rect.xMin, rect.yMax - inset),
color, width);
break;
default:
throw new ArgumentOutOfRangeException(nameof(face), face, null);
}
}
} }
internal static class StageEditorRectExtensions internal static class StageEditorRectExtensions
@ -975,4 +1093,4 @@ namespace Editor.GON.StageEditor
return color; return color;
} }
} }
} }

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 26d7437e85a64a85a49c28877b7e145b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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,40 @@ 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;
StageDefinitionSyncer.CreateOrSyncStageDefinition(worldLayout, stage);
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)

View File

@ -51,6 +51,8 @@ namespace Editor.GON.WorldEditor
} }
stage.EnsureDefaults(); stage.EnsureDefaults();
ValidateStageDefinitionSync(layout, stage, messages);
var endpointCount = 0; var endpointCount = 0;
foreach (var endpoint in layout.endpoints) foreach (var endpoint in layout.endpoints)
{ {
@ -88,6 +90,46 @@ namespace Editor.GON.WorldEditor
} }
} }
private static void ValidateStageDefinitionSync(
WorldLayoutSO layout,
WorldStageAuthoring stage,
List<WorldValidationMessage> messages)
{
if (stage.stageDefinition is null)
{
messages.Add(new WorldValidationMessage(WorldValidationSeverity.Warning,
"Stage has no StageDefinition. Use Create StageDefinition from the stage inspector.",
stage.id));
return;
}
stage.stageDefinition.EnsureDefaults();
var sync = stage.stageDefinition.worldSync;
if (sync is null || !sync.HasSync)
{
messages.Add(new WorldValidationMessage(WorldValidationSeverity.Warning,
"StageDefinition is not synced. Use Sync StageDefinition from the stage inspector.",
stage.id));
return;
}
if (!string.IsNullOrEmpty(sync.worldStageId) && sync.worldStageId != stage.id)
{
messages.Add(new WorldValidationMessage(WorldValidationSeverity.Error,
"StageDefinition is synced to another world stage id. Reassign or recreate the StageDefinition.",
stage.id));
return;
}
var expectedHash = StageDefinitionSyncer.ComputeExpectedContractHash(layout, stage);
if (sync.contractHash != expectedHash)
{
messages.Add(new WorldValidationMessage(WorldValidationSeverity.Warning,
"StageDefinition sync contract is outdated. Use Sync StageDefinition from the stage inspector.",
stage.id));
}
}
private static void ValidateEndpoints(WorldLayoutSO layout, List<WorldValidationMessage> messages) private static void ValidateEndpoints(WorldLayoutSO layout, List<WorldValidationMessage> messages)
{ {
var nonSocketByTile = new Dictionary<string, JEndpointKind>(); var nonSocketByTile = new Dictionary<string, JEndpointKind>();

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2bf885cff2224c51bf65853c1b36117e
timeCreated: 1782101245

View File

@ -5,7 +5,7 @@ using Intrepid.Localization;
using Intrepid.Utilities; using Intrepid.Utilities;
using UnityEngine; using UnityEngine;
namespace __Prototype.Scripts namespace Intrepid.Temporary
{ {
public class DebugActionCallable : ActionCallable public class DebugActionCallable : ActionCallable
{ {

View File

@ -0,0 +1,58 @@
using System;
using Intrepid.Core;
using Intrepid.Core.GameState;
using Intrepid.SimpleEvents;
using Intrepid.Theatre.BackStage;
using Intrepid.Theatre.BackStage.Events;
using Intrepid.Theatre.Curtain;
using Intrepid.Utilities;
using TMPro;
using UnityEngine;
namespace Intrepid.Temporary
{
public class FakeSceneManager : EventListener
{
[SerializeField] private GameState nextState = GameState.Gameplay;
[SerializeField] private float delay = 2f;
[SerializeField] private CurtainTypeTag curtainType;
[SerializeField] private string sceneName;
[SerializeField] private TextMeshProUGUI textMeshPro;
[SerializeField] private bool startOnBootstrap;
[SerializeField] private BackStageProcessorMonoBehaviour backStageProcessor;
private Timer Timer { get; } = new();
protected override void InitializeVariables()
{
}
protected override void OnBootstrap()
{
if (startOnBootstrap)
{
Timer.Start();
}
}
protected override void Subscriptions()
{
Subscribe<BackStageWorkIsCompletedEvent>(WhenBackStageWorkIsCompleted);
}
private void WhenBackStageWorkIsCompleted(BackStageWorkIsCompletedEvent e)
{
Timer.Start();
}
private void Update()
{
if (Timer.IsNotRunning) return;
Timer.Update(Time.deltaTime);
if (!textMeshPro.IsNull()) textMeshPro.text = $"{sceneName} -> {nextState}\n{delay - Timer.RunningTime:F2}";
if (!Timer.Check(delay)) return;
Timer.Stop();
GameManager.Instance.ChangeGameStateTo(nextState, curtainType, new BackStageProcessorAggregator(backStageProcessor));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5ce8a81b63f84fc2a163369a0e2824f0
timeCreated: 1782101252

View File

@ -2,7 +2,7 @@ using Intrepid.Utilities;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace __Prototype.Scripts namespace Intrepid.Temporary
{ {
public class FloorDetection : MonoBehaviour public class FloorDetection : MonoBehaviour
{ {

View File

@ -4,7 +4,7 @@ using Intrepid.Utilities;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace __Prototype.Scripts namespace Intrepid.Temporary
{ {
public class FloorManager : SingletonMonoBehaviour<FloorManager> public class FloorManager : SingletonMonoBehaviour<FloorManager>
{ {

View File

@ -4,7 +4,7 @@ using Intrepid.Utilities;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace __Prototype.Scripts namespace Intrepid.Temporary
{ {
public struct VisibilityRequest public struct VisibilityRequest
{ {

View File

@ -3,7 +3,7 @@ using Intrepid.Utilities;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace __Prototype.Scripts namespace Intrepid.Temporary
{ {
public sealed class MeshColorSetter : MonoBehaviour public sealed class MeshColorSetter : MonoBehaviour
{ {

View File

@ -4,7 +4,7 @@ using Intrepid.Localization;
using Intrepid.Utilities; using Intrepid.Utilities;
using UnityEngine; using UnityEngine;
namespace __Prototype.Scripts namespace Intrepid.Temporary
{ {
[RequireComponent(typeof(MeshColorSetter))] [RequireComponent(typeof(MeshColorSetter))]
public class SwitchActionCallable : ActionCallable public class SwitchActionCallable : ActionCallable

View File

@ -1,4 +1,3 @@
using Intrepid.Core.Camera;
using Intrepid.Core.Camera.Events; using Intrepid.Core.Camera.Events;
using Intrepid.GameManagers; using Intrepid.GameManagers;
using Intrepid.Gameplay.Entities.Player.ActionBelt.Events; using Intrepid.Gameplay.Entities.Player.ActionBelt.Events;
@ -8,7 +7,7 @@ using Intrepid.Utilities;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace __Prototype.Scripts namespace Intrepid.Temporary
{ {
public class TempBoot : MonoBehaviour public class TempBoot : MonoBehaviour
{ {

View File

@ -3,7 +3,7 @@ using Intrepid.Utilities;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace __Prototype.Scripts namespace Intrepid.Temporary
{ {
public class TestGroundMorion : GroundProfileMotionBehaviour public class TestGroundMorion : GroundProfileMotionBehaviour
{ {

View File

@ -1,2 +1,3 @@
{"type":"meta","next_id":{"DSC":2,"AGD":2,"DEC":2,"PLN":5,"LSN":2,"CLSN":1}} {"type":"meta","next_id":{"DSC":3,"AGD":3,"DEC":3,"PLN":9,"LSN":2,"CLSN":1}}
{"type":"discussion","id":"DSC-0001","status":"done","ticket":"world-editor-journey-spec","title":"World Editor / Journey Spec","created_at":"2026-06-19","updated_at":"2026-06-20","tags":["world-editor","journey","stage-contract","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-world-editor-journey-spec/LSN-0001-editor-authoring-runtime-publication.md","status":"done","created_at":"2026-06-20","updated_at":"2026-06-20"}]} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"world-editor-journey-spec","title":"World Editor / Journey Spec","created_at":"2026-06-19","updated_at":"2026-06-20","tags":["world-editor","journey","stage-contract","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-world-editor-journey-spec/LSN-0001-editor-authoring-runtime-publication.md","status":"done","created_at":"2026-06-20","updated_at":"2026-06-20"}]}
{"type":"discussion","id":"DSC-0002","status":"accepted","ticket":"world-stage-editor-sync","title":"World Editor / Stage Editor Sync","created_at":"2026-06-20","updated_at":"2026-06-21","tags":["world-editor","stage-editor","sync","stage-definition"],"agendas":[{"id":"AGD-0002","file":"AGD-0002-world-stage-editor-sync.md","status":"accepted","created_at":"2026-06-20","updated_at":"2026-06-20"}],"decisions":[{"id":"DEC-0002","file":"DEC-0002-world-stage-editor-sync-v1.md","status":"accepted","created_at":"2026-06-20","updated_at":"2026-06-21","ref_agenda":"AGD-0002"}],"plans":[{"id":"PLN-0005","file":"PLN-0005-stage-world-sync-data.md","status":"open","created_at":"2026-06-21","updated_at":"2026-06-21","ref_decisions":["DEC-0002"]},{"id":"PLN-0006","file":"PLN-0006-world-editor-stage-definition-sync-action.md","status":"open","created_at":"2026-06-21","updated_at":"2026-06-21","ref_decisions":["DEC-0002"]},{"id":"PLN-0007","file":"PLN-0007-world-stage-sync-validation.md","status":"open","created_at":"2026-06-21","updated_at":"2026-06-21","ref_decisions":["DEC-0002"]},{"id":"PLN-0008","file":"PLN-0008-stage-editor-world-sync-underlay.md","status":"open","created_at":"2026-06-21","updated_at":"2026-06-21","ref_decisions":["DEC-0002"]}],"lessons":[]}

View File

@ -0,0 +1,220 @@
---
id: AGD-0002
ticket: world-stage-editor-sync
title: World Editor / Stage Editor Sync
status: accepted
created: 2026-06-20
resolved: 2026-06-20
decision: DEC-0002
tags:
- world-editor
- stage-editor
- sync
- stage-definition
---
## Contexto
O World Editor ja possui `WorldLayoutSO` como asset editorial de macro-mundo e consegue criar stages, save points, footprints, sockets, anchors, lifts e connections. O Stage Editor ja possui `StageDefinition`, atualmente em `Assets/Scripts/Editor/GON/StageEditor/StageDefinition.cs`, com floors, cells e objetos internos de stage.
Na decisao anterior, o World Editor ficou responsavel por publicar apenas dados logicos de Journey em `JGraphSO`. O bake e a geometria interna continuam sendo responsabilidade do Stage Editor. Agora existe uma nova necessidade: o World Editor deve ajudar a criar ou sincronizar um `StageDefinition` que o Stage Editor consiga abrir e usar sem configuracao manual repetitiva.
Hoje o `WorldStageAuthoring` ja tem uma referencia opcional para `StageDefinition`, mas essa referencia e manual. Falta definir qual botao/fluxo cria, atualiza, valida ou abre o asset correspondente.
Atualizacao de direcao em 2026-06-20: o usuario prefere que o botao fique dentro do inspector do stage selecionado no World Editor, nao na topbar nem em uma acao global inicial. Esse botao deve operar como **create-or-sync**: se o stage ainda nao possui `StageDefinition`, cria um asset herdando valores relevantes do World Editor; se ja possui, sincroniza os dados de contrato.
## Problema
O designer cria e posiciona stages no World Editor, mas ainda precisa criar ou associar manualmente um `StageDefinition` para trabalhar no Stage Editor. Isso cria friccao e risco de divergencia:
- stage existe no World Editor mas nao tem `StageDefinition`;
- `StageDefinition` existe mas nao sabe de qual world stage veio;
- endpoints declarados no World Editor nao tem markers correspondentes no Stage Editor;
- mudancas no footprint ou no tipo especial do stage podem nao ser refletidas no Stage Editor;
- validacoes entre os dois editores ainda nao tem contrato claro.
Precisamos discutir se o World Editor deve gerar `StageDefinition`, sincronizar metadados, criar markers, abrir o Stage Editor, ou apenas manter referencias.
## Pontos Criticos
- `StageDefinition` e editor-only; nao deve virar runtime Journey.
- `WorldLayoutSO` tambem e editor-only, mas e a fonte autoral do macro-mundo.
- O Stage Editor deve continuar dono da geometria interna, floors, objetos, bake e cena.
- O World Editor sabe o footprint macro, endpoints e tipo do stage, mas nao sabe paredes internas, layout jogavel nem objetos finais.
- Gerar conteudo interno demais a partir do World Editor pode invadir a responsabilidade do Stage Editor.
- Sincronizacao destrutiva pode apagar trabalho manual do Stage Editor.
- Endpoints precisam de algum vinculo futuro com markers internos do stage para validar implementacao.
- Save points podem exigir template inicial especial no Stage Editor.
- O local do botao importa para UX: no inspector do stage selecionado, a acao fica contextual e reduz risco de sincronizar o stage errado.
- Os dados sincronizados precisam ser claramente identificados como "contrato herdado do World Editor", nao como geometria interna autoral do Stage Editor.
## Opcoes
### Opcao A - World Editor apenas cria e associa StageDefinition vazio
- **Abordagem:** adicionar botao no World Editor para criar um `StageDefinition` se o stage selecionado ainda nao tiver um. O asset recebe id/nome derivados do world stage e fica referenciado no `WorldStageAuthoring.stageDefinition`.
- **Pro:** simples, pouco invasivo, reduz friccao imediata.
- **Contra:** nao sincroniza endpoints, footprint, save point ou revisoes.
- **Manutencao:** baixa complexidade, mas pode virar insuficiente rapidamente.
### Opcao B - World Editor cria/atualiza StageDefinition com metadados de sync
- **Abordagem:** `StageDefinition` passa a guardar metadados editoriais de origem: `worldStageId`, world layer, footprint macro, endpoints esperados e possivelmente uma versao/hash de sync. O botao `Sync Stage Definition` fica no inspector do stage selecionado no World Editor e cria ou atualiza esses dados sem mexer na geometria interna.
- **Pro:** cria contrato rastreavel entre editores e permite validacao cruzada.
- **Contra:** exige alterar `StageDefinition` e definir o que e autoritativo em cada campo.
- **Manutencao:** boa se os campos forem claramente de contrato/sync e nao de geometria final.
### Opcao C - World Editor gera templates internos iniciais no StageDefinition
- **Abordagem:** alem de metadados, o World Editor cria floors/cells/markers iniciais no Stage Editor baseados no footprint e endpoints. Save point poderia gerar um stage 1x1 com estrutura minima.
- **Pro:** acelera prototipagem e cria um primeiro layout jogavel.
- **Contra:** alto risco de sobrescrever trabalho autoral; mistura macro footprint com geometria interna real; exige regras de merge.
- **Manutencao:** mais caro, porque cada evolucao do Stage Editor precisa considerar geracao/merge.
### Opcao D - Criar um asset intermediario de contrato StageSync
- **Abordagem:** World Editor gera um asset separado de sync por stage, e o Stage Editor le esse asset. `StageDefinition` referencia esse contrato ou importa dele manualmente.
- **Pro:** separa ainda mais contrato de geometria interna.
- **Contra:** cria mais um asset e mais fluxo de authoring; pode ser excesso para V1.
- **Manutencao:** boa para sistemas grandes, pesada para a fase atual.
## Sugestao / Recomendacao
Recomendacao inicial: seguir a **Opcao B** de forma conservadora, com o botao localizado no **inspector do stage selecionado** no World Editor.
O World Editor deve ter um botao contextual de sync para o stage selecionado:
```text
World Stage selecionado
Inspector do Stage
Create/Sync StageDefinition
se nao existir StageDefinition: criar asset
se existir: atualizar somente metadados de contrato/sync
associar StageDefinition ao WorldStageAuthoring
opcionalmente abrir Stage Editor com esse StageDefinition
```
O sync nao deve gerar geometria interna nem objetos finais na V1. Ele deve apenas garantir que o Stage Editor saiba:
- qual `worldStageId` originou o stage;
- qual e o nome/display name esperado;
- qual e o footprint macro;
- qual e o `WorldLayer`;
- quais endpoints o stage deve implementar;
- qual e a versao/hash do contrato sincronizado;
- se o stage e especial, como `SavePoint`.
Isso cria base para validacao futura sem invadir o Stage Editor.
### Dados Relevantes para Sync
Dados que parecem relevantes para gravar no `StageDefinition` como contrato herdado do World Editor:
- `worldLayoutId`: identifica o layout autoral de origem.
- `worldStageId`: id estavel do stage no World Editor.
- `worldStageDisplayName`: nome esperado no macro-mundo.
- `worldStageKind`: stage comum, `SavePoint`, ou futuros tipos especiais.
- `worldLayer`: camada macro onde o stage vive.
- `worldTileSize`: tamanho do WorldTile no momento do sync.
- `worldLayerHeight`: altura macro do layer no momento do sync.
- `worldTiles`: footprint macro ocupado pelo stage.
- `worldBounds`: bounds derivados do footprint, util para visualizacao/validacao.
- `endpoints`: lista dos endpoints esperados para esse stage.
- `connections`: opcionalmente ids das conexoes relacionadas, apenas como contexto de validacao.
- `syncedAt`: timestamp editorial.
- `syncVersion` ou `contractVersion`: versao do formato de sync.
- `contractHash`: hash dos dados herdados para detectar divergencia.
Dados de endpoint relevantes:
- `endpointId`: id estavel do endpoint no World Editor/Journey.
- `kind`: socket, anchor ou lift.
- `worldTile`: tile macro dentro do world layout.
- `socketFace`: apenas para sockets.
- `required`: se a implementacao no Stage Editor deve ser obrigatoria.
- `connectedEndpointIds` ou `connectionIds`: contexto para validacao e debug.
### Representacao no Stage Editor
Ha duas camadas possiveis:
1. **Contrato sincronizado passivo:** uma lista serializada no `StageDefinition`, por exemplo `StageWorldSyncData`, contendo footprint e endpoints esperados. O Stage Editor mostra essa informacao e valida implementacao, mas nao cria objetos jogaveis automaticamente.
2. **Markers internos implementaveis:** objetos/markers no layer `Markers` associados a `endpointId`, indicando onde o designer implementou a porta, portal, lift ou save point dentro do stage real.
Recomendacao atual: comecar pela camada 1 e preparar a arquitetura para a camada 2. O contrato passivo evita sobrescrever trabalho autoral e permite que os markers sejam introduzidos depois com uma decisao menor.
Para V1, o Stage Editor poderia exibir:
- uma secao "World Sync" no inspector do stage;
- status "Not Synced / Synced / Outdated";
- lista de endpoints esperados;
- warnings quando endpoint obrigatorio ainda nao tem marker interno associado;
- destaque especial para `SavePoint`.
Markers internos podem vir depois como extensao de `StageObjectDefinition` ou como uma nova lista especifica de markers. Usar `StageObjectDefinition` agora pode ser conveniente, mas talvez misture objeto jogavel com contrato editorial. Uma lista especifica como `StageEndpointMarkerDefinition` tende a deixar a fronteira mais clara.
## Perguntas em Aberto
- [x] O `StageDefinition` deve guardar diretamente `worldStageId`, footprint e endpoints esperados, ou isso deve ficar em uma sub-struct como `StageWorldSyncData`?
- [x] O botao inicial deve ser contextual, dentro do inspector do stage selecionado no World Editor.
- [x] Tambem deve existir futuramente `Sync All Stage Definitions`, ou isso fica fora da V1?
- [x] Onde os assets gerados devem ser salvos por padrao?
- [x] O sync deve criar pastas automaticamente por world/stage?
- [x] O sync deve abrir o Stage Editor automaticamente apos criar/associar o asset?
- [x] Como representar endpoints esperados no Stage Editor: lista passiva de contrato, markers internos, ou ambos?
- [x] Se houver markers internos, eles devem ser `StageObjectDefinition`, nova lista `StageEndpointMarkerDefinition`, ou outro modelo?
- [x] Se um endpoint for removido no World Editor, o sync remove o marker no StageDefinition ou apenas marca como stale?
- [x] Save Point deve gerar algum template inicial no StageDefinition ou apenas metadata `stageKind = SavePoint`?
- [x] Como detectar alteracao manual no StageDefinition desde o ultimo sync?
- [x] O Stage Editor deve validar "endpoint esperado ainda nao implementado" como warning ou erro?
- [x] O sync deve copiar apenas ids/dados estaveis ou tambem dados visuais como cor/nome do World Stage?
- [x] O `contractHash` deve considerar somente footprint/endpoints ou tambem nome/tipo/layer?
## Criterio para Encerrar
A agenda pode virar decisao quando fecharmos:
- o escopo do botao de sync;
- quais dados o `StageDefinition` deve receber;
- como esses dados sao agrupados no `StageDefinition`;
- quais dados o sync nunca pode sobrescrever;
- comportamento para criar asset novo versus atualizar asset existente;
- comportamento de abertura do Stage Editor;
- validacoes minimas entre World Editor e Stage Editor;
- tratamento inicial de Save Point e endpoints.
## Discussion
Entrada inicial em 2026-06-20: o usuario solicitou uma agenda para discutir um botao no World Editor capaz de sincronizar ou gerar um `StageDefinition` legivel pelo Stage Editor, cobrindo dados de sync, comportamento e validacao entre editores.
Atualizacao em 2026-06-20: o usuario definiu preferencia de UX para o botao dentro do inspector do stage selecionado. O botao deve criar o `StageDefinition` com valores herdados do World Editor quando ainda nao existir, ou sincronizar o asset existente quando ja houver referencia. A discussao deve focar quais dados sao relevantes e como representa-los no Stage Editor.
## Resolution
Resolucao em 2026-06-20: a V1 de integracao World Editor / Stage Editor deve seguir a Opcao B conservadora.
Decisoes aceitas para emissao da decisao:
- O botao inicial deve ficar no inspector do stage selecionado no World Editor.
- O botao deve operar como `Create/Sync StageDefinition`.
- Se o stage nao tiver `StageDefinition`, o botao cria um asset novo e associa ao `WorldStageAuthoring`.
- Se o stage ja tiver `StageDefinition`, o botao atualiza apenas dados de contrato/sync herdados do World Editor.
- Os dados herdados devem ficar agrupados em uma sub-struct clara no `StageDefinition`, chamada inicialmente de `StageWorldSyncData`.
- Assets gerados devem ser salvos por padrao em uma pasta `Stages` ao lado do `WorldLayoutSO`, criando a pasta automaticamente quando necessario.
- V1 nao precisa de `Sync All Stage Definitions`.
- O botao principal nao deve abrir automaticamente o Stage Editor. Um segundo botao contextual, `Open Stage Editor`, pode existir no inspector.
- V1 deve representar endpoints esperados como lista passiva de contrato em `StageWorldSyncData`.
- Markers internos de endpoint ficam fora da V1, mas a arquitetura deve permitir uma lista futura especifica como `StageEndpointMarkerDefinition`.
- Sync nao deve remover dados autorais nem markers futuros automaticamente; endpoints removidos no World Editor devem sair do contrato sincronizado, e implementacoes futuras podem ser marcadas como stale.
- Save Point deve sincronizar apenas metadata/contrato (`stageKind = SavePoint`, footprint 1x1 e endpoints esperados), sem gerar template interno de geometria na V1.
- Alteracao/desatualizacao deve ser detectada por `contractHash` calculado sobre os dados herdados do World Editor.
- O hash deve considerar dados de contrato estavel: world layout id, world stage id, display name, stage kind, world layer, tile/layer sizes, footprint e endpoints esperados. Cor visual nao deve fazer parte do hash.
- `StageDefinition` ausente no World stage deve ser warning.
- `StageDefinition` sincronizado com outro `worldStageId` deve ser error.
- Sync desatualizado deve ser warning.
- Endpoint obrigatorio sem implementacao interna deve ser warning na V1, pois markers internos ainda nao sao parte do contrato.

Some files were not shown because too many files have changed in this diff Show More